Spring Security REST API roles based on URL parameters - java

I have a REST API written in Spring Boot with Spring Security and OAuth2. The resources are secured this way:
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/v1/security/**").hasRole("ADMIN");
}
I'd like to introduce a new part of the API where the permissions are fine grained, based on projects. Let's consider a simple endpoint that prints the project configuration.
GET /api/v1/project/{projectId}/config
How would I configure the resource server to only allow access for users who have the role ROLE_PROJECT_{projectId}_ADMIN without having to manually specify all projects?
Also if this mechanism has a specific name, please let me know in comments to I can change the question title.

You can use path values in authorization expressions.
According to Path Variables in Web Security Expressions you should write your custom authorization logic.
public class WebSecurity {
public boolean checkUserHasAccessToProjectId(Authentication authentication, int projectId) {
// here you can check if the user has the correct role
// or implement more complex and custom authorization logic if necessary
}
}
Then in your Java security configuration you can refer to this method and pass it the value of the relevant path fragment.
http.authorizeRequests()
.antMatchers("/api/v1/project/{projectId}/config")
.access("#webSecurity.checkUserHasAccessToProjectId(authentication,#projectId)")
...

Related

How to check security access before validation (#Valid) in Controller?

I am creating a Restful API with Spring Boot 2.5 and would like to know the right way to implement validation while checking roles for some routes. Also, for some routes I need to make sure that only admins can modify the resource or its owner.
#PreAuthorize seems to be the solution, but #Valid seems to be processed before an actual method call, otherwise known as executed before #PreAuthorize.
See : How to check security acess (#Secured or #PreAuthorize) before validation (#Valid) in my Controller?
Is this really the only available and clean solution to make a Restful API with both validation and roles with Spring Boot & Spring Security?
I'm afraid that that is the cleanest solution.
To check roles for some routes, you can configure your HttpSecurity to check roles before even getting to the controller, like so:
#Bean
SecurityFilterChain app(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests
.antMatchers("/route1").hasAnyRole("ADMIN", "USER")
)
return http.build();
}
So, with this configuration, you are making sure that only ROLE_USER or ROLE_ADMIN are allowed to request /route1.
But now, the ROLE_USER is allowed only if they are the resource owners. For this, you must have to resolve the method parameters to know which resource you are requesting. And then, in the #PreAuthorize, you can do something like this:
#PreAuthorize("#myBean.isResourceOwner(resourceId, authentication)")
#PutMapping("/{resourceId}")
public void update(#PathVariable Long resourceId) {
...
}

Spring security and() function

Can you please explain in a simple way for what reason we need to use and() method in HttpSecurity.
Code:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").hasAnyRole("Employee", "Manager", "HR")
.antMatchers("/hr_info").hasRole("HR")
.antMatchers("/manager_info/**").hasRole("Manager")
.and().formLogin().permitAll();
}
The spring security reference explains it as follows.
The Java Configuration equivalent of closing an XML tag is expressed
using the and() method which allows us to continue configuring the
parent. If you read the code it also makes sense. I want to configure
authorized requests and configure form login and configure HTTP Basic
authentication.
Accordingly, you say the following with your own settings:
Ensures that any request to our application requires the user to be authenticated,
Allows authentication for some links only to the specified role,
Allows users to authenticate with form based login.

Spring Boot with OAuth 2

I'm using Spring Boot to create my microservices and I'm enabling the OAuth2 to add security to my services.
However, there are some methods that I can not understand what are the differences between then. For example, I have the following code:
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(STATELESS);
http.authorizeRequests()
.antMatchers(POST, "/v1/files/").access("#oauth2.clientHasRole('ROLE_CLIENT')");
}
In this example, I used the access method to check if the system that is going to access my services has the ROLE_CLIENT role.
The question is : what are the main differences between the following methods:
hasRole
hasAuthority
access
hasRole(NAME) checks that client has ROLE_NAME whether hasAuthority(NAME) checks only NAME role.
hasRole("CLIENT") is equivalent to hasAuthority("ROLE_CLIENT")

Develop Spring Security as a Common Module and Swicting it in Spring Boot Micro Service Architecture

I have a micro service architecture with spring boot. I decided to add Spring security for each micro service which will authenticate, authorise the user.
So i develop a separate project with has Spring Security authentication.
I have use a Filter which extends AbstractAuthenticationProcessingFilter.
The paths which needs authentication and authorisation are mentioned in my filter class as below,
private AntPathRequestMatcher[] authenticationMatcher = {
new AntPathRequestMatcher("//api/myservice1/**"),
new AntPathRequestMatcher("/api/myservice")
};
private AntPathRequestMatcher[] authorizationMatcher = {
new AntPathRequestMatcher("/api/myservice")
};
So in the filter class doFilter method i check request path and do relevant logics.
My SecurityConfig class configure method just look like below,
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(getMyAuthenticationFilter(), BasicAuthenticationFilter.class);
}
So my questions are,
What approach i should do for introduce this module (project) to each micro service?
What i had in my mind is expose this as a jar file and use it in any micro service. In that case how can i over ride those authenticationMatcher and authorizationMatcher url's which will be specific to each micro services?
Am i declare those url's in correct place and if so what Object Oriented principles i should apply?
Is there a possibility i can by pass authentication filter if required and enable it when required? Like switching it?
I believe this approach can work like you want and can be done using Spring Boot. In regards to your questions:
In your filter class you can declare something like this which can be populated by bean initialization.
#Value("${security.checkValues}")
private String[] checkValues;
Below is an example I used with my own custom filter declared as a bean and passed in to the security configuration. You probably don't need all of it.
#Configuration
#Order(3)
public static class SubscriptionWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/subscribe**","/subscribe/**").and()
.addFilterBefore(applicationSecurityTokenFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
#Bean
public ApplicationSecurityTokenFilter applicationSecurityTokenFilter() {
return new ApplicationSecurityTokenFilter();
}
}
Then each application.properties instance in your micro services (assuming they are separate projects) will have a line like below to specify the URLs to use.
security.checkValues=/api/myservice,/api/myservice2
If you include an empty string property like this:
security.checkValues=
Then the String array will be set to a 0 length array and you can know that it should not be active. I'm not entirely sure this is what your question was referencing so please review.
Let me know if this is what you are looking for. We can flush it out a little further if necessary.

Security config when using #PreAuthorize

My security config class (which inherits from WebSecurityConfigurerAdapter) has a method like the following.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/restaurant/**").access("hasRole('ROLE_USER')")
.and()
.formLogin();
}
However I'd rather use #PreAuthorize on my controllers instead. If I remove the method everything requires auth. What should my method look like so everything is available and access is only determined by PreAuthorize?
As has been already stated, it is not very common to use method level security to secure controller methods but rather to secure methods with business logic. And even if you need to perform authorization based on request attributes, it should be possible to achieve this with URL based security and web security expressions.
Available expressions are defined by WebSecurityExpressionRoot class, an instance of which is used as the expression root object when evaluation web-access expressions. This object also directly exposed the HttpServletRequest object under the name request so you can invoke the request directly in an expression.
Here you can find more details on when to use URL based security and when method level security.
It is rather uncommon to use #PreAuthorize on controller methods, but there may me use cases, if the decision depends on request parameters ...
If you do not want to do any authorization at the request level, you can simply have :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin();
}
You only declare a form login, and no request security. But do not forget that request security uses less resources than method security.
Instead of .access("hasRole('ROLE_USER')"), try .access("permitAll"). Note that for request mappings that doesn't have a #PreAuthorize, everyone will be given access.

Categories