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

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

Related

How does one use different session creation policies for UI and REST endpoints with Spring Security?

I have an application that contains both a UI and some REST endpoints. The UI uses SAML login (the old Spring Security SAML extension) and the REST endpoints using a custom authentication. The REST endpoints are only called by external applications.
For the REST endpoints ("/api/**") I have stated a stateless session creation policy and for the rest of the endpoint no session creation policy at all (I also tried with ALWAYS as in the below example).
Prior to some Spring Boot version, not sure which, this worked. Currently I'm using Spring Boot v.2.6.1. The UI endpoint got the authentication object from the Http session.
But now it doesn't work. The security context object cannot be found in the Http session using the default HttpSessionSecurityContextRepository implementation. It is saved but it can't be restored.
So is it possible to use two session creation policy, one for the REST and the other for the UI part, or should this be handled in a different way?
Now it seems that the stateless session creation policy is also used by the UI, which is not intended.
I'm using two WebSecurityConfigurerAdapter classes; one for the API and the other for the UI.
After a successful SAML login the redirect URL now contains the ";jsessionid=6051854D94A0771BB9B99FE573AA4DFD" parameter. Probably because of the stateless policy...?
protected void configure(HttpSecurity http) throws Exception {
List<AbstractAuthenticationProcessingFilter> authFilters = new ArrayList<>();
authFilters.add(new OAuthMacAuthenticationProcessingFilter(authenticationManager(), this.properties));
ApiAuthenticationProcessingFilter apiAuthenticationProcessingFilter = new ApiAuthenticationProcessingFilter(authenticationManager(),authFilters);
http
.csrf()
.disable()
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.addFilterBefore(apiAuthenticationProcessingFilter, BasicAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
and for the UI part
protected void configure(HttpSecurity http) throws Exception {
http.securityContext().securityContextRepository(customSessionSecurityContextRepository);
http
.httpBasic()
.authenticationEntryPoint(samlEntryPoint());
http
.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class);
var auth = http
.authorizeRequests()
.antMatchers("/saml/**").permitAll()
.antMatchers("/loggedout/**").permitAll()
.antMatchers("/error").permitAll();
auth
.anyRequest()
.authenticated();
http.csrf().disable();
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
http.headers().frameOptions().sameOrigin();
http.exceptionHandling().accessDeniedHandler(this.accessDeniedHandler());
http
.logout()
.disable(); // The logout procedure is already handled by SAML filters.
}
I'll answer this myself. The above code does actually work. The problem was on the remote end, the IDP I was using had some problems that day that resulted in that it didn't work as expected. The day after, it worked.

Spring boot and Spring Security, different logon forms to use different success handlers

I have a spring boot/mvc site using spring security.
I have to use ways of logging in,
In the navbar present on each page
and the login page which you are redirected to when attempting to access a restricted resource.
For the navbar i'd like the user to stay on the page after successful login
For the login page i'd like the user to be redirected to the resource they were trying to originally access after login.
I can do each functionality individually
First use case is handled by:
SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
handler.setUseReferer(true);
Second case is the default functionality.
But i've been unable to make them both work.
Does anyone have any insights on how to achieve this?
You can configure each login page with a different AuthenticationSuccessHandler like described here
https://www.baeldung.com/spring_redirect_after_login
Like:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/anonymous*").anonymous()
.antMatchers("/login*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
.successHandler(myAuthenticationSuccessHandler())
.and()
.formLogin()
.loginPage("/login2.html")
.loginProcessingUrl("/login2")
.successHandler(mySecondAuthenticationSuccessHandler())
// ...
}

How to allow certain endpoint in spring security to be allowed without authentication?

I have Spring Boot Rest API web app in which I am using spring security to have most endpoints to require authentication.
This is part of code:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
....
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.requestMatchers()
.antMatchers("/oauth/token")
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated();
}
Can some one explain what each line begining with http.csrf means?
How can I modify above code so that enpoint /bars/pk can be allowed to be accessed without requiring authentication just as if there was no Spring Security ?
By default, Spring Boot activates protection against CSRF attack (Cross Site Request Forgery attack). The attack consists of a malicious site taking advantage of a user being alredy authenticated to a site (e.g. bank) in order to trick the user to do actions on that site (e.g. fund transfer).
The protection against the attack consists of the Spring Boot application sending a token with every response and expecting the token to be sent by the client on subsequent request. If the token is not received, Spring Boot returns an error.
Sometimes, you want to disable this behavior (at your own risks), so you use csrf.disable. You might find it convenient to disable csrf protection if you develop a Stateless API, and you have no way to link a POST request to any previous requests or session. But again, you need to consider this and reason about it carefully.
Please note that CSRF protection has not effect on GET requests. It only affects state chaning requests (e.g. POST, DELETE)
In order to allow your endoints to anyone, without requiring any authentication, you need to use
http.authorizeRequests().antMatchers("/**").permitAll();
EDIT
To specifically allow unauthorized requests to /bars/pk and keep the other elements unchanged, modify your code as follows :
http.csrf().disable()
.requestMatchers()
.antMatchers("/oauth/token")
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers("/bars/pk").permitAll()
.anyRequest().authenticated();
Here is a complete example:
httpSecurity.authorizeRequests()
.antMatchers(HttpMethod.GET)
.permitAll() // Allow all GET requests to go unauthenticated
.antMatchers(allowedResources)
.permitAll() // Allow all requests to go unauthenticated for the specified paths
.antMatchers(protectedResources).hasRole(USER)
.antMatchers(adminResources).hasRole(ADMIN)
.anyRequest().authenticated(); // Authenticate all other request paths

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!

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

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
}

Resources