J'ai donc 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 teste avec une boucle
curl acme@localhost:8110/oauth/token -d grant_type=password -d client_id=acme -d username=user -d password=password
Je reçois un JWT en tant que respons, mais dès que j'essaie d'accéder à l'AuthServer à partir de mon frontend (JS angulaire sur un port différent), j'obtiens une erreur CORS. Pas parce qu'il manque des en-têtes, mais parce que la demande OPTION est rejetée et qu'il manque les informations d'identification.
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 devais ajouter un CorsFilter et j'ai également trouvé cet article où j'ai utilisé l'extrait de code pour la première réponse pour permettre aux OPTIONS de demander l'accès /oauth/token
Sans informations d'identification:
@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 cela, 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, je viens d'ajouter http.csrf().disable()
à la méthode configure
de la classe MyWebSecurity, qui résout le problème avec la demande OPTION, mais donc le POST la demande ne fonctionne plus et j'obtiens There is no client authentication. Try adding an appropriate authentication filter.
(également avec curl).
J'ai essayé de savoir si je dois en quelque sorte connecter la classe MyWebSecurity et l'AuthServer, mais sans aucune chance. L'exemple d'origine (lien au début) injecte également le gestionnaire d'authentification, mais cela n'a rien changé pour moi.
J'ai trouvé la raison de mon problème!
J'avais juste besoin de mettre fin à la chaîne de filtres et de retourner le résultat immédiatement si une demande 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 pouvais ignorer la demande de contrôle en amont OPTIONS dans mon AuthServer = D
Le serveur fonctionne donc comme dans l'exemple ci-dessus et vous pouvez ignorer le commentaire de bloc avec la classe MyWebSecurity au début.
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);
}
...
}
Je suis tombé sur un problème similaire en utilisant ce qui suit
Spring Boot 1.5.8.RELEASE
Spring OAuth 2.2.0.RELEASE
WVuejs
application utilisant axios
bibliothèque de requêtes ajaxAvec postman
tout fonctionne! Lorsque j'ai commencé à faire une demande depuis l'application Vuejs
, j'ai eu les erreurs suivantes
OPTIONS http: // localhost: 8080/springboot/oauth/token 401 ()
et
XMLHttpRequest ne peut pas se charger http: // localhost: 8080/springboot/oauth/token . La réponse pour le contrôle en amont a un code d'état HTTP non valide 401
Après avoir lu un peu, j'ai découvert que je peux demander à mon Spring OAuth
D'ignorer la demande OPTIONS
en remplaçant configure
dans ma classe d'implémentation WebSecurityConfigurerAdapter
comme suit
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS);
}
L'ajout de ce qui précède a aidé mais ensuite, je suis tombé sur l'erreur spécifique de CORS
OPTIONS http: // localhost: 8080/springboot/oauth/token 403 ()
et
XMLHttpRequest ne peut pas se charger http: // localhost: 8080/springboot/oauth/token . La réponse à la demande de contrôle en amont 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. L'origine ' http: // localhost: 80 ' n'est donc pas autorisée à accéder. La réponse avait le code d'état HTTP 403.
Et 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, cela fonctionne comme prévu. Avant de partir prod
je vais rechercher consequences
sur l'utilisation
web.ignoring().antMatchers(HttpMethod.OPTIONS);
ainsi que best practices
pour la configuration Cors
ci-dessus. Pour l'instant, *
Fait le travail mais n'est certainement pas sécurisé pour la production.
La réponse de Cyril m'a aidé partially
puis je suis tombé sur l'idée de CorsConfig
dans ce Github problème.
vous avez raison! c'est une solution, et cela a aussi fonctionné pour moi (j'ai eu le même problème)
Mais laissez-moi essayer d'utiliser une implémentation plus intelligente du filtre CORS pour Java: http://software.dzhuvinov.com/cors-filter.html
Il s'agit d'une solution très complète pour les applications Java.
En fait, vous pouvez voir ici comment votre point est résolu.
Utilisation de Spring Boot 2 ici.
Je devais 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...
}