I'm trying to put a two way to login:
One way is useing formLogin user and password. This is de code with only one configuration and works fine:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Autowired
private UserDetailsService userDetailsService;
#Configuration
public static class WebSecurityConfigBasic extends WebSecurityConfigurerAdapter {
#Autowired
private LoginSuccessHandler loginSuccessHandler;
#Order(2)
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(...).permitAll()
.antMatchers(HttpMethod.POST, ...).permitAll()
.antMatchers(HttpMethod.GET,...).permitAll()
.antMatchers(...).permitAll()
.antMatchers(Constantes.INTERNO_SUCESSO, "/").access("hasRole('...')")
.antMatchers(Constantes.EXTERNO_SUCESSO).access("hasRole('...')")
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.successHandler(loginSuccessHandler)
.failureUrl("/login-error")
.and()
.logout().logoutSuccessUrl("/");
http.headers().frameOptions().disable();
}
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
String hierarchy = "... > ... > ... > ... > ... > ...";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}
}
This is the code with oAuth configuration, and if only this configuration actived, works fine too:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Autowired
private UserDetailsService userDetailsService;
#Configuration
public static class WebSecurityConfigOAuth extends WebSecurityConfigurerAdapter {
#Autowired
private LoginSuccessHandler loginSuccessHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(...).permitAll()
.antMatchers(HttpMethod.POST, ...).permitAll()
.antMatchers(HttpMethod.GET,...).permitAll()
.antMatchers(...).permitAll()
.antMatchers(Constantes.INTERNO_SUCESSO, "/").access("hasRole('...')")
.antMatchers(Constantes.EXTERNO_SUCESSO).access("hasRole('...')")
.anyRequest().authenticated()
.and()
.oauth2Login()
.loginPage("/login").permitAll()
.successHandler(loginSuccessHandler)
.failureUrl("/login-error")
.and()
.logout().logoutSuccessUrl("/");
http.headers().frameOptions().disable();
}
#Bean
public AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository() {
return new HttpSessionOAuth2AuthorizationRequestRepository();
}
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
String hierarchy = "... > ... > ... > ... > ... > ...";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}
}
And if actived thw two configuration, only works the formlogin with user and password:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Autowired
private UserDetailsService userDetailsService;
#Order(1)
#Configuration
public static class WebSecurityConfigBasic extends WebSecurityConfigurerAdapter {
#Autowired
private LoginSuccessHandler loginSuccessHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(...).permitAll()
.antMatchers(HttpMethod.POST, ...).permitAll()
.antMatchers(HttpMethod.GET,...).permitAll()
.antMatchers(...).permitAll()
.antMatchers(Constantes.INTERNO_SUCESSO, "/").access("hasRole('...')")
.antMatchers(Constantes.EXTERNO_SUCESSO).access("hasRole('...')")
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.successHandler(loginSuccessHandler)
.failureUrl("/login-error")
.and()
.logout().logoutSuccessUrl("/");
http.headers().frameOptions().disable();
}
}
#Order(2)
#Configuration
public static class WebSecurityConfigOAuth extends WebSecurityConfigurerAdapter {
#Autowired
private LoginSuccessHandler loginSuccessHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(...).permitAll()
.antMatchers(HttpMethod.POST, ...).permitAll()
.antMatchers(HttpMethod.GET,...).permitAll()
.antMatchers(...).permitAll()
.antMatchers(Constantes.INTERNO_SUCESSO, "/").access("hasRole('...')")
.antMatchers(Constantes.EXTERNO_SUCESSO).access("hasRole('...')")
.anyRequest().authenticated()
.and()
.oauth2Login()
.loginPage("/login").permitAll()
.successHandler(loginSuccessHandler)
.failureUrl("/login-error")
.and()
.logout().logoutSuccessUrl("/");
http.headers().frameOptions().disable();
}
#Bean
public AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository() {
return new HttpSessionOAuth2AuthorizationRequestRepository();
}
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
String hierarchy = "... > ... > ... > ... > ... > ...";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}
}
At thw antmatchers on the both configuration is the same.
I have two types of urls one are secured and one are not secured like registration and SignIn
I want "registration" and "SignIn" to byPass security and filters while all the other urls must pass through filters and security.
Following is my Security Configuration code but it is not working.
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#Configuration
public class AppSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
TempTokenGenerator tempTokenGenerator;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(getPasswordEncoder());
}
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("notsecured/signin");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers("/", "**secured/**").authenticated()
.and().logout().permitAll()
.and()
.apply(new TempConfigurer(tempTokenGenerator));
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
what am I missing? What should I do to include "secured" urls in authentication and filters while excluding "unsecured" from authentication and filter.
web.ignoring().antMatchers("notsecured/signin");
seems not working if I put
.anyRequest().authenticated()
with
http.authorizeRequests() to make secured urls work.
and if I put
.antMatchers("/","**/secured/**").authenticated()
with
.anyRequest().permitAll()
it is also not working.
use configure(HttpSecurity http) method to secure your request-endpoints
http.csrf().disable()
.cors().and()
.authorizeRequests()
.antMatchers("/notsecured/**").permitAll()
.antMatchers("/secured/**").fullyAuthenticated()
.and().sessionManagement...
.and().formLogin()
...
use configure(WebSecurity web) method to ignore static resources like images, css,...
In my spring-boot+React app working fine on the desktop web browser, but when I create a record through a mobile(I used iPhone) web browser like chrome or safari, it shows 'Origin 'http://www.example.com' is not allowed by Access-Control-Allow-Origin'.I have already added 'Cors Mappings' like
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final long MAX_AGE_SECS = 3600;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE")
.maxAge(MAX_AGE_SECS);
}
}
Can anybody help me to solve this issue?
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
CustomUserDetailsService customUserDetailsService;
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean(BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/brands/**", "/api/users/**")
.permitAll()
.antMatchers(
"/",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js"
) .permitAll()
.antMatchers("/api/auth/**")
.permitAll()
.antMatchers("/api/user/checkUsernameAvailability", "/api/user/checkEmailAvailability")
.permitAll()
.antMatchers("/api/brands/**").permitAll()
.antMatchers("/api/familys/**").permitAll()
.antMatchers("/api/models/**").permitAll()
.anyRequest()
.authenticated();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
Remove WebMvcConfig class and add a bean called corsConfigurationSource in your SecurityConfig class which will be able to set headers/methods and allow any origins using "*" but if you want to allow a specific origin, you can add it.
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token"));
configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
I am Implementing Spring Security using Oauth following these websystique , baeldung,What I found WebSecurityConfigurerAdapter and ResourceServerConfigurerAdapter both provides control over HttpSecurity,and filterchain adds them in order 0 and 3 respectively.
So I am overriding configure of any of the above ConfigurerAdapter but only one at a time.
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.requestMatchers().antMatchers("/api/**").and()
.authorizeRequests()
.antMatchers("/api/ads").permitAll()
.antMatchers("/api/admin").hasAuthority(RoleConstant.ADMIN.getRole())
.antMatchers("/api/user").hasAuthority(RoleConstant.USER.getRole())
.anyRequest().authenticated()
.and()
.exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
What I observe in case of WebSecurityConfigurerAdapter I am able to access unauthorized resources ie I am able to access /api/user after being authenticated even with token having authority ADMIN.Why so?
Note : I am not overriding HttpSecurity of ResourceServerConfigurerAdapter.
References : There are similar resources available here. Resource1 , Resource2.
Also I want to know,I must have to override both configure(HttpSecurity http) or any of the class is sufficient?If yes,which one is recommended?
ResourceServer :
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_rest_api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.requestMatchers().antMatchers("/api/**").and()
.authorizeRequests()
.antMatchers("/api/ads").permitAll()
.antMatchers("/api/admin").hasAuthority(RoleConstant.ADMIN.getRole())
.antMatchers("/api/user").hasAuthority(RoleConstant.USER.getRole())
.antMatchers("/api/readProperty").access("hasRole('ADMIN')")
.anyRequest().authenticated()
.and()
.exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
SpringSecurityConfig :
#Configuration
#EnableWebSecurity
#ComponentScan(basePackages = {"com.ttnd.mvc_mod.services","com.ttnd.mvc_mod.repository","com.ttnd.mvc_mod.config","com.ttnd.mvc_mod.custom"})
#Import({SpringORMHibernateSupportConfig.class})
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
private CustomAuthenticationProvider authProvider;
/* #Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.requestMatchers().antMatchers("/**").and()
.authorizeRequests()
.antMatchers("/oauth/token","/api/ads").permitAll()
.antMatchers("/api/admin").hasAuthority(RoleConstant.ADMIN.getRole())
.antMatchers("/api/user").hasAuthority(RoleConstant.USER.getRole())
.antMatchers("/api/readProperty").access("hasRole('ADMIN')")
.and()
.exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());//.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint);
}
*/
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//auth.userDetailsService(customUserDetailsService);
auth.authenticationProvider(authProvider);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
I have a problem with CORS filter on spring security URL's.
It doesn't set Access-Control-Allow-Origin and other exposed header on URL's belonging to spring sec (login/logout) or filtered by Spring Security.
Here are the configurations.
CORS:
#Configuration
#EnableWebMvc
public class MyWebMvcConfig extends WebMvcConfigurerAdapter {
********some irrelevant configs************
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/*").allowedOrigins("*").allowedMethods("GET", "POST", "OPTIONS", "PUT")
.allowedHeaders("Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method",
"Access-Control-Request-Headers")
.exposedHeaders("Access-Control-Allow-Origin", "Access-Control-Allow-Credentials")
.allowCredentials(true).maxAge(3600);
}
}
Security:
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
.formLogin()
.successHandler(ajaxSuccessHandler)
.failureHandler(ajaxFailureHandler)
.loginProcessingUrl("/authentication")
.passwordParameter("password")
.usernameParameter("username")
.and()
.logout()
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.and()
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/authentication").permitAll()
.antMatchers("/oauth/token").permitAll()
.antMatchers("/admin/*").access("hasRole('ROLE_ADMIN')")
.antMatchers("/user/*").access("hasRole('ROLE_USER')");
}
}
So, if I make a request to the url's which are not listened by security - CORS headers are set. Spring security URL's - not set.
Spring boot 1.4.1
Option 1 (Use WebMvcConfigurer bean):
The CORS configuration that you started with is not the proper way to do it with Spring Boot. You need to register a WebMvcConfigurer bean. Reference here.
Example Spring Boot CORS configuration:
#Configuration
#Profile("dev")
public class DevConfig {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:4200");
}
};
}
}
This will provide the CORS configuration for a basic (no security starter) Spring Boot application. Note that CORS support exists independent of Spring Security.
Once you introduce Spring Security, you need to register CORS with your security configuration. Spring Security is smart enough to pick up your existing CORS configuration.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and()
....
Option 2 (Use CorsConfigurationSource bean):
The first option I described is really from the perspective of adding Spring Security to an existing application. If you are adding Spring Security from the get-go, the way that is outlined in the Spring Security Docs involves adding a CorsConfigurationSource bean.
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// by default uses a Bean by the name of corsConfigurationSource
.cors().and()
...
}
#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;
}
}
Instead of using the CorsRegistry you can write your own CorsFilter and add it to your security configuration.
Custom CorsFilter class:
public class CorsFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request= (HttpServletRequest) servletRequest;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", true);
response.setHeader("Access-Control-Max-Age", 180);
filterChain.doFilter(servletRequest, servletResponse);
}
#Override
public void destroy() {
}
}
Security config class:
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
CorsFilter corsFilter() {
CorsFilter filter = new CorsFilter();
return filter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(corsFilter(), SessionManagementFilter.class) //adds your custom CorsFilter
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
.formLogin()
.successHandler(ajaxSuccessHandler)
.failureHandler(ajaxFailureHandler)
.loginProcessingUrl("/authentication")
.passwordParameter("password")
.usernameParameter("username")
.and()
.logout()
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.and()
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/authentication").permitAll()
.antMatchers("/oauth/token").permitAll()
.antMatchers("/admin/*").access("hasRole('ROLE_ADMIN')")
.antMatchers("/user/*").access("hasRole('ROLE_USER')");
}
}
This is quite clean and doesn't require any extra configurations. Pass asterisks where you want all option to be valid (like I did in setAllowedHeaders).
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().configurationSource(request -> {
var cors = new CorsConfiguration();
cors.setAllowedOrigins(List.of("http://localhost:4200", "http://127.0.0.1:80", "http://example.com"));
cors.setAllowedMethods(List.of("GET","POST", "PUT", "DELETE", "OPTIONS"));
cors.setAllowedHeaders(List.of("*"));
return cors;
}).and()...
}
}
I have a React based web client, and my backend REST API is running Spring Boot Ver 1.5.2
I wanted to quickly enable CORS on all controller route requests from my client running on localhost:8080. Inside my security configuration, I simply added a #Bean of type FilterRegistrationBean and got it working easily.
Here is the code:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthConfiguration extends WebSecurityConfigurerAdapter {
....
....
#Bean
public FilterRegistrationBean corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin(corsAllowedOrigin); // #Value: http://localhost:8080
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // **permit OPTIONS call to all**
....
}
....
....
}
You can refer Spring Boot docs here
I just had a similar issue, I was trying to execute a request from my frontend in React executing on http://localhost:3000, to my backend in SpringBoot executing at http://localhost:8080. I had two errors:
Access Control Allow Origin
I solved this very easily by adding this to my RestController:
#CrossOrigin(origins = ["http://localhost:3000"])
After fixing this, I started getting this error:
The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true'
Access-Control-Allow-Credentials
This one can be worked around in two ways:
Adding allowCredentials = "true" to the CrossOrigin configuration:
#CrossOrigin(origins = ["http://localhost:3000"], allowCredentials = "true")
Changing the credential options of the fetch in the frontend request. Basically, you'll need to perform the fetch call like this:
fetch('http://localhost:8080/your/api', { credentials: 'same-origin' })
Hope this helps =)
Currently the OPTIONS requests are blocked by default if security is enabled.
Just add an additional bean and preflight requests will be handled correctly:
#Bean
public IgnoredRequestCustomizer optionsIgnoredRequestsCustomizer() {
return configurer -> {
List<RequestMatcher> matchers = new ArrayList<>();
matchers.add(new AntPathRequestMatcher("/**", "OPTIONS"));
configurer.requestMatchers(new OrRequestMatcher(matchers));
};
}
Please note that depending on your application this may open it for potential exploits.
Opened issue for a better solution: https://github.com/spring-projects/spring-security/issues/4448
If you need it for quick local development just add this annotation on your controller. (offcourse change origins as required)
#CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)
You could also achieve this with an interceptor.
Use the exception to ensure you are ending the lifecycle of the request:
#ResponseStatus (
value = HttpStatus.NO_CONTENT
)
public class CorsException extends RuntimeException
{
}
Then, in your interceptor, set headers for all OPTIONS requests and throw the exception:
public class CorsMiddleware extends HandlerInterceptorAdapter
{
#Override
public boolean preHandle (
HttpServletRequest request,
HttpServletResponse response,
Object handler
) throws Exception
{
if (request.getMethod().equals("OPTIONS")) {
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Access-Control-Allow-Methods","GET, POST, PUT, OPTIONS, DELETE");
response.addHeader("Access-Control-Allow-Headers", "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,Authorization,If-Modified-Since,Cache-Control,Content-Type");
response.addHeader("Access-Control-Max-Age", "3600");
response.addHeader("charset", "utf-8");
throw new CorsException();
}
return super.preHandle(request, response, handler);
}
}
Lastly, apply the interceptor to all routes:
#Configuration
public class MiddlewareConfig extends WebMvcConfigurerAdapter
{
#Override
public void addInterceptors (InterceptorRegistry registry)
{
registry.addInterceptor(new CorsMiddleware())
.addPathPatterns("/**");
}
}
If anyone struggles with the same problem in 2020. here's what did the work for me. This app is for learning purposes so I have enabled everything
CorsFilter class:
public class CorsFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
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-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Content-Length, X-Requested-With");
chain.doFilter(req, res);
}
#Override
public void destroy() {
}
}
and then again setup of headers in class extending WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter {
#Bean
CorsFilter corsFilter() {
CorsFilter filter = new CorsFilter();
return filter;
}
protected void configure(HttpSecurity http) throws Exception {
System.out.println("Im configuring it");
(
(HttpSecurity)
(
(HttpSecurity)
(
(ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)
http
.headers().addHeaderWriter(
new StaticHeadersWriter("Access-Control-Allow-Origin", "*")).and()
.addFilterBefore(corsFilter(), SessionManagementFilter.class)
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
.anyRequest()
).authenticated().and()
).formLogin().and()
).httpBasic();
}
}
I tried with below config and it worked!
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().configurationSource(configurationSource()).and()
.requiresChannel()
.anyRequest()
.requiresSecure();
}
private CorsConfigurationSource configurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.setAllowCredentials(true);
config.addAllowedHeader("X-Requested-With");
config.addAllowedHeader("Content-Type");
config.addAllowedMethod(HttpMethod.POST);
source.registerCorsConfiguration("/**", config);
return source;
}
}