CrossOrigin annotation doesn't work with spring security - java

When i enable the spring-boot-starter-security dependency. CORS support doesn't work.
This is my SecurityConfiguration Class:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return authentication -> {
// ...
};
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf()
// Disabling CSRF
.disable()
// Disabling Session Management
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
// Adding custom REST Authentication filter
.addFilterBefore(new RestAuthenticationFilter(authenticationManager()), LogoutFilter.class)
// Authorizing requests
.authorizeRequests()
.antMatchers("/", "/frontend/login")
.permitAll()
.antMatchers("/api/**", "/frontend/**")
.authenticated()
.antMatchers("/**")
.permitAll();
}
}
My Controller Class has a CrossOrigin Annotation:
#CrossOrigin
#RequestMapping("/frontend")
#RestController
public class FrontEndController extends BaseController {
I can handle CORS with custom CORS Filter but I want to use just one Annoation.

I found 2 methods for adding CORS support to spring-security enabled spring-boot project. We can add spring-web CorsFilter to security filter chain. The following example belongs to token based authentication project. So we used a custom RestAuthenticationFilter.
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
source.registerCorsConfiguration("/**", config);
http.csrf()
// Disabling CSRF
.disable()
// Disabling Session Management
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
// Adding spring-web CORS filter
.addFilterBefore(new CorsFilter(source), LogoutFilter.class)
// Adding custom REST Authentication filter
.addFilterBefore(new RestAuthenticationFilter(authenticationManager()), LogoutFilter.class)
// Authorizing requests
.authorizeRequests()
.antMatchers("/", "/frontend/login")
.permitAll()
.antMatchers("/api/**", "/frontend/**")
.authenticated()
.antMatchers("/**")
.permitAll();
}
}
But in the above example our CrossOrigin annotations in the controllers are redundant. So we should give the ability to control CORS requests to spring-web layer. Therefore we can allow CORS pre-flight (OPTIONS HTTP Methods).
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf()
// Disabling CSRF
.disable()
// Disabling Session Management
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
// Adding custom REST Authentication filter
.addFilterBefore(new RestAuthenticationFilter(authenticationManager()), LogoutFilter.class)
// Authorizing requests
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**")
.permitAll()
.antMatchers("/", "/frontend/login")
.permitAll()
.antMatchers("/api/**", "/frontend/**")
.authenticated()
.antMatchers("/**")
.permitAll();
}
}
With the help of above configuration we can use both #CrossOrigin annotations and spring-security configuration.

Related

Unauthenticated User Access throws AuthenticationException after Migration to Spring Security 6

We have migrated to Spring Boot 3 and Spring Security 6. The behavior of users who are not authenticated has changed.
Unauthenticated users should have Anonymous Authetification as before. Instead we get an AuthenticationException.
Is this behavior correct in the new version of Spring Security 6 or do we need to adjust our WebSecurityConfig?
Here is our filter chain:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
log.debug("Configuring HTTP Security");
http
.csrf().disable()
.cors()
.and()
.headers()
.frameOptions().disable()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.and()
.authorizeHttpRequests()
.requestMatchers("/sf/**").permitAll()
.requestMatchers(HttpMethod.GET, "/health").permitAll()
.requestMatchers(HttpMethod.GET, "/metrics").permitAll()
.requestMatchers(HttpMethod.GET, "/error").permitAll()
.requestMatchers(HttpMethod.GET, "/favicon.ico").permitAll()
.requestMatchers(HttpMethod.GET, "/info").permitAll()
.requestMatchers("/").permitAll()
.requestMatchers("/**").authenticated()
.and()
.addFilterBefore(new RolesToRightsConverterFilter(s3RSpringConfig), BasicAuthenticationFilter.class)
.addFilterAfter(new Slf4jMDCFilter(authService, tracingService), RolesToRightsConverterFilter.class)
.oauth2ResourceServer().jwt().jwtAuthenticationConverter(new AadJwtBearerTokenAuthenticationConverter());
return http.build();
}

SecurityFilterChain bean loaded in initilization but ignored on HTTP Requests

I implemented a custom Filter for my Spring-boot application, but still couldn't get the request to go through the SecurityFilterChain bean.
Here is the code inside my bean. Its being loaded when I start the application but being ignored when I do a request, any idea what could be the problem?
#Configuration
#EnableWebSecurity
public class ProjectSecurityConfig {
#Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception{
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().cors().configurationSource(new CorsConfigurationSource() {
#Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Collections.singletonList("http://localhost:4200"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowCredentials(true);
config.setExposedHeaders(Collections.singletonList("Authorization"));
config.setMaxAge(86400L);
return config;
}
}).and()
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterAfter(new JWTTokenGeneratorFilter(), BasicAuthenticationFilter.class)
.addFilterBefore(new JWTTokenValidatorFilter(), BasicAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("api/login", "api/registration/**").permitAll()
.antMatchers(HttpMethod.POST, "api/questions/**", "api/exams").hasRole("ADMIN")
.antMatchers(HttpMethod.GET, "api/users/").hasRole("ADMIN")
.antMatchers("api/questions/**", "api/exams", "api/complete-exam").hasRole("USER")
.and().formLogin()
.and().httpBasic();
return http.build();
}
}
I tried some configuration, dissabling the deafultFilters in the spring app with: #SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
But still request are not passing through this FilterChain.
Just rename your bean from defaultSecurityFilterChain to a different name because that one is already the default one created by Spring and yours will be overriden.

Spring webflux Security - Disable csrf with property

