Spring Boot Security with Basic Auth and OAuth Order Issue - java

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.

Related

Spring Boot HTTP security configuration anonymous filter and a custom filter on a different path

I have experienced a strange problem while trying to configure HTTP security by using WebSecurityConfigurerAdapter. Here is the full configuration class I tried so far:
#Slf4j
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
#ConditionalOnProperty(name = "module.client.security.enabled")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${management.endpoints.web.base-path}")
private String managementEndpoint;
#Autowired
private List<ModuleProperties> moduleProperties;
#Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(this.managementEndpoint + "/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable();
http.formLogin().disable();
// FIXME also doesn't work because of the later http.requestMatchers().antMatchers() calls
http.authorizeRequests()
.antMatchers("/**").anonymous();
http.requestMatchers()
.antMatchers("/app/**")
.and()
.addFilterBefore(new ClientResolveFilter(), FilterSecurityInterceptor.class);
}
What I would like to do is actually enabling anonymous authentication for all endpoints — to prevent NullPointerExceptions when operating on SecurityContextHolder — Plus, enabling/adding a custom filter to only a subset or different endpoint path which is /app/** in this case.
I expected the above code would work but what exactly happens is the AnonymousAuthenticationFilter disabled for all and both filters only work for the path /app/** only.
If I remove the http.requestMatchers().antMatchers("/app/**") part, then AnonymousAuthenticationFilter works for all paths as normal. I suspect that the second .antMatchers("/app/**") call kinda replaces the former one or substitutes it implicitly which doesn't make sense to me, but I could be wrong.
I tried diving into the source but still confused and cannot find a clean solution to make it work as my expectation. Any ideas and help will be appreciated.
Cheers!
EDIT: I'm using Spring Boot 2.5.2 and the Spring Security version is 5.5.1
The addFilterBefore (and other addFilter*) method will add the filter to the filter chain which apply to all request. If you want the filter to apply to certain requests only, then you have to check inside the filter (eg. using HttpServletRequest.getgetRequestURI() to check the url).
After #Dickson's advice, I found a special bean called FilterRegistrationBean provided by spring boot.
Thus, I configured it as a bean which applies a specific servlet filter to only configured paths:
#Bean
public FilterRegistrationBean<ClientResolveFilter> clientResolveFilter(){
final FilterRegistrationBean<ClientResolveFilter> frb = new FilterRegistrationBean<>();
frb.setFilter(new ClientResolveFilter());
frb.addUrlPatterns("/app/*");
return frb;
}
This solution worked perfectly for my requirement.
Pay attention to that the path string is not an ant matcher now — must be written with single /app/* instead of double /app/** — it's actually the pattern when we manually configured web.xml file like in the old days :)

Understanding spring-security-oauth2 #EnableAuthorizationServer

I have a spring-security-oauth2 project running smoothly with a class as Authorization server.
The client-ids, user-tokens, refresh-tokens are all managed by the database.
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static String REALM = "MY_OAUTH_REALM";
...
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.realm(REALM + "/client");
}
}
Everything is working fine except that i don't have any idea what the configure method is doing. Even if i remove the complete method the oauth2 process still works fine.
What is the main use of configure method in this context and what realm is it setting here?
Please help me in understanding it.
Thanks.
Purpose of configure method
AuthorizationServerConfigurerAdapter has three configure(...) methods and all three could be overridden and those serve different purposes.
In your question, you have quoted only one.
Their purpose is to provide your custom settings for Authorization Server end points, clients & security. So its up to you as how many you wish to override as there are some predefined default settings.
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// This can be used to configure security of your authorization server itself
// i.e. which user can generate tokens , changing default realm etc.
// Sample code below.
// We're allowing access to the token only for clients with 'ROLE_TRUSTED_CLIENT' authority.
// There are few more configurations and changing default realm is one of those
oauthServer
.tokenKeyAccess("hasAuthority('ROLE_TRUSTED_CLIENT')")
.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// Here you will specify about `ClientDetailsService`
// i.e. information about OAuth2 clients & where their info is located - memory , DB , LDAP etc.
// Sample code below.
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// This can be used to configure security of your authorization server itself
// i.e. which user can generate tokens , changing default realm etc - Sample code below.
// we're allowing access to the token only for clients with 'ROLE_TRUSTED_CLIENT' authority.
// There are few more configurations and changing default realm is one of those
oauthServer
.tokenKeyAccess("hasAuthority('ROLE_TRUSTED_CLIENT')")
.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// Here you will specify about `ClientDetailsService` i.e.
// information about OAuth2 clients & where their info is located - memory , DB , LDAP etc.
// Sample code below
clients.inMemory()
.withClient("trusted-app")
.authorizedGrantTypes("client_credentials", "password", "refresh_token")
.authorities("ROLE_TRUSTED_CLIENT")
.scopes("read", "write")
.resourceIds("oauth2_id")
.accessTokenValiditySeconds(10000)
.refreshTokenValiditySeconds(20000)
.secret("secret");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// Here you will do non-security configs for end points associated with your Authorization Server
// and can specify details about authentication manager, token generation etc. Sample code below
endpoints
.authenticationManager(this.authenticationManager)
.tokenServices(tokenServices())
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter());
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("abcd");
return converter;
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenEnhancer(accessTokenConverter());
return defaultTokenServices;
}
Purpose of #EnableAuthorizationServer
A javadoc explanation is already provided in previous answer.
In layman's language, this is to set up your token generation end point i.e.
if you provide the properties security.oauth2.client.client-id and security.oauth2.client.client-secret, Spring will give you an authentication server, providing standard Oauth2 tokens at the endpoint /oauth/token
In practical scenario, what this means is that you are setting up a token generation web-application ( Layer-7 ) on top of your enterprise User LDAP or User Database and is usually a separate application from your consumer side apps ( APIs etc ).
If you have a look at the JavaDoc comment for #EnableAuthorizationServer you can see that it says the following;
Convenience annotation for enabling an Authorization Server (i.e. an
AuthorizationEndpoint and a TokenEndpoint in the current application
context, which must be a DispatcherServlet context. Many features of
the server can be customized using #Beans of type
AuthorizationServerConfigurer (e.g. by extending
AuthorizationServerConfigurerAdapter. The user is responsible for
securing the Authorization Endpoint (/oauth/authorize) using normal
Spring Security features (EnableWebSecurity #EnableWebSecurity
etc.), but the Token Endpoint (/oauth/token) will be automatically
secured using HTTP Basic authentication on the client's credentials.
Clients must be registered by providing a ClientDetailsService through
one or more AuthorizationServerConfigurers.
Extending AuthorizationServerConfigurerAdapter is just used for customization of the Authorization Server. You can easily set up a functioning Authorization Server within Spring Security by Just Annotating a Bean class with #EnableAuthorizationServer

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

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

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.

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