Standalone Spring OAuth2 JWT Authorization Server + CORS

donc j'ai le serveur D'autorisation suivant condensé de cet exemple de Dave Syer

@SpringBootApplication
public class AuthserverApplication {

    public static void main(String[] args) {
            SpringApplication.run(AuthserverApplication.class, args);
    }

    /* added later
    @Configuration
    @Order(Ordered.HIGHEST_PRECEDENCE)
    protected static class MyWebSecurity extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http //.csrf().disable() 
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll();
       }
    }*/

    @Configuration
    @EnableAuthorizationServer
    protected static class OAuth2AuthorizationConfig extends
                    AuthorizationServerConfigurerAdapter {

            @Autowired
            private AuthenticationManager authenticationManager;

            @Bean
            public JwtAccessTokenConverter jwtAccessTokenConverter() {
                    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
                    KeyPair keyPair = new KeyStoreKeyFactory(
                                    new ClassPathResource("keystore.jks"), "foobar".toCharArray())
                                    .getKeyPair("test");
                    converter.setKeyPair(keyPair);
                    return converter;
            }

            @Override
            public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
                    clients.inMemory()
                                    .withClient("acme")
                                    //.secret("acmesecret")
                                    .authorizedGrantTypes(//"authorization_code", "refresh_token",
                                                    "password").scopes("openid");
            }

            @Override
            public void configure(AuthorizationServerEndpointsConfigurer endpoints)
                            throws Exception {
                    endpoints.authenticationManager(authenticationManager).accessTokenConverter(
                                    jwtAccessTokenConverter());
            }

            @Override
            public void configure(AuthorizationServerSecurityConfigurer oauthServer)
                            throws Exception {
                    oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess(
                                    "isAuthenticated()");
            }
    }
}

quand je le lance et le tester avec curl

curl acme@localhost:8110/oauth/token -d grant_type=password -d client_id=acme -d username=user -d password=password

j'obtiens un JWT comme réponse, mais dès que j'essaie d'accéder à L'AuthServer depuis mon Frontend (JS angulaire sur un port différent) j'obtiens l'erreur de CORS. Pas parce qu'il manque des en-têtes, mais parce que l'OPTION request est rejetée et qu'il manque les justificatifs d'identité.

Request URL:http://localhost:8110/oauth/token
Request Method:OPTIONS
Status Code:401 Unauthorized
WWW-Authenticate:Bearer realm="oauth", error="unauthorized", error_description="Full authentication is required to access this resource"

je savais déjà que je dois ajouter un CorsFilter et en plus trouvé ce post où j'ai utilisé le snippet pour la première réponse pour laisser les OPTIONS demande l'accès /oauth/token sans justificatifs d'identité:

@Order(-1)
public class MyWebSecurity extends WebSecurityConfigurerAdapter {
   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http
          .authorizeRequests()
          .antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll();
   }
}

après que j'ai obtenu avec curl l'erreur suivante:

{"timestamp":1433370068120,"status":403,"error":"Forbidden","message":"Expected CSRF token not found. Has your session expired?","path":"/oauth/token"}

donc pour faire simple j'ai juste ajouté http.csrf().disable() à la méthode configure de MyWebSecurity class, qui résout le problème avec l'OPTION request, mais par conséquent L'option POST request ne fonctionne plus et je reçois There is no client authentication. Try adding an appropriate authentication filter. (aussi avec curl).

j'ai essayé de savoir si je devais d'une manière ou d'une autre connecter ma classe de sécurité Web et le serveur Auths, mais sans succès. L'exemple d'origine (lien au début) injecte ainsi la authenticationManager, mais cela n'a rien changé pour moi.

18
demandé sur Community 2015-06-04 01:52:32

6 réponses

a trouvé la raison de mon problème!

j'avais juste besoin de mettre fin à la chaîne de filtrage et de retourner le résultat immédiatement si une demande D'OPTIONS est traitée par le CorsFilter!