I have a Spring WebFlux security as follows and would like to control CSRF using property. How can I add if check for the CSRF alone here?
#Bean
public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
return http.authorizeExchange().matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
//.pathMatchers("/register", "/login").permitAll()
.anyExchange().authenticated()
.and().formLogin()
.securityContextRepository(securityContextRepository())
.and()
.exceptionHandling()
.accessDeniedHandler(new HttpStatusServerAccessDeniedHandler(HttpStatus.BAD_REQUEST))
.and().csrf().disable()
.build();
}
you just add something like:
// All your stuff up here then
if(!csrfEnabled) {
http.csrf().disable();
}
return http.build();

how to ignore request parameters in spring security

I want to implement AuthenticationFailureHandler with the following configuration:
// Auth failure handler
#Bean
public AuthenticationFailureHandler appAuthenticationFailureHandler() {
ExceptionMappingAuthenticationFailureHandler failureHandler = new ExceptionMappingAuthenticationFailureHandler();
Map<String, String> failureUrlMap = new HashMap<>();
failureUrlMap.put(BadCredentialsException.class.getName(), "/login?error");
failureUrlMap.put(AccountExpiredException.class.getName(), "/login?expired");
failureUrlMap.put(LockedException.class.getName(), "/login?locked");
failureUrlMap.put(DisabledException.class.getName(), "/login?disabled");
failureHandler.setExceptionMappings(failureUrlMap);
return failureHandler;
}
and in class SecurityConfiguration extends WebSecurityConfigurerAdapter I have:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/register", "/confirm").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
// username password
.usernameParameter("username")
.passwordParameter("password")
// success and failure handlers
.successHandler(appAuthenticationSuccessHandler())
.failureHandler(appAuthenticationFailureHandler())
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.invalidateHttpSession(true)
.clearAuthentication(true)
.permitAll()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler())
;
}
with this, all mentioned above is not redirecting to relevant failure URL, but if I remove
.anyRequest()
.authenticated()
then it is being redirected to relevant failure URL, but that is not good practice now the question is how I can configure the configure() to ignore /login?request parameter and implement further logic accordingly?
As I understand, the issue is that urls like "/login?.*" are available only after authorization. According to spring examples, you can exclude paths from authorized access with the following code in Config file:
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}

Spring security configuration: enable/disable authentication

my question is like this:
I want to disable and enable authentication through configuration in class which extends WebSecurityConfigurerAdapter. I have test which expects that status is unauthroized if there is no login info provided. This is configuration class:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
public static final String USER = "workshop-user";
public static final String ADMIN = "workshop-admin";
#Value("${WORKSHOP_USER_PASSWORD:user}")
private String userPassword;
#Value("${WORKSHOP_ADMIN_PASSWORD:admin}")
private String administratorPassword;
#Value("${features.security.disable}")
private boolean securityDisable;
#Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder(9);
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser(USER)
.password(encoder().encode(userPassword))
.roles("CLIENT_APP")
.and()
.withUser(ADMIN)
.password(encoder().encode(administratorPassword))
.roles("CLIENT_APP", "ADMINISTRATOR");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
if(!securityDisable) {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/**/import").hasRole("ADMINISTRATOR")
.antMatchers("/api-docs/**", "/swagger-resources/**", "/v2/api-docs", "/**/favicon.ico", "/webjars/**", "/api/admin/health").permitAll()
.anyRequest().permitAll()
//replace .permitAll() with .authenticated() for authentiaction
//replace .authenticated() with .permitAll() for disabling security
.and()
.csrf().disable()
.headers().disable()
.httpBasic();
}
else{
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/**/import").hasRole("ADMINISTRATOR")
.antMatchers("/api-docs/**", "/swagger-resources/**", "/v2/api-docs", "/**/favicon.ico", "/webjars/**", "/api/admin/health").permitAll()
.anyRequest().authenticated()
//replace .permitAll() with .authenticated() for authentiaction
//replace .authenticated() with .permitAll() for disabling security
.and()
.csrf().disable()
.headers().disable()
.httpBasic();
}
}
and this is my flag from application.properties
features.security.disable = true
I have tried to find another way to do it through configuration but couldn't come to another answer. The thing is that i know it is very simple becaues of if/else statement. One is authenticated and the other permitAll entries. Do you know is there a way that uses "better aproach" which does not pollute code with duplication like this? I tried to look in documentation and other posts but couldn't find any relevant information for me.
You can create two security configurations
#Configuration
#Profile("prod")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/**/import").hasRole("ADMINISTRATOR")
.antMatchers("/api-docs/**", "/swagger-resources/**", "/v2/api-docs", "/**/favicon.ico", "/webjars/**", "/api/admin/health").permitAll()
.anyRequest().authenticated()
//replace .permitAll() with .authenticated() for authentiaction
//replace .authenticated() with .permitAll() for disabling security
.and()
.csrf().disable()
.headers().disable()
.httpBasic();
}
}
#Configuration
#Profile("test")
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/**/import").hasRole("ADMINISTRATOR")
.antMatchers("/api-docs/**", "/swagger-resources/**", "/v2/api-docs", "/**/favicon.ico", "/webjars/**", "/api/admin/health").permitAll()
.anyRequest().permitAll()
//replace .permitAll() with .authenticated() for authentiaction
//replace .authenticated() with .permitAll() for disabling security
.and()
.csrf().disable()
.headers().disable()
.httpBasic();
}
}
Run based on your requirement
-Dspring.profiles.active=prod
-Dspring.profiles.active=test

Categories