SpringMVC,Restful,API,how to match url to control privilege - java

SpringMVC,restful api
GET /order/{orderId}
POST /order/{orderId}/abc/{abcId}-{bcdId}
POST /order/{orderId}/myresource/{subResources:[a-zA-Z0-9_/]+}
role1 can call api1
role2 can call api1 & api2 & api3
how to match url for the API path
sorry My English is poor.

If you're using Java Based configuration you can do this:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(new AntPathRequestMatcher("/order/*", HttpMethod.GET.name())).hasAnyRole("ROLE1", "ROLE2")
.requestMatchers(new AntPathRequestMatcher("/order/*/abc/*", HttpMethod.POST.name())).hasRole("ROLE2")
.requestMatchers(new AntPathRequestMatcher("/order/*/myresource/**", HttpMethod.POST.name())).hasRole("ROLE2");
}
}
This is just showing the role based authorization config you can apply to the URLs, not the full Spring Security configuration. Just what regards to url matching role authorization.
There are many other RequestMatcher implementations you could use. You could implement your own too if the ant path matching isn't enough for you.
A completely different way of doing this with the same result would be to enable global method security with annotation #EnableGlobalMethodSecurity in your configuration file. An then using one of the #Secured, #PreAuthorize or #PostAuthorize annotations in your service/endpoint. For instance:
#RequestMapping(value="/order/{orderId}", method=RequestMethod.GET)
#Secured(value = {"ROLE1", "ROLE2"})
public #ResponseBody Order getOrder(#PathVariable("orderId") String orderId) {
...
}
Again, this just shows how you could apply the role authorization to your endpoint and not all config required for Spring Security.

Related

Spring Boot Security with Basic Auth and OAuth Order Issue

I'm trying to implement a simple spring boot project. I got several REST-Endpoints which I've to secure differently. One has to be secured by Basic Auth, another one with OAuth and one with a custom security implementation.
REST-Endpoints:
/basic/auth
/application/secure (oauth)
/application/secure2 (own implementation)
From tutorials, I know I've to set the order of the security adapters. My first intention was to set the order in steps of ten (e.g. #Order(10), #Order(20)) in case I need to add other security filters in between. By doing so I investigated the following behavior:
If I add the basic auth filter with #Order(10) and an OAuth filter with #Order(20) only the OAuth filter works.
If I add the basic auth filter with #Order(1) or #Order(2) and an OAuth filter with #Order(4) both filters works.
If I add a filter to #Order(3) I receive an error which says, that order 3 is already in use and cannot be configured twice.
So there is a default spring security adapter (or whatever) which has the default order 3. I thought I disable every default spring security behavior by adding #EnableWebSecurity. After I did not find an answer by google my questions would be:
Am I doing the right things?
What is this security adapter with order 3 by spring?
Does the default security adapter block my basic auth implementation?
WebSecurityConfig:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Order(10)
#Configuration
public class BasicAuthConfig extends WebSecurityConfigurerAdapter {
#Value("${security.user.password}")
private String password;
#Value("${security.user.name}")
private String username;
private static final String ROLE_ADMIN = "ADMIN";
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser(username).password(password).roles(ROLE_ADMIN);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestMatchers().antMatchers("/basic/**", "/") //
.and().authorizeRequests().anyRequest().authenticated() //
.and().httpBasic();
}
}
#Order(20)
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class Oauth2ServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
System.out.println("Filter called");
// #formatter:off
http.csrf().disable();
http.authorizeRequests().antMatchers("/application/**").authenticated()
// .antMatchers(GET, "/application/secure").authenticated()
.anyRequest().authenticated();
// #formatter:on
}
// offline token validator
}
This is an old question, but if anyone is still wondering what the issue is, here are my observations:
#EnableResourceServer imports ResourceServerConfiguration, which has an order of 3.
There are ways that may allow you to add more than 2 filters before the order 3 resource server configurer, for instance
by giving some of them negative order values (Although I don't suppose negative values would be any special, one would need to take into account other implicit web security configurers -- for instance the one with order 0 -- enabled by default. This however means there is a possibility of collision between filters in different versions of the framework as new features are introduced);
by adding them as resource configurers (The ResourceServerConfiguration class does not add any request matchers, but enforces a fallback to anyRequest().authenticated() if the user has not configured anything).
For a better understanding on how paths are matched in the configured request matchers, you can take a quick glance at Ant path patterns.

Spring Security REST API roles based on URL parameters

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)")
...

