Annotation CrossOrigin not working in Spring boot - reactjs

I have a Spring Boot application that exposes some endpoints. From a React app I want to make requests to these endpoints, but it keeps giving me CORS problem:
access to XMLHttpRequest at
'localhost:9090/helios-admin/api/dashboard/clients?page=0&size=30'
from origin 'http://localhost:3000' has been blocked by CORS policy:
Cross origin requests are only supported for protocol schemes: http,
data, chrome, chrome-extension, https.
So I've tried using #CrossOrigin annotation for all my methods and also for the Controller class, but the error is the same.
The get request in my react app looks like this:
constructor() {
this.url = 'localhost:9090/helios-admin/api/dashboard/clients?page=0&size=30';
}
getProjectStatusById(projectId) {
Axios.get(this.url).then(res=>{
console.log(res.data);
})
}
What's missing?
EDIT
In my spring boot app I have only this class to configure security:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
SysdataUserDetailsService sysdataUserDetailsService;
#Autowired
private JwtAuthEntryPoint jwtAuthEntryPoint;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(sysdataUserDetailsService)
.passwordEncoder(encoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(PathConstants.USER_AUTH +"/**", PathConstants.HELIOS+"/dashboard/**").permitAll()
.antMatchers(HttpMethod.GET, "/"+PathConstants.PROCESS_DEFINITION+"/**").permitAll()
.antMatchers(HttpMethod.POST, "/"+PathConstants.PROCESS_DEFINITION+"/**").permitAll()
.antMatchers(HttpMethod.GET, "/"+PathConstants.PROCESS_INSTANCE+"/**").permitAll()
//.anyRequest().authenticated()
.anyRequest().permitAll()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// custom jwt filter.
http.addFilterBefore(jwtAuthFilter(), UsernamePasswordAuthenticationFilter.class);
}
}

You can add it by overriding addCorsMappings of WebMvcConfigurerAdapter, so either create a class that extends WebMvcConfigurerAdapter or define a bean in your configuration class like this:
#Bean
public WebMvcConfigurer corsConfigurer () {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain1.com", "http://domain2.com")
.allowedMethods("GET", "OPTIONS")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(false).maxAge(3600);
}
}
}
Edit
As of 5.0 WebMvcConfigurerAdapter is deprecated and hence you could acheive the same thing by implementing WebMvcConfigurer interface (added default methods, thanks java 8 ! and can be implemented directly without the need for this adapter)
#Configuration
#EnableWebMvc
public class MyWebMvcConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain1.com", "http://domain2.com")
.allowedMethods("GET", "OPTIONS")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(false).maxAge(3600);
}
}

You can create separate CORS configuration class as follows. This class will handle the CORS configurations for all requests throughout your application and you need not annotate each controller separately with #CrossOrigin.
#Configuration
public class CorsConfig {
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*"); //'*' allows all endpoints, Provide your URL/endpoint, if any.
config.addAllowedHeader("*");
config.addAllowedMethod("POST"); //add the methods you want to allow like 'GET', 'PUT',etc. using similar statements.
config.addAllowedMethod("GET");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}

You can add a global configuration like
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
}
};
}
Just use this to add a global corrs configuration that will affect all the endpoints.Try this if the annotation doesn't work.

Related

'http://localhost:4200' has been blocked by CORS policy:No 'Access-Control-Allow-Origin' header is present on the requested resource

