This question already has answers here:
Spring Security : Multiple HTTP Config not working
(2 answers)
Closed 3 years ago.
I have Spring Boot configuration which looks something like this:
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore( new Filter(), UsernamePasswordAuthenticationFilter.class)
.csrf().disable() // Disabled, cause enabling it will cause sessions
.headers()
.frameOptions()
.sameOrigin()
.addHeaderWriter(new XXssProtectionHeaderWriter())
.and()
.authorizeRequests()
.antMatchers("/app/**", "/rest/**").hasAuthority(DefaultPrivileges.ACCESS_TASK)
.anyRequest().permitAll();
My understanding was only the requests which start with /app or /rest will be intercepted by my custom filter but it turns out the requests to the root (http://localhost:8080/context/) are also intercepted.
I have multiple configurations for Spring Security the other configuration looks like this:
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable();
if (taskAppProperties.isRestEnabled()) {
if (restAppProperties.isVerifyRestApiPrivilege()) {
http
.antMatcher("/*-api/**")
.authorizeRequests()
.antMatchers("/*-api/**").hasAuthority(DefaultPrivileges.ACCESS_REST_API)
.and()
.httpBasic();
} else {
http
.antMatcher("/*-api/**")
.authorizeRequests()
.antMatchers("/*-api/**").authenticated()
.and()
.httpBasic();
}
} else {
http
.antMatcher("/*-api/**")
.authorizeRequests()
.antMatchers("/*-api/**").denyAll();
}
Can anyone help?
I realize this is a bit confusing, but there are actually two antMatchers methods, one that branches from authorizedRequests and another that branches from requestMatchers.
Let's look at the following declaration:
http
.requestMatchers()
.antMatchers("/app/**", "/api/**")
.and()
.authorizeRequests()
.antMatchers("...").authenticated()
...
The requestMatchers() DSL is where you describe the endpoints that matter to that instance of the Spring Security filter chain. So, this filter chain will only engage for URIs that start with /app or /api.
Let's take a look at another one:
http
.authorizeRequests()
.antMatchers("/app/**", "/api/**")
.authenticated();
While this may appear to be doing the same thing, it isn't. That's because you are calling the antMatchers method that belongs to authorizeRequests().
This is why indentation is important with the Spring Security DSL. Because there's a hierarchy in the DSL, then you want to indent, just like you want to indent inside your if statements.
In Spring Security 5.2, this is simplified a bit with the new lambda DSL:
http
.requestMatchers(r -> r.antMatchers("/app/**", "/api/**"))
.authorizeRequests(a -> a.antMatchers("...").authenticated());
HttpSecurity.authorizeRequests - returns ExpressionInterceptUrlRegistry where we are setting Matchers and Roles condition, which will be added using method ExpressionInterceptUrlRegistry.getRegistry and if you check other usage of this method only at permitAll stub where actual authentication happens.
The filter we add using HttpSecurity.addFilterBefore will not check any Request matching. If you need, you can do one more check in your custom filter to avoid other URIs
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterAfter( new Filter() {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = ((HttpServletRequest) request);
if(httpServletRequest.getRequestURI().startsWith("/app/") || httpServletRequest.getRequestURI().startsWith("/rest/")) {
// Do you secured filter computations
}
chain.doFilter(request, response);
}
#Override
public void destroy() {
}}, UsernamePasswordAuthenticationFilter.class)
.csrf()
.disable() // Disabled, cause enabling it will cause sessions
.headers()
.frameOptions()
.sameOrigin()
.addHeaderWriter(new XXssProtectionHeaderWriter())
.and()
.authorizeRequests()
.antMatchers("/app/**", "/rest/**")
.hasAuthority(DefaultPrivileges.ACCESS_TASK)
.anyRequest()
.permitAll();
Related
I have been trying to enable spring security in web flux, with my own custom authentication method. So far so good, but I am not able to allow certain URL patterns using permitall.
I have tried to create different beans of SecurityWebFilterChain, also tried with different config altogether, but nothing seems to work for me.
Here is my SecurityWebFilterChain
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http.csrf()
.disable()
.httpBasic()
.disable()
.formLogin()
.disable()
.logout()
.disable()
.authenticationManager(this.authenticationManager())
.securityContextRepository(this.securityContextRepository())
.authorizeExchange()
.pathMatchers("**/signal/health").permitAll()
.pathMatchers("**/order").permitAll()
.and()
.authorizeExchange()
.anyExchange()
.authenticated()
.and()
.build();
}
I have an internal health check system, which runs as soon as my application is up, so I want this to be allowed.
Moreover, I also want to allow another couple or URI, but the above config doesn't work for me.
Everything goes for authentication.
What am I doing wrong here?
I see a wrong and and authorizeExchange placed in between. Try using this:
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http.csrf().disable()
.httpBasic().disable()
.formLogin().disable()
.logout().disable()
.authenticationManager(this.authenticationManager())
.securityContextRepository(this.securityContextRepository())
.authorizeExchange()
.pathMatchers("**/signal/health","**/order").permitAll()
.anyExchange()
.authenticated()
.and().build();
}
There are Authorization Server(UAA) and Resource Server and Gateway applications that have been working correctly. The scenario for authentication is authorization_code. In the first time after authentication, the end of request is added ;jesessionid=[value], so its result is exception from HttpFirewall of Gateway application, because of having ';' in the request.
My question is that what is it and why jessionid is added the end of request? and how is it adaptable with HttpFirewall.
I have found a way around but I know it has some risks. It is like this:
#Bean
public HttpFirewall allowUrlEncodedSlashHttpFirwall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowSemicolon(true);
return firewall;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.headers().cacheControl().disable()
.and()
.headers()
.cacheControl()
.disable()
.frameOptions()
.sameOrigin()
.and()
.httpBasic().disable()
.authorizeRequests()
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.mvcMatchers("/uaa/**", "/login**", "/favicon.ico", "/error**").permitAll()
.anyRequest().authenticated()
.and()
.logout()
.permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.httpFirewall(allowUrlEncodedSlashHttpFirwall());
}
As above configuration, the ; is skipped but it is not right and it has some risks.
What is the correct way and config to solve this problem?
How can I disable sessions for anonymous users in spring boot 2.0?
My current configuration:
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/actuator/**").permitAll()
.antMatchers("/password/forgot/**").permitAll()
.antMatchers("/password/reset/**").permitAll()
.antMatchers("/css/**").permitAll()
.antMatchers("/js/**").permitAll()
.antMatchers("/img/**").permitAll()
.antMatchers("/manage/**").permitAll()
.antMatchers( "/favicon.ico").permitAll()
.anyRequest().authenticated();
http
.formLogin()
.loginPage("/login")
.permitAll()
.successHandler(authSuccessHandler)
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login")
.invalidateHttpSession(true)
.permitAll();
}
}
When I open the login page, there is still a session created:
Perhaps the session is being automatically created by Spring Security. That behavior can be disabled by setting create-session to never. This means Spring Security will not attempt to automatically create a session.
<http create-session="never">
[...]
</http>
Some more details here: https://www.baeldung.com/spring-security-session
try adding "anonymous.disable()" after http in FormLoginWebSecurityConfigurerAdapter class.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.anonymous.disable()
.authorizeRequests()
.antMatchers("/actuator/**").permitAll()
.antMatchers("/password/forgot/**").permitAll()
.antMatchers("/password/reset/**").permitAll()
.antMatchers("/css/**").permitAll()
.antMatchers("/js/**").permitAll()
.antMatchers("/img/**").permitAll()
.antMatchers("/manage/**").permitAll()
.antMatchers( "/favicon.ico").permitAll()
.anyRequest().authenticated();
http
.formLogin()
.loginPage("/login")
.permitAll()
.successHandler(authSuccessHandler)
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login")
.invalidateHttpSession(true)
.permitAll();
}
Enjoyy!
In my case I had to do 3 things:
Add http.anonymous.disable() to my WebSecurityConfigurerAdapter.
Also add http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER). I don't know why, but this DOES create sessions for my authenticated users, even though the docs say that it will never create sessions for you.
Deleted all my cookies before testing. I discovered that if you made a request with an old session cookie, the server would use said cookie to create a new session, ignoring completely the SessionCreationPolicy we just set.
I've got REST service that will be used for authentication. The authentication endpoint will look like /api/v.1/authentication. The API version is a variable that can be changed to reflect updated versions. One example would be /api/v.2/authentication. I like to have an antMatcher that can deal with both these cases so I tried .antMatchers(HttpMethod.POST,"**/authenticate").permitAll() using ** to match any beginning of the endpoint but this doesn't work. The full setup below.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "**/authenticate").permitAll()
.antMatchers(HttpMethod.GET, "**/get-public-key").permitAll()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest().authenticated();
}
Any suggestions how I can solve this?
You have to use absolute pattern, see AntPathMatcher:
Note: a pattern and a path must both be absolute or must both be relative in order for the two to match. Therefore it is recommended that users of this implementation to sanitize patterns in order to prefix them with "/" as it makes sense in the context in which they're used.
Your modified and simplified configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/**/authenticate").permitAll()
.antMatchers(HttpMethod.GET, "/**/get-public-key").permitAll()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest().authenticated();
}
I have swagger UI working with spring-boot. I have a stateless authentication setup for my spring rest api which is restricted based on roles for every api path.
However, I am not sure how can i put <server_url>/swagger-ui.html behind Basic authentication.
UPDATE
I have following websecurity configured via WebSecurityConfig
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/sysadmin/**").hasRole("SYSADMIN")
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/siteadmin/**").hasRole("SITEADMIN")
.antMatchers("/api/**").hasRole("USER")
.anyRequest().permitAll();
// Custom JWT based security filter
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
One suggestion without knowing more about your configuration is from this SO question.
https://stackoverflow.com/a/24920752/1499549
With your updated question details here is an example of what you can add:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/sysadmin/**").hasRole("SYSADMIN")
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/siteadmin/**").hasRole("SITEADMIN")
.antMatchers("/api/**").hasRole("USER")
// add the specific swagger page to the security
.antMatchers("/swagger-ui.html").hasRole("USER")
.anyRequest().permitAll();
// Custom JWT based security filter
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
The problem with this is it only protects the Swagger UI page and not the API specification which is loaded as a .json file from that UI page.
A better approach is to put the swagger files under a path so that you can just add antMatchers("/swagger/**").hasRole("USER")
A bit late to answer. I carried out a small POC to execute the same. I am using Keycloak and Spring Security. Below is my configuration
http
.antMatcher("/**").authorizeRequests()
.antMatchers("/swagger-resources/**","/swagger-ui.html**","/swagger-ui/").hasRole("admin")
.anyRequest().authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(new AccessDeniedHandlerImpl())
.defaultAuthenticationEntryPointFor(authenticationEntryPoint(), new CustomRequestMatcher(AUTH_LIST))
.and()
.httpBasic()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.csrf().disable()
.logout()
.logoutUrl("/logout")
.invalidateHttpSession(true)
.clearAuthentication(true)
.addLogoutHandler(keycloakLogoutHandler());
I have a working example here