J'essaie d'appeler les points de terminaison REST sur une application (application à démarrage à ressort) à partir d'une autre (angularjs). Les applications s'exécutent sur les hôtes et ports suivants.
- Application REST, utilisant le démarrage à ressort
http://localhost:8080
- Application HTML, utilisant angularjs,
http://localhost:50029
J'utilise également spring-security
avec l'application spring-boot. À partir de l'application HTML, je peux m'authentifier auprès de l'application REST, mais par la suite, je ne peux toujours pas accéder à un point de terminaison REST. Par exemple, j'ai un service angularjs défini comme suit.
adminServices.factory('AdminService', ['$resource', '$http', 'conf', function($resource, $http, conf) {
var s = {};
s.isAdminLoggedIn = function(data) {
return $http({
method: 'GET',
url: 'http://localhost:8080/api/admin/isloggedin',
withCredentials: true,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
};
s.login = function(username, password) {
var u = 'username=' + encodeURI(username);
var p = 'password=' + encodeURI(password);
var r = 'remember_me=1';
var data = u + '&' + p + '&' + r;
return $http({
method: 'POST',
url: 'http://localhost:8080/login',
data: data,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
};
return s;
}]);
Le contrôleur angularjs ressemble à ce qui suit.
adminControllers.controller('LoginController', ['$scope', '$http', 'AdminService', function($scope, $http, AdminService) {
$scope.username = '';
$scope.password = '';
$scope.signIn = function() {
AdminService.login($scope.username, $scope.password)
.success(function(d,s) {
if(d['success']) {
console.log('ok authenticated, call another REST endpoint');
AdminService.isAdminLoggedIn()
.success(function(d,s) {
console.log('i can access a protected REST endpoint after logging in');
})
.error(function(d, s) {
console.log('huh, error checking to see if admin is logged in');
$scope.reset();
});
} else {
console.log('bad credentials?');
}
})
.error(function(d, s) {
console.log('huh, error happened!');
});
};
}]);
Lors de l'appel à http://localhost:8080/api/admin/isloggedin
, je reçois un 401 Unauthorized
.
Du côté de l'application REST, j'ai un filtre CORS qui ressemble à ce qui suit.
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
@Override
public void destroy() { }
@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", "http://localhost:50029");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, X-Auth-Token");
response.setHeader("Access-Control-Allow-Credentials", "true");
if(!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
chain.doFilter(req, res);
}
}
@Override
public void init(FilterConfig config) throws ServletException { }
}
Ma configuration de sécurité Spring ressemble à ce qui suit.
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
@Autowired
private JsonAuthSuccessHandler jsonAuthSuccessHandler;
@Autowired
private JsonAuthFailureHandler jsonAuthFailureHandler;
@Autowired
private JsonLogoutSuccessHandler jsonLogoutSuccessHandler;
@Autowired
private AuthenticationProvider authenticationProvider;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PersistentTokenRepository persistentTokenRepository;
@Value("${rememberme.key}")
private String rememberMeKey;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/", "/admin", "/css/**", "/js/**", "/fonts/**", "/api/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(jsonAuthSuccessHandler)
.failureHandler(jsonAuthFailureHandler)
.permitAll()
.and()
.logout()
.deleteCookies("remember-me", "JSESSIONID")
.logoutSuccessHandler(jsonLogoutSuccessHandler)
.permitAll()
.and()
.rememberMe()
.userDetailsService(userDetailsService)
.tokenRepository(persistentTokenRepository)
.rememberMeCookieName("REMEMBER_ME")
.rememberMeParameter("remember_me")
.tokenValiditySeconds(1209600)
.useSecureCookie(false)
.key(rememberMeKey);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(authenticationProvider);
}
}
Tout ce que font les gestionnaires est d'écrire une réponse JSON comme {success: true}
si l'utilisateur s'est connecté, n'a pas réussi à s'authentifier ou s'est déconnecté. Le RestAuthenticationEntryPoint
ressemble à ce qui suit.
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException ex)
throws IOException, ServletException {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
Des idées sur ce qui me manque ou ce que je fais mal?
la source
API Gateway
cloud avec spring, que je pensais exagéré. .Réponses:
import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class SimpleCORSFilter implements Filter { private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class); public SimpleCORSFilter() { log.info("SimpleCORSFilter init"); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me"); chain.doFilter(req, res); } @Override public void init(FilterConfig filterConfig) { } @Override public void destroy() { } }
Plus besoin de définir ce filtre, ajoutez simplement cette classe. Spring sera numérisé et l'ajoutera pour vous. SimpleCORSFilter. Voici l'exemple: spring-enable-cors
la source
HEADERS
etX_REDIRECT_LOCATION_HEADER
? 2) La ligne est-ellerequest.getRequestURL());
une faute de frappe ou une erreur de copier / coller? 3) Pourquoi ne pas vérifierOPTIONS
et continuer simplement avec la chaîne de filtrage?request.getHeader("Origin")
comme indiqué ci-dessus en raison du fractionnement de la réponse HTTP@Order(Ordered.HIGHEST_PRECEDENCE)
.J'avais été dans la même situation. Après avoir fait des recherches et des tests, voici mes résultats:
Avec Spring Boot, la méthode recommandée pour activer CORS global est de déclarer dans Spring MVC et de la combiner avec une
@CrossOrigin
configuration fine comme:@Configuration public class CorsConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*") .allowedHeaders("*"); } }; } }
Désormais, puisque vous utilisez Spring Security, vous devez également activer CORS au niveau de Spring Security pour lui permettre d'exploiter la configuration définie au niveau Spring MVC comme:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors().and()... } }
Voici un très excellent tutoriel expliquant la prise en charge de CORS dans le framework Spring MVC.
la source
Si vous souhaitez activer CORS sans utiliser de filtres ou sans fichier de configuration, ajoutez simplement
@CrossOrigin
vers le haut de votre contrôleur et cela fonctionne.
la source
Pour s'appuyer sur les autres réponses ci-dessus, au cas où vous auriez une application de service Spring Boot REST (pas Spring MVC) avec la sécurité Spring, l'activation de CORS via la sécurité Spring suffit (si vous utilisez Spring MVC, utilisez un
WebMvcConfigurer
bean comme mentionné par Yogen pourrait être la voie à suivre car la sécurité de Spring déléguera à la définition CORS mentionnée ici)Vous devez donc disposer d'une configuration de sécurité qui effectue les opérations suivantes:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { //other http security config http.cors().configurationSource(corsConfigurationSource()); } //This can be customized as required CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); List<String> allowOrigins = Arrays.asList("*"); configuration.setAllowedOrigins(allowOrigins); configuration.setAllowedMethods(singletonList("*")); configuration.setAllowedHeaders(singletonList("*")); //in case authentication is enabled this flag MUST be set, otherwise CORS requests will fail configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; }
}
Ce lien contient plus d'informations sur le même: https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors
Remarque:
UserDetailsService
par exemple), leconfiguration.setAllowCredentials(true);
doit être ajoutéTesté pour Spring boot 2.0.0.RELEASE (c'est-à-dire Spring 5.0.4.RELEASE et Spring security 5.0.3.RELEASE)
la source
J'utilise
spring boot 2.1.0
et ce qui a fonctionné pour moi était deA. Ajoutez des mappages de cors en:
@Configuration public class Config implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("*"); } }
B.Ajoutez la configuration ci-dessous à my
HttpSecurity
pour la sécurité du printemps.cors().configurationSource(new CorsConfigurationSource() { @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration config = new CorsConfiguration(); config.setAllowedHeaders(Collections.singletonList("*")); config.setAllowedMethods(Collections.singletonList("*")); config.addAllowedOrigin("*"); config.setAllowCredentials(true); return config; } })
Également dans le cas d'un proxy Zuul, vous pouvez utiliser ceci AU LIEU DE A et B (utilisez simplement
HttpSecurity.cors()
pour l'activer dans la sécurité Spring):@Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("OPTIONS"); config.addAllowedMethod("HEAD"); config.addAllowedMethod("GET"); config.addAllowedMethod("PUT"); config.addAllowedMethod("POST"); config.addAllowedMethod("DELETE"); config.addAllowedMethod("PATCH"); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); }
la source
org.springframework.web.filter.CorsFilter
. J'ai eu le même problème pendant que je l'utilisais accidentellement à partir de paquets catalina.Cela fonctionne pour moi:
@Configuration public class MyConfig extends WebSecurityConfigurerAdapter { //... @Override protected void configure(HttpSecurity http) throws Exception { //... http.cors().configurationSource(new CorsConfigurationSource() { @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration config = new CorsConfiguration(); config.setAllowedHeaders(Collections.singletonList("*")); config.setAllowedMethods(Collections.singletonList("*")); config.addAllowedOrigin("*"); config.setAllowCredentials(true); return config; } }); //... } //... }
la source
Pour moi, la seule chose qui a fonctionné à 100% lorsque la sécurité du ressort est utilisée était de sauter toutes les peluches supplémentaires de filtres et de haricots supplémentaires et tout ce que les gens «magiques» indirects ont continué à suggérer que cela fonctionnait pour eux mais pas pour moi.
Au lieu de cela, forcez-le simplement à écrire les en-têtes dont vous avez besoin avec un simple
StaticHeadersWriter
:@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http // your security config here .authorizeRequests() .antMatchers(HttpMethod.TRACE, "/**").denyAll() .antMatchers("/admin/**").authenticated() .anyRequest().permitAll() .and().httpBasic() .and().headers().frameOptions().disable() .and().csrf().disable() .headers() // the headers you want here. This solved all my CORS problems! .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Origin", "*")) .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Methods", "POST, GET")) .addHeaderWriter(new StaticHeadersWriter("Access-Control-Max-Age", "3600")) .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Credentials", "true")) .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Headers", "Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization")); } }
C'est la manière la plus directe et la plus explicite que j'ai trouvée pour le faire. J'espère que ça aide quelqu'un.
la source
Étape 1
En annotant le contrôleur avec une
@CrossOrigin
annotation, vous autoriserez les configurations CORS.@CrossOrigin @RestController public class SampleController { ..... }
Étape 2
Spring a déjà un CorsFilter même si vous pouvez simplement enregistrer votre propre CorsFilter en tant que bean pour fournir votre propre configuration comme suit.
@Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); // Provide list of origins if you want multiple origins config.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH")); config.setAllowCredentials(true); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); }
la source
vérifier celui-ci:
@Override protected void configure(HttpSecurity httpSecurity) throws Exception { ... .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() ... }
la source
L'extension de la classe WebSecurityConfigurerAdapter et la substitution de la méthode configure () dans votre classe @EnableWebSecurity fonctionneraient: Voici un exemple de classe
@Override protected void configure(final HttpSecurity http) throws Exception { http .csrf().disable() .exceptionHandling(); http.headers().cacheControl(); @Override public CorsConfiguration getCorsConfiguration(final HttpServletRequest request) { return new CorsConfiguration().applyPermitDefaultValues(); } }); } }
la source
Si à l'origine votre programme n'utilise pas la sécurité Spring et ne peut pas se permettre un changement de code, la création d'un simple proxy inverse peut faire l'affaire. Dans mon cas, j'ai utilisé Nginx avec la configuration suivante:
http { server { listen 9090; location / { if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # # Custom headers and headers various browsers *should* be OK with but aren't # add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; # # Tell client that this pre-flight info is valid for 20 days # add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; } if ($request_method = 'POST') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; } if ($request_method = 'GET') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; } proxy_pass http://localhost:8080; } } }
Mon programme écoute : 8080 .
REF: CORS sur Nginx
la source
C'est ce qui a fonctionné pour moi.
@EnableWebSecurity public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors(); } } @Configuration public class WebConfiguration implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry .addMapping("/**") .allowedMethods("*") .allowedHeaders("*") .allowedOrigins("*") .allowCredentials(true); } }
la source
Cette réponse copie la réponse @abosancic mais ajoute une sécurité supplémentaire pour éviter l'exploit CORS .
Astuce 1: ne reflétez pas l'origine entrante telle quelle sans vérifier la liste des hôtes autorisés à accéder.
Astuce 2: n'autorisez les demandes d'informations d'identification que pour les hôtes en liste blanche.
import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class SimpleCORSFilter implements Filter { private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class); private List<String> allowedOrigins; public SimpleCORSFilter() { log.info("SimpleCORSFilter init"); allowedOrigins = new ArrayList<>(); allowedOrigins.add("https://mysafeorigin.com"); allowedOrigins.add("https://itrustthissite.com"); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String allowedOrigin = getOriginToAllow(request.getHeader("Origin")); if(allowedOrigin != null) { response.setHeader("Access-Control-Allow-Origin", allowedOrigin); response.setHeader("Access-Control-Allow-Credentials", "true"); } response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me"); chain.doFilter(req, res); } @Override public void init(FilterConfig filterConfig) { } @Override public void destroy() { } public String getOriginToAllow(String incomingOrigin) { if(allowedOrigins.contains(incomingOrigin.toLowerCase())) { return incomingOrigin; } else { return null; } } }
la source
Dans notre application Spring Boot, nous avons configuré CorsConfigurationSource comme ceci.
La séquence d'ajout d'
allowedOrigns
abord, puis de réglageapplyPermitDefaultValues()
permet à Spring de configurer les valeurs par défaut pour les en-têtes autorisés, les en-têtes exposés, les méthodes autorisées, etc. afin que nous n'ayons pas à les spécifier.public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("http://localhost:8084")); configuration.applyPermitDefaultValues(); UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource(); configurationSource.registerCorsConfiguration("/**", configuration); return configurationSource; }
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/api/**") .access("@authProvider.validateApiKey(request)") .anyRequest().authenticated() .and().cors() .and().csrf().disable() .httpBasic().authenticationEntryPoint(authenticationEntryPoint); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); }
la source
Faites simplement une seule classe comme, tout ira bien avec ceci:
@Component @Order(Ordered.HIGHEST_PRECEDENCE) public class MyCorsConfig implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { final HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, enctype"); response.setHeader("Access-Control-Max-Age", "3600"); if (HttpMethod.OPTIONS.name().equalsIgnoreCase(((HttpServletRequest) req).getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { chain.doFilter(req, res); } } @Override public void destroy() { } @Override public void init(FilterConfig config) throws ServletException { } }
la source
C'est ce qui a fonctionné pour moi afin de désactiver CORS entre Spring boot et React
@Configuration public class CorsConfig implements WebMvcConfigurer { /** * Overriding the CORS configuration to exposed required header for ussd to work * * @param registry CorsRegistry */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("*") .allowedHeaders("*") .allowCredentials(true) .maxAge(4800); } }
J'ai dû modifier la configuration de sécurité également comme ci-dessous:
@Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .cors().configurationSource(new CorsConfigurationSource() { @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { CorsConfiguration config = new CorsConfiguration(); config.setAllowedHeaders(Collections.singletonList("*")); config.setAllowedMethods(Collections.singletonList("*")); config.addAllowedOrigin("*"); config.setAllowCredentials(true); return config; } }).and() .antMatcher("/api/**") .authorizeRequests() .anyRequest().authenticated() .and().httpBasic() .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and().exceptionHandling().accessDeniedHandler(apiAccessDeniedHandler()); }
la source