I am trying to update the data using spring boot and angular, but whenever i try to update the data i got this error 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This is my spring controller and angular service.
I tried other solutions from stackoverflow but it doesn't work
please tell me what i am doing wrong here..
InfoController.java
#RestController
#RequestMapping("/student")
public class InfoController {
#Autowired
private InfoDAO infoDAO;
#CrossOrigin(origins = "http://localhost:4200")
#PutMapping("/infos/{id}")
public List<Info> updateStudent(#RequestBody Info info, #PathVariable int id) {
List<Info> information = infoDAO.getbyID(id);
System.out.println("this is id");
info.setId(id);
infoDAO.update(info);
// info1.update(info1);
return information;
}
}
InfoDAO.java
List<Info> getbyID(int id);
boolean update(Info info);
InfoDAOImpl.java
public class InfoDAOImpl implements InfoDAO {
#PersistenceContext
#Autowired
private EntityManager em;
#Override
public List<Info> getbyID(int id) {
String query = "FROM Info WHERE id = :id";
return em
.createQuery(query,Info.class)
.setParameter("id",id)
.getResultList();
}
public boolean update(Info info) {
try {
em
.merge(info);
return true;
}
catch(Exception ex) {
return false;
}
}
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
.and().csrf().disable()
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/login").hasRole("ADMIN")
.antMatchers("/Signup").hasRole("USER")
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied")
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager(), customUserDetailService));
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowCredentials(true);
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Origin","Authorization", "Cache-Control", "Content-Type", "xsrfheadername","xsrfcookiename"
,"X-Requested-With","XSRF-TOKEN","Accept", "x-xsrf-token","withcredentials","x-csrftoken"));
configuration.setExposedHeaders(Arrays.asList("custom-header1", "custom-header2"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Web.service.ts
export class WebService {
constructor(private httpClient: HttpClient) { }
serverUrl = 'http://localhost:8083/student';
editPlan(data: Student, id:any): Observable<any> {
const url = `/infos/${id}`;
return this.httpClient.put(this.serverUrl + url, data);
}
getWebPlanInfo(): Observable<any> {
const url = '/plan/info';
return this.httpClient.get(this.serverUrl + url);
}
}
This issue is with this Line cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues()).
If you remove this line it will work.
But how ? That is because your #Bean method corsConfigurationSource will be loaded by spring container during the runtime and it will create the Bean for this.
By adding this line cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues()) it overrides the Bean corsConfigurationSource.
This method applyPermitDefaultValues() in CorsConfiguration class will allow GET, POST, HEAD request methods by default. Due to this your PUT/DELETE method is not working.
Reference: https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java#L428
In your spring security configuration use the following, so that the corsconfiguration bean that you are creating is automatically taken up by spring instead of the configuration that you provided in the http bean itself.In your configuration you were using the new operator to create an instance yourself manually rather than leaving it to spring to autowire the corsconfiguration bean that you provided below . So try like :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/login").hasRole("ADMIN")
.antMatchers("/Signup").hasRole("USER")
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied")
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager(), customUserDetailService));
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowCredentials(true);
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
configuration.setExposedHeaders(Arrays.asList("custom-header1", "custom-header2"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
In theory, if you have setup everything right spring security should automatically add the response headers like Access-Control-Allow-Origin to your response. Official spring secutiy doc official cors doc

Spring BOOT Cors Filter OPTIONS Error 'Response to preflight request doesn't pass access control check'

I have been trying to solve this cors error for hours and I tried every possible solution except one (which is adding options method for every resource/request).. You can find every tried things below;
Cors-Configuration Class
#Configuration
public class CorsConfiguration
{
#Bean
public WebMvcConfigurer corsConfigurer()
{
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.allowedOrigins("*")
.exposedHeaders(AuthorizationController.AUTHENTICATION_KEY_NAME + "," +
HandlerHelper.REASON_KEYNAME)
.maxAge(3600);
}
};
}
}
Pre Handle
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Origin","*");
response.setHeader("Access-Control-Allow-Methods" ,"GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers",AuthorizationController.AUTHENTICATION_KEY_NAME +","+ REASON_KEYNAME);
response.setHeader("Access-Control-Max-Age","3600"); }
application.properties
spring.mvc.dispatch-options-request=true
Adding both annotation to class and OPTIONS method to any request per resource
#CrossOrigin(origins = "*", maxAge = 3600)
#RequestMapping(value = "/**", method = RequestMethod.OPTIONS)
public ResponseEntity handle() {
return new ResponseEntity(HttpStatus.OK);
}
How can i allow 'not simple cors request' in spring boot? Or is this react issue? My front-end developer cant send request from axios..
Adding code below fixed the problem.
#EnableWebSecurity
public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// ...
http.cors().and().csrf().disable();
}
}

Get 403 status when using Spring Websocket and SockJS in AngularJS