Spring Security: How to reject a request by default if no #PreAuthorize was specified

I have a simple Spring Boot application which exposes a REST API.
I have successfully configured the spring security to secure every method in the rest API according to its ROLE, using the #PreAuthorize("hasRole('ROLE_4')") annotation.
I have noticed that If I don't put the #PreAuthorize annotation at all, the framework allows this request to any authenticated user. I want to reverse this behavior. So if one of the programmers will forget to add the #PreAuthorize annotation, any request to this method will be rejected automatically.
Here is my configuration:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//Disable HTTP Basic authentication
http.httpBasic().disable();
//Add the filters
http.addFilterBefore(new AuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(securityServiceAuthenticationProvider());
}
#Bean
public AuthenticationProvider securityServiceAuthenticationProvider() {
return new SecurityServiceAuthenticationProvider();
}
}
Thanks
Guy Hudara
You can use a MethodSecurityInterceptor; sample XML configuration is here. The example applies security to a single bean, but the expressions are very flexible, you can protect e.g. all public members of any class with name ending in "Controller". I have used a similar XML configuration before, but I haven't done this with Java configuration; but I suppose you can do the same thing in Java configuration.
You could specify a custom AccessDecisionManager and, if the queried object is a MethodInvocation, then check if it has a #PreAuthorize annotation; if yes, let it pass. Otherwise, fail it. You can add it to the configuration like this: http.authorizeRequests().accessDecisionManager(myManager).

spring boot ldap group and restricted endpoints

I want to restrict certain rest endpoints to be only for LDAP users in a certain group.
I followed the guide https://spring.io/guides/gs/authenticating-ldap/ to setup LDAP authentication which is working perfectly. So how do I restrict certain rest endpoints?
I tried
#PreAuthorize("hasRole('developers')")
#RequestMapping("/foo")
public String foo(HttpServletRequest request) {
return "Welcome to FOO " + request.getRemoteUser();
}
but it still lets users not in the developers group access that endpoint
You can modify your WebSecurityConfigurerAdapter configuration to something like:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.antMatchers("/foo").hasRole("developers")
.and()
.formLogin();
}
I am not exactly sure of the syntax and if that first rule will override your second rule, but it will be similar to that.
Or, you can try configuring security on a method by method basis like this sample.
#EnableGlobalMethodSecurity(securedEnabled=true) needed to be added to the webSecurityConfig. Once I did that I was able to use #Secured("ROLE_DEVELOPERS") and that method was then restricted to that role.

Spring Boot security form login and Oauth2 resource server

I'm using Spring Boot and I want my app to host Oauth2 resource server for accessing my api endpoints on the same server. I also need to have a web interface with secured pages via form login.
For example I have api endpoints /api/v1/** where requests can only be made via having a token from my oauth2 resource server.
Additionally there are endpoints like /account/** where user needs to be logged in via form.
All of this needs to be in one Spring Boot instance for now.
My WebSecurityConfig file:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/account/**").authenticated()
.and()
.httpBasic();
}
}
And in my Oauth2SecurityConfig I have:
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/v1/me/**").authenticated();
}
}
The problem is, oauth2 config seems to override the first configuration and all my webpage resources are exposed without asking username password in form login. And if I try accessing my api endpoints I get the expected oauth error response.
Do I need to have them both in one overriden method? Do I need to have 2 instances of HttpSecurity? How can I solve this?
I had faced the same situation. Finally got a solution for this. You just need to make the use of #Order annotation.
Add this to your WebSecurityConfig class
#Order(1)
And add this to your ResourceServerConfiguration class
#Order(2)
To secure all your resources you would have to add
.authorizeRequests().anyRequest().authenticated(). Did you mean that your "/account/**" resource is exposed?
Those two HttpSecurity objects are not the same object. That being said you don't need to have both of them configured, but each one of them serves different purpose. (I have both of them configured in my project and it's working fine)
I would start by reviewing your expectations for this as I am not sure whether it is feasible to have a part of your server protected by OAuth and another part by another authentication mechanism (Form login). Both would be considered .authenticated(). You could manage the access by roles and oauth scopes, but then you would have to also provide both login/logout mechanisms by different filters and maybe another filter to provide persisting of both authentications when navigating through your server. Seems to me like a lot of not so standard work and I would think about different solutions.

Categories