Spring boot security, applying an authentication filter only to certain routes - spring

I'm building a web application which will contain an API and an admin interface in a single application. As a result, I need two types of authentication, token based auth for the API, and form based auth for the admin interface.
I've almost got it working by applying a filter to authenticate API tokens, however the filter is being executed for every request, and I only want it to be executes on paths matching '/api/**'.
Hopefully it's clear from my security configuration what I'm trying to do, but sadly it doesn't work as expected.
All API requests will start '/api/', while all admin interface requests will start '/admin/'. So I was hoping to apply different security rules to each.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api/account/login").permitAll();
http.addFilterBefore(webServiceAuthenticationFilter, UsernamePasswordAuthenticationFilter.class).authorizeRequests().antMatchers("/api/**").hasAuthority("APIUSER");
http.authorizeRequests().antMatchers("/admin/**").authenticated().and()
.formLogin()
.loginPage("/admin/account/login").permitAll()
.passwordParameter("password")
.usernameParameter("username")
.failureUrl("/admin/account/login?error").permitAll()
.defaultSuccessUrl("/admin/dashboard")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/admin/account/logout"))
.logoutSuccessUrl("/admin/account/login");
http.exceptionHandling().accessDeniedPage("/admin/account/forbidden");
}

There is a way to configure several HttpSecuritys depending on the url by using the antMatcher (or in more advanced cases requestMatchers) on the HttpSecurity directly (not on authorizeRequests!). See: https://docs.spring.io/spring-security/site/docs/current/apidocs/org/springframework/security/config/annotation/web/builders/HttpSecurity.html#antMatcher-java.lang.String-
This requires defining several WebSecurityConfigurerAdapters with defined #Orders such that Spring uses the first appropriate configuration depending on the given url and the order of the configurations. For more details please take a look at the docs at http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#multiple-httpsecurity

I don't know if this is the 'correct' way of doing it, but I've managed to only get the filters code to execute when a route is matched with '/api/**' by adding an if statement to the filter itself;
So within my filter I have the following;
AntPathMatcher urlMatch = new AntPathMatcher();
if (urlMatch.match("/api/**", httpRequest.getRequestURI().substring(httpRequest.getContextPath().length()))) {
// Token authentication in here
}

Related

Spring security is blocking Vue.js pages | receiving 403 error

I have a Spring/Vuejs project that couples the frontend and backend into one warFile and for some reason the Spring Security setup I have blocks the Vue.js pages when I run the warFile.
When I have the Configuration set this way, it blocks the pages -
#Override
public void configure(HttpSecurity security) throws Exception {
security.csrf().disable()
.cors().and()
.authorizeRequests()
.antMatchers("/api/v1/authenticate", "/api/v1/register").permitAll()
.anyRequest().authenticated().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
security.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
When I have it set up this way, it shows the pages AND the data from my api's (it creates the JWT but still allows the api calls without Authorization) -
#Override
public void configure(HttpSecurity security) throws Exception {
security.csrf().disable()
.cors().and()
.authorizeRequests()
.antMatchers("/api/v1/authenticate", "/api/v1/register", "/**","/css/**","/js/**").permitAll()
.anyRequest().authenticated().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
security.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
When I have it setup the first way and run the frontend and backend on different ports, it works perfectly. It shows the pages and blocks all API calls that don't have an authorized JWT.
I want the same type of result with backend/frontend in the same warFile
Is there a solution for this???
When I have it setup the first way and run the frontend and backend on different ports, it works perfectly.
I wonder if this is due to the CORS policy being invoked. Otherwise, Spring Security doesn't take the port number into consideration when making authorization decisions.
When I have the Configuration set this way, it blocks the pages -
That's because you have this line:
.anyRequest().authenticated()
It means that any URL, including JS files, will require authentication.
When I have it set up this way, it shows the pages AND the data from my api's
That's because you have this line:
.antMatchers("/**").permitAll()
It means that any URL is permitted.
The DSL processes URLs in the order the paths are declared. I think you want a hybrid of the two. Something like this:
.antMatchers("/api/v1/authenticate", "/api/v1/register", "/css/**","/js/**").permitAll()
.anyRequest().authenticated()
This means that the two /api endpoints all /css endpoints and all /js endpoints won't require authentication, but everything else will.
Consider making the permitAll as narrow as you can -- it's not clear to me whether you really want all your JS files to not require authentication.
#jzheaux your root comment led me to the answer.
This was the change I made and it works just as I need it to now. Thanks for the guidance!
#Override
public void configure(HttpSecurity security) throws Exception {
security.csrf().disable()
.cors().and()
.authorizeRequests()
.antMatchers("/api/v1/authenticate","/api/v1/register","/static/**","/").permitAll()
.anyRequest().authenticated().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
security.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}

Spring boot security, API-key protection OR oauth2 resourceServer for same resources

I have a spring boot 2.4 application where I want to protect it with either an API-key or a resource server. I was thinking that I could use filters here to first check if the api key is given and if so, grant access to the resource, otherwhise a "second chance" should be given to authenticate with an opaque oauth2-token (api key for machine to machine, token for frontend -> backend)
Where I get stuck is that my security config looks like this today (with a resource server activated)
#Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? =
http.authorizeExchange()
.anyExchange().authenticated()
.and()
.oauth2ResourceServer {
it.authenticationManagerResolver(myMultiTenantResolver)
}
.build()
how would I go about to add an API-protection in here which should grant access (if it succeeds) without also invoking the resourceServer-snippet here (if it doesn't succeed, the resourceServer-snippet should be invoked)?
One possible solution can be as following:-
Create both your filters i.e the api-key filter and the auth-token filter.
In your configure(HttpSecurity http) method of ApplicationSecurityConfiguration add the api-key filter before the auth-token filter.
If you pass the api-key, put you authentication details in securityContextHolder. In the next filter(auth-token filter) Override the doFilter, where you need to check that if the previous filter has been authenticated, you do not run the current filter(auth-token filter) by calling chain.doFilter(request, response).
Please let me know if you need the complete implementation.

Configure communication between multiple OAuth2 authorization servers and a single resource server

I'm currently setting up a single resource server that will be validating access tokens from various authorization servers.
Spring security (using the Okta security starter with this as well) seems to only allow me to set a single issuer URI.
I managed to find a solution that works but I'm unsure if this is the best practice/standard way of doing it. In the code snippet below I've explicitly setup the resources with Spring's Java Config for simplicity.
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange()
.pathMatchers("/api/protected/by/authserver1")
.and()
.oauth2ResourceServer()
.jwt()
.jwtDecoder(ReactiveJwtDecoders.fromOidcIssuerLocation("https://authserver1")
.and()
.and()
.authorizeExchange()
.pathMatchers("/api/protected/by/authserver2")
.and()
.oauth2ResourceServer()
.jwt()
.jwtDecoder(ReactiveJwtDecoders.fromOidcIssuerLocation("https://authserver2");
return http.build()
}
This seems to work exactly as intended, tokens minted from one auth server and used on the endpoint validating the other receive 401. When the minted tokens are used on their respective endpoint, they are successfully validated.
It looks a little funny having .and() calls back to back, I'm under the impression that these chained calls are just creating multiple web filters under the hood? Either way, is this the standard way of enabling this functionality in a Spring application with Spring Security and WebFlux?
Additionally, I came across this SO question but I don't know that I'll be able to setup a 'federation provider' within the context of this project. However, If that approach is the best practice I'd like to know. However, I think that's happening to some extent at the Okta level with the federation broker mode on the auth server access policies...?
Either way, is this the standard way of enabling this functionality in a Spring application with Spring Security and WebFlux?
No. What's more the example you've provided won't work. You can investigate the ServerHttpSecurity implementation and see why. Actually when you call oauth2ResourceServer() it sets new OAuth2ResourceServerSpec or returns the old one which can be modified. So in your case only the second JwtDecoder will be applied, because it overrides the first one. If you want to configure oauth2ResourceServer per path you'll have to define multiple SecurityWebFilterChain as posted here https://stackoverflow.com/a/54792674/1646298 .

Restricting access to certain methods in Controller classes for newly introduced Role using Spring Security

What is the default behavior of a Controller method that doesn't have either #PreAuthorize("hasRole('ROLE_xxxx')") or #Secured("ROLE_xxxx") annotation.
Which roles are allowed to make use of these methods? Is every Authenticated user able to call those methods?
Considering the following scenario.
On an already existing application that makes use of Spring Security, we introduce an new kind of user role, i.e. "ROLE_THIRD_PARTY_CONTRACTOR" and we want them to be able to login but allow them access only to certain methods in certain Controllers. How do I restrict those users from accessing methods that don't use #PreAuthorize or #Secured annotations?
you could try to secure at the http level before you reach the controller.
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated()
.and()
// ...
.formLogin();
}
See: https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc-httpsecurity

Spring security antMatchers permitAll doesn't work

I know that there are topics with this problem, but the config which I made is correct and I compared it to a project where it works correctly.
I want to "unsecure" a /login endpoint for JWT security, but still the AuthenticationFilter runs before reaching the /login endpoint.
I'm confused why it is no working.
My Code Below :
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated();
http
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
http.headers().cacheControl();
}
Duplicate: How to add a filter only for one special path WebSecurityConfigurerAdapter
You can't do that with a single Configuration class. Take a look at this question: How to apply spring security filter only on secured endpoints?.
In this case, I think the better solution is to configure multiple HttpSecurity. From Spring IO documentation:
We can configure multiple HttpSecurity instances just as we can have
multiple blocks. The key is to extend the
WebSecurityConfigurationAdapter multiple times. For example, the
following is an example of having a different configuration for URL’s
that start with /api/.
The documentation has a full example with the necessary steps to accomplish this:
Configure Authentication as normal
Create an instance of WebSecurityConfigurerAdapter that contains
#Order to specify which WebSecurityConfigurerAdapter should be
considered first.
The http.antMatcher states that this HttpSecurity
will only be applicable to URLs that start with /api/
Create another instance of WebSecurityConfigurerAdapter. If the URL does not start with /api/ this configuration will be used. This
configuration is considered after ApiWebSecurityConfigurationAdapter
since it has an #Order value after 1 (no #Order defaults to last).
Good luck!

Resources