I got this error and my web socket cannot run:
403
Here is my configure for websocket:
#Configuration
#EnableWebSocketMessageBroker
public class SpringWebSocketConfig extends
AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setUserDestinationPrefix("/ws-secured/user/");
config.setApplicationDestinationPrefixes("/ws");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws-secured/init-api").withSockJS();
registry.setErrorHandler(new ApplicationStompSubProtocolErrorHandler());
}
}
and websocket security:
#Configuration
public class SpringWebSocketSecurityConfig extends
AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry
messages) {
messages.simpTypeMatchers(
SimpMessageType.CONNECT,
SimpMessageType.MESSAGE,
SimpMessageType.SUBSCRIBE).authenticated()
.simpTypeMatchers(
SimpMessageType.UNSUBSCRIBE,
SimpMessageType.DISCONNECT).permitAll()
.anyMessage().denyAll();
}
#Override
protected boolean sameOriginDisabled() {
return true;
}
}
For more information, here is my csrf configure in Spring Sec:
http.headers().frameOptions().sameOrigin().and().authorizeRequests();
http
.csrf()
.ignoringAntMatchers("/ws-secured/**")
.and()
.headers()
.frameOptions().sameOrigin();
I have also pass my csrf token to the header:
$stomp.connect('/ws-secured/init-api', {'X-CSRF-TOKEN' : csrf_token}, function(data) {
I have searched through the Internet but cannot find the answer. I am using Tomcat 7. Do you have any ideas?

Spring Oauth2 + User Registration

I have problem with Spring Oauth2 again. I know this topic is not easy to suggest sth or check the codes because we have too much configuration.
My project has 3 different servers, Authentication server, resource server and front-end server. I want to put register.html to user's registration in front-end project(under Angularjs files) but when I make request to the related url (http://localhost:7080/app/#register) its redirecting to the login page (http://localhost:9080/auth-service/login) only for a second i can see my register.html content but after that its going to login page.
The question is, where should i put this register.html, it should be under front-end project or authentication server ?
My authentication server and front-end server codes are;
#Configuration
public class AuthServerSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.parentAuthenticationManager(authenticationManager);
auth.authenticationProvider(userAuthProviderService());
}
private CsrfMatcher csrfRequestMatcher = new CsrfMatcher();
#Override
protected void configure(final HttpSecurity http) throws Exception {
/*http.csrf().disable();*/
http.csrf().requireCsrfProtectionMatcher(csrfRequestMatcher);
http
.formLogin().loginPage("/login").defaultSuccessUrl("/")
/*.failureUrl("")*/.successHandler(new AuthSuccessHandler()).permitAll()
.and()
.requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access","/register")
.and()
.authorizeRequests().anyRequest().authenticated();
}
#Bean
public UserAuthProviderService userAuthProviderService(){
return new UserAuthProviderService();
}
private class CsrfMatcher implements RequestMatcher {
#Override
public boolean matches(HttpServletRequest request) {
return false;
}
}
}
#Configuration
#EnableAutoConfiguration
#RestController
#EnableZuulProxy
#EnableOAuth2Sso
#EnableOAuth2Client
public class UIServiceMain {
public static void main(String[] args) {
SpringApplication.run(UIServiceMain.class, args);
}
#Configuration
protected static class SecurityConfiguration extends OAuth2SsoConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.logout().and()
.antMatcher("/**").authorizeRequests()
.antMatchers("/index.html", "/home.html", "/", "/login","/register.html").permitAll().anyRequest()
.authenticated().and().csrf().disable();
http.headers().frameOptions().disable(); //FOR EMBED MAP
}
//unused
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null
&& !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
//unused
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
}
in your UI server try to create websecurity with /register.hml enabled, something like this
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/register.html")
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
}
edit:
or maybe in your current configuration remove .antMatcher("/**").authorizeRequests() and add and() .authorizeRequests().anyRequest().authenticated()
so finally it could be something like this:
#Override
public void configure(HttpSecurity http) throws Exception {
http.logout().and()
.antMatchers("/index.html", "/home.html", "/", "/login","/register.html").permitAll().anyRequest()
.authenticated()
.and().csrf().disable();
http.headers().frameOptions().disable() //FOR EMBED MAP
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
Couple of things:
I can't think of a good reason not to put your *.html anywhere other than front end server.
Also, in general, you should permit access to your static UI components publically, like #bilak mentioned:
.antMatchers("/index.html", "/home.html", "/", "/login","/register.html").permitAll()
If you are able to see register.html page at all (assuming unauthenticated user) then it is public already
Perhaps, there is a webservice call on register.html's load event that is behind Spring security that is triggering the auth flow.

CORS with spring-boot and angularjs not working

I am trying to call REST endpoints on one application (spring-boot application) from another (angularjs). The applications are running on the following hosts and ports.
REST application, using spring boot, http://localhost:8080
HTML application, using angularjs, http://localhost:50029
I am also using spring-security with the spring-boot application. From the HTML application, I can authenticate to the REST application, but, thereafter, I still cannot access any REST endpoint. For example, I have an angularjs service defined as follows.
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;
}]);
The angularjs controller looks like the following.
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!');
});
};
}]);
On the call to http://localhost:8080/api/admin/isloggedin, I get a 401 Unauthorized.
On the REST application side, I have a CORS filter that looks like the following.
#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 { }
}
My spring security configuration looks like the following.
#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);
}
}
All the handlers are doing is writing out a JSON response like {success: true} based on if the user logged in, failed to authenticate, or logged out. The RestAuthenticationEntryPoint looks like the following.
#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");
}
}
Any ideas on what I am missing or doing wrong?
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() {
}
}
No need extra define this filter just add this class. Spring will be scan and add it for you. SimpleCORSFilter.
Here is the example: spring-enable-cors
I had been into the similar situation. After doing research and testing, here is my findings:
With Spring Boot, the recommended way to enable global CORS is to declare within Spring MVC and combined with fine-grained #CrossOrigin configuration as:
#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("*");
}
};
}
}
Now, since you are using Spring Security, you have to enable CORS at Spring Security level as well to allow it to leverage the configuration defined at Spring MVC level as:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()...
}
}
Here is very excellent tutorial explaining CORS support in Spring MVC framework.
UPDATE (Sep 13, 2022):
With latest version of Spring 5 and above, you have to use WebMvcConfigurer as below:
#EnableWebMvc
public class CorsConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
}
}
If you want to enable CORS without using filters or without config file just add
#CrossOrigin
to the top of your controller and it work.
To build on other answers above, in case you have a Spring boot REST service application (not Spring MVC) with Spring security, then enabling CORS via Spring security is enough (if you use Spring MVC then using a WebMvcConfigurer bean as mentioned by Yogen could be the way to go as Spring security will delegate to the CORS definition mentioned therein)
So you need to have a security config that does the following:
#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;
}
}
This link has more information on the same: https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors
Note:
Enabling CORS for all origins (*) for a prod deployed application may not always be a good idea.
CSRF can be enabled via the Spring HttpSecurity customization without any issues
In case you have authentication enabled in the app with Spring (via a UserDetailsService for example) then the configuration.setAllowCredentials(true); must be added
Tested for Spring boot 2.0.0.RELEASE (i.e., Spring 5.0.4.RELEASE and Spring security 5.0.3.RELEASE)
Im using spring boot 2.1.0 and what worked for me was to
A. Add cors mappings by:
#Configuration
public class Config implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
}
B. Add below configuration to my HttpSecurity for spring security
.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;
}
})
Also in case of a Zuul proxy you can use this INSTEAD OF A and B (just use HttpSecurity.cors() to enable it in Spring security):
#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);
}
This works for me:
#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;
}
});
//...
}
//...
}
This is what worked for me.
#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);
}
}
For me the only thing that worked 100% when spring security is used was to skip all the additional fluff of extra filters and beans and whatever indirect "magic" people kept suggesting that worked for them but not for me.
Instead just force it to write the headers you need with a plain 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"));
}
}
This is the most direct and explicit way I found to do it. Hope it helps someone.
Step 1
By annotating the controller with #CrossOrigin annotation will allow the CORS configurations.
#CrossOrigin
#RestController
public class SampleController {
.....
}
Step 2
Spring already has a CorsFilter even though You can just register your own CorsFilter as a bean to provide your own configuration as follows.
#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);
}
If originally your program doesn't use spring security and can't afford for a code change, creating a simple reverse proxy can do the trick. In my case, I used Nginx with the following configuration:
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;
}
}
}
My program listens to :8080.
REF: CORS on Nginx
In our Spring Boot app, we have set up CorsConfigurationSource like this.
Sequence of adding allowedOrigns first and then setting applyPermitDefaultValues() let Spring set up default values for allowed headers, exposed headers, allowed methods, etc. so we don't have to specify those.
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);
}
check this one:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
...
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
...
}
Extending WebSecurityConfigurerAdapter class and overriding configure() method in your #EnableWebSecurity class would work : Below is sample class
#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();
}
});
}
}
This answer copies the #abosancic answer but adds extra safety to avoid CORS exploit.
Tip 1: Do not reflect the incoming Origin as is without checking the list of allowed hosts to access.
Tip 2: Allow credentialed request only for whitelisted hosts.
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;
}
}
}
Just Make a single class like, everything will be fine with this:
#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 {
}
}
This is what has worked for me in order to disable CORS between Spring boot and 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);
}
}
I had to modify the Security configuration also like below:
#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());
}
I was suprised to only find Eduardo Dennis pointing to the up-to-date solution which is much simpler & doesn't involve the need to write your own Filter classes: It's using the
org.springframework.web.bind.annotation.CrossOrigin annotation on your Controllers
and including and().cors() to your Spring Security configuration.
That's all you have to do!
You can use the #CrossOrigin annotation like this:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
#RequestMapping("/api")
#CrossOrigin
public class BackendController {
...
}
If you want to configure allowedHeaders, methods, origins and so on, you can simply add those values to the annotation like this: #CrossOrigin(origins = "http://localhost:50029", maxAge = 3600).
Using the #CrossOrigin annotation, the Spring Security configuration becomes extremely easy. Simply add and().cors() to your WebSecurityConfig.java class:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.and().cors()
...
}
That's all! You may delete your Filter/CORSFilter classes. If you want to add a global configuration, you can declare a CorsConfigurationSource also. See this great answer or this blog post by Sébastien Deleuze). There's also clearly stated by the Spring developers:
This approach supersedes the filter-based approach previously
recommended.
Therefore the accepted answer is outdated. Here's also a fully working example project: https://github.com/jonashackt/microservice-api-spring-boot
To enable CORS Globally you need to make changes in two places:
1. Spring Boot:
#Configuration
public class CorsConfiguration extends WebMvcConfigurationSupport {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("*")
.allowCredentials(true);
}
}
You can do the same in WebMvcConfigurerAdapter, or create bean of WebMvcConfigurer.
2. Spring Security
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll() //Permits your preflight request
}
Works as on Spring Boot 2.3.3.RELEASE
The simple way is to create a bean in your spring boot application class(class with #SpringBootApplication) as below:
Note! i specified "http://localhost:4200" below on "setAllowedOrigins()" because am running the application on localhost and using angular default port.
#Bean
public CorsFilter corsFilter(){
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
corsConfiguration.setAllowedHeaders(Arrays.asList("Origin","Access-Control-Allow-Origin","Content-Type",
"Accept", "Authorization", "Origin, Accept", "X-Requested-With",
"Access-Control-Request-Method", "Access-Control-Request-Headers"));
corsConfiguration.setExposedHeaders(Arrays.asList("Origin", "Content-Type", "Accept","Authorization",
"Access-Control-Allow-Origin", "Access-Control-Allow-Origin", "Access-Control-Allow-Credentials"));
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST","PUT","DELETE","OPTIONS"));
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
package tiny.url.urlshortner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#SpringBootApplication
public class UrlshortnerApplication {
public static void main(String[] args) {
SpringApplication.run(UrlshortnerApplication.class, args);
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*")
.allowedHeaders("*");
}
};
}
}
You can use this annotation on every restController class in sprıng boot
#CrossOrigin("*")
if you are using spring security you need to use this on any class with extended extends WebSecurityConfigurerAdapter
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}

Resources