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.
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.
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 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 deaxios
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.
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.
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...
}
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();
}
}