SimpleCorsFilter.java

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {

    public SimpleCorsFilter() {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization");

        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void destroy() {
    }
}

après cela je pourrais ignorer les OPTIONS de la requête pré-vol dans mon AuthServer =d

de sorte que le serveur fonctionne comme dans le snipped ci-dessus et vous pouvez ignorer le commentaire de bloc avec la classe de sécurité MyWebSecurity dans le début.

81
répondu Michael K. 2016-07-05 08:25:03

j'ai trouvé une solution en utilisant la solution pour la question. Mais j'ai une autre façon de décrire la solution:

@Configuration
public class WebSecurityGlobalConfig extends WebSecurityConfigurerAdapter {
      ....
      @Override
      public void configure(WebSecurity web) throws Exception {
        web.ignoring()
          .antMatchers(HttpMethod.OPTIONS);
      }
      ...
}
13
répondu Cyril 2017-06-09 13:42:28

je suis tombé sur une question similaire en utilisant la suite

  • Backend Spring Boot 1.5.8.RELEASE
  • Spring OAuth2 Spring OAuth 2.2.0.RELEASE w
  • Vuejs app à l'aide de axios requête ajax library

avec postman tout fonctionne! Quand j'ai commencé à faire la demande de Vuejs app puis j'ai eu les erreurs suivantes

OPTIONS http://localhost:8080/springboot/oauth/token 401 ()

et

XMLHttpRequest ne peut pas charger http://localhost:8080/springboot/oauth/token . La réponse pour preflight a un code D'état HTTP non valide 401

après un peu de lecture, j'ai découvert que je peux donner instruction à mon Spring OAuth d'ignorer la demande OPTIONS en outrepassant configure dans mon WebSecurityConfigurerAdapter la mise en œuvre de la classe ainsi

@Override
public void configure(WebSecurity web) throws Exception {
   web.ignoring().antMatchers(HttpMethod.OPTIONS);
}

L'ajout de ce qui précède a aidé, mais alors, je suis tombé sur le CORS erreur spécifique

OPTIONS http://localhost:8080/springboot/oauth/token 403 ()

et

XMLHttpRequest cannot load"" http://localhost:8080/springboot/oauth/token . Réponse à la requête preflight ne passe pas la vérification du contrôle d'accès: aucun en-tête 'Access-Control-Allow-Origin' n'est présent sur la ressource demandée. Origine " http://localhost:8000 " n'est donc pas autorisé à y accéder. La réponse contenait le code de statut HTTP 403.

et a résolu le problème ci-dessus à l'aide d'un CorsConfig comme indiqué ci-dessous

@Configuration
public class CorsConfig {
    @Bean
    public FilterRegistrationBean corsFilterRegistrationBean() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.applyPermitDefaultValues();
        config.setAllowCredentials(true);
        config.setAllowedOrigins(Arrays.asList("*"));
        config.setAllowedHeaders(Arrays.asList("*"));
        config.setAllowedMethods(Arrays.asList("*"));
        config.setExposedHeaders(Arrays.asList("content-length"));
        config.setMaxAge(3600L);
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}

Après l'ajout de la classe ci-dessus, il fonctionne comme prévu. Avant que je vais prod je vais rechercher consequences de l'utilisation de

web.ignoring().antMatchers(HttpMethod.OPTIONS);

ainsi que best practices pour la configuration ci-dessus Cors . Pour l'instant * fait le travail mais, certainement pas sûr pour la production.

la réponse de Cyril m'a aidé partially et puis je suis tombé sur le CorsConfig idée dans ce Github de l'émission.

5
répondu Raf 2018-03-12 01:30:46

vous avez raison! c'est une solution, et ça a marché aussi pour moi (j'avais le même problème)

mais laissez-moi sussgest d'utiliser une plus intelligente CORS mise en œuvre de filtre pour Java: http://software.dzhuvinov.com/cors-filter.html

c'est une solution très complète pour les applications Java.

en fait, vous pouvez voir ici comment votre point est résolu.

1
répondu Gervasio Amy 2015-08-17 19:26:30

en utilisant la botte à ressort 2 ici.

j'ai dû le faire dans mon AuthorizationServerConfigurerAdapter

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {

    Map<String, CorsConfiguration> corsConfigMap = new HashMap<>();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    //TODO: Make configurable
    config.setAllowedOrigins(Collections.singletonList("*"));
    config.setAllowedMethods(Collections.singletonList("*"));
    config.setAllowedHeaders(Collections.singletonList("*"));
    corsConfigMap.put("/oauth/token", config);
    endpoints.getFrameworkEndpointHandlerMapping()
            .setCorsConfigurations(corsConfigMap);

    //additional settings...
}
1
répondu Sebastiaan van den Broek 2018-07-04 10:10:46

utilisant la botte de printemps comme un serveur de OAuth avec la sécurité de ressort et SSL pour un backend RESTful. C'est dur d'essayer de relier ça, surtout avec CORS. Je pense que l'exclusion de la config mvc peut avoir perdu une partie de la configuration du CORS automatique.

ajouter manuellement un filtre CORS tel que décrit dans les autres réponses a fonctionné pour moi. Cependant, je devais aussi ajouter:

@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    // Disable /oauth/token Http Basic Auth
    oauthServer.allowFormAuthenticationForClients();
}

avec ce bit ajouté, je pourrais laisser tomber mon filtre CORS personnalisé et ajouter le configuration suggérée par la réponse de Sebastiaan:

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    // Workaround for https://github.com/spring-projects/spring-boot/issues/1801
    endpoints.authenticationManager(authentication -> authenticationManager.getOrBuild().authenticate(authentication));
    endpoints.getFrameworkEndpointHandlerMapping().setCorsConfigurations(getCorsConfig());
}

private Map<String, CorsConfiguration> getCorsConfig() {

    List<String> methodsList = Stream.of("GET", "POST", "PUT", "DELETE", "OPTIONS").collect(Collectors.toList());
    List<String> allowableOriginsList = Stream.of("*").collect(Collectors.toList());
    List<String> allowableHeadersList =
            Stream.of("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
            .collect(Collectors.toList());

    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.setAllowedOrigins(allowableOriginsList);
    config.setAllowedMethods(methodsList);
    config.setAllowedHeaders(allowableHeadersList);

    Map<String, CorsConfiguration> corsConfigMap = new HashMap<>();
    corsConfigMap.put("/oauth/token", config);

    return corsConfigMap;
}

puis il n'y avait plus besoin d'utiliser le WebSecurityConfigurerAdapter (tout peut maintenant vivre dans AuthorizationServerConfigurerAdapter ) et ce morceau pourrait être enlevé:

public class MyWebSecurity extends WebSecurityConfigurerAdapter {
   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http
          .authorizeRequests()
          .antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll();
   }
}
0
répondu Glenn 2018-08-20 10:59:01