I was working on security layer in my Spring Boot project and faced with the following problem:
SecurityContextHolder.getContext().getAuthentication()
This code returns:
String "anonymousUser" for Anonymous user
UserDetails object for authenticated user
So, I want to configure this code to return UserDetails for both cases. How can I do it?
As I guess, I need to implement custom AnonymousAuthenticationFilter. Am I correct?
As I guess, I need to implement custom AnonymousAuthenticationFilter.
Am I correct?
There is a simpler approach and that's the anonymous() method of the HttpSecurity DSL. Just use that block to set your desired principal:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// other configuration methods
#Override
protected void configure(HttpSecurity http) throws Exception {
UserDetails anonymousUserDetails = // your custom UserDetails for anonymous case
http
// other configurations
.anonymous()
.principal(anonymousUserDetails);
}
}
Using a Null Object Pattern-ish approach may be a good idea for that custom implemention for anonymous user.
Related
I would like to access the http request, specifically auth header in AuthenticationManager.authenticate() context.
Requirement is to authenticate a custom token. There is an external library which does that and so I don't have the luxury to read out principal from the token. Hence, in the custom filter, I am returning the full token in the getPreAuthenticatedPrincipal() method. This seems borderline incorrect and I would like to not pass the token pretending it to be principal.
Is there any way I can get it without violating any framework constraints?
Or is there a better way to handle the scenario which I'm trying to achieve?
Here's the config class:
#Configuration
#EnableWebSecurity(debug = true)
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity httpSecurity) throws Exception{
CustomTokenFilter customTokenFilter = new CustomTokenFilter();
customTokenFilter.setAuthenticationManager(new CustomAuthenticationMgr());
httpSecurity
// csrf etc etc
.addFilter(customTokenFilter)
.authorizeRequests()
.mvcMatchers("/users/**")
.authenticated()
.and()
.authorizeRequests()
.mvcMatchers("/other-api/**")
.permitAll()
.and()
.httpBasic();
}
Here's the custom token filter class:
public class CustomTokenFilter extends AbstractPreAuthenticatedProcessingFilter {
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
String authorization = request.getHeader("authorization");
if(authorization.indexOf("Custom") == 0){
return Map.of("Custom",authorization.split(" ")[1]);
}
return null;
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "";
}
}
And finally, the custom authentication manager class:
public class CustomAuthenticationMgr implements AuthenticationManager {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Map<String,String> map = (Map) authentication.getPrincipal();
String token = map.get("Custom");
// Custom validation - checking length here just to simplify
if(token.length() > 0)
authentication.setAuthenticated(true);
return authentication;
}
}
Version: Spring Boot 2.6.7 (transitive: spring-core 5.3.19)
Constraints: Cannot upgrade to other versions at the moment
Thanks in advance!
You're right, this isn't a good way to do it. (It's great you noticed -- too few people care whether their code is idiomatic.)
A better way would be to start by writing your own filter that actually just... does the authentication. You can extend OncePerRequestFilter rather than something more specific. That's what Spring Security itself does, both for basic authentication (BasicAuthenticationFilter) and for OAuth bearer tokens (BearerTokenAuthenticationFilter). You may want to take a careful look at the code for BearerTokenAuthenticationFilter since the problem it solves is very similar to yours. (I wouldn't extend it, though, since it's very clearly intended to do OAuth specifically. I wouldn't straight up copy the code either -- it's fairly simple as Spring Security filters go but probably still does more than you need. Try to understand the code instead; that will help a lot with your understanding of Spring Security in general.)
Okay, so you have a filter which looks a lot like BearerTokenAuthenticationFilter. That is, it contains an AuthenticationManager and its doFilter method consists of extracting the token from the request, passing that into the AuthenticationManager and then doing some SecurityContext-related stuff. Except, problem: AuthenticationManager.authenticate() expects an Authentication, not a String, and the token is a String.
The solution is to write a wrapper object for your token which implements Authentication. You can do this a couple of ways. Personally, what I'd do is use two classes: one which you pass into AuthenticationManager.authenticate(), and one which you get back. So we have, say, CustomTokenAuthenticationRequest implements Authentication and CustomTokenAuthentication implements Authentication. Both are immutable.
CustomTokenAuthenticationRequest basically just contains the token; its isAuthenticated() is return false, its getPrincipal() returns the token and its getCredentials() also returns the token. This is essentially what Spring Security itself does with BearerTokenAuthenticationToken.
CustomTokenAuthentication, on the other hand, probably contains a UserDetails of some sort; its isAuthenticated() is return true, its getName() is a username or user id or something, etc.
Now you need to teach the AuthenticationManager to authenticate CustomTokenAuthenticationRequests. The way to do this isn't to implement AuthenticationManager, it's to implement AuthenticationProvider. So you write a class that looks roughly like
public class CustomTokenAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication a) {
String token = ((CustomTokenAuthenticationRequest) a).getToken();
if (/* the token is valid */) {
CustomTokenAuthentication returnValue = // whatever you want it to be
return returnValue;
}
throw new BadCredentialsException("Invalid token");
}
#Override
public boolean supports(Class<?> authClass) {
return authClass == CustomTokenAuthenticationRequest.class;
}
}
Finally, wire it all up. Add the authentication provider to your HttpSecurity using its authenticationProvider() method. (If you do this, and you don't change the default authentication manager configuration, authenticationProvider() results in your authentication provider getting added to an AuthenticationManager which Spring Security configures for you -- an instance of ProviderManager.) Add the filter using addFilterAt(BasicAuthenticationFilter.class). Also, don't call httpBasic() because this adds a BasicAuthenticationFilter which I am guessing you don't want. Or maybe you want basic authentication and also your custom token authentication? But you didn't say that. If you do want both, you'll want to add your filter with addFilterBefore or addFilterAfter, and you need to think about ordering. Generally filter ordering is important in Spring Security.
I glossed over a lot of stuff here, barely gave you any code, and still wrote something of blog post length. Spring Security is very complex, and the thing you're trying to do isn't easily done in an idiomatic manner if you don't have much experience. I highly recommend just reading the Spring Security reference documentation from start to finish before you try implementing any of my suggestions. You'll also need to read quite a lot of Javadoc and tutorials and/or framework code. If there's something specific you want to follow up on I might respond to a comment, but I don't promise it; I had to do some research for this answer and have already spent more time on it than I planned to.
you should look spring-security-lambda-dsl,add filter,add auth provider
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.
I have a web service that I have built on top of Spring. I am currently authenticating using Spring Security as follows:
#Configuration
#EnableGlobalMethodSecurity(securedEnabled=true)
#EnableWebSecurity
public class ServerSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Properties properties;
private static final String ALL_URI = "/v1/**";
private static final String HEALTH_URI = "/v1/healthCheck";
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(getFilter(), BasicAuthenticationFilter.class);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers(HEALTH_URI).permitAll()
.anyRequest().authenticated();
http.csrf().disable();
}
private AuthenticationFilter getFilter() {
return new AuthenticationFilter( properties.getKey());
}
}
My AuthenticationFilter class extends AbstractAuthenticationProcessingFilter and performs the actual authentication. If I want to add Authorization to my security, would I just make those checks in the attemptAuthentication method apart of the AuthenticationFilter? Or is there a better way to do it? The way I understand it is that Authorization and Authentication should be done independently. You first authenticate, and then you verify the permissions. So, I would assume there would be a better approach to do authorization within Spring Security rather than just adding it to the attemptAuthentication method.
You need a AuthenticationProvider to do authenticate, implement the AuthenticationProvider and override the authentication and supports methods, and then inject to the AuthenticationManager.
attemptAuthentication method in filter is usually to get authentication(e.g. UsernamePasswordFilter gets username and password from request, and then builds a UsernamePasswordAuthenticationToken to AuthenticationManager ),
supports method tests the AuthenticationProvider whether can be used to do authenticate.(e.g DaoAuthenticationProvider supports UsernamePasswordAuthenticationToken)
authenticate method is used to do authenticate(e.g DaoAuthenticationProvider gets the real password by username and then compare to the user input), this method should return an Authentication that is already authenticated(e.g UsernamePasswordAuthenticationToken), and this authentication should contains the user authorities(this can be used to hasRole('xxx')), or use detail and so on.
After attemptAuthentication successful, the Authentication will set into SecurityContextHolder. and then you can use the hasRole('xx'), or something else.
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).
I've searched for a solution but can't find one anywhere, at least not a current one or one that uses non-xml based Spring and Spring Security configuration.
I need to implement a handler that will be used prior to the spring logout handler. I've read plenty of articles about the LogoutSuccessHandler but that is called after a successful logout by the Logout Filter and I need to access user data that is stored in the users session to perform some database entries, site logout info, etc. This session is lost once spring logs out the user so it has to be before that.
I've tried creating my own custom logout class and defined it in my application configuration class like this:
#Bean
public CustomLogoutHandler customLogoutHandler() {
return new CustomLogoutHandler();
}
and my class extends the LogoutHandler like the spring documents say to do:
public class CustomLogoutHandler extends LogoutHandler {
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
// business logic here
}
}
This is still not working. I put a breakpoint in the code and it never gets picked up. Does anyone have an idea of what could be causing this or what I need to do to get it to work?
To use your own custom logout handler that implements Spring's LogoutHandler.class you need to let Spring know that you are using your own in the configuration file under the logout options using .addLogoutHandler. I think you were missing this step. In the security config file:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
... // Other methods here
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.otherConfigOptions
.logout()
.addLogoutHandler(customLogoutHandler()) <- custom handler
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.otherConfigOptions....
}
}
And define the bean, I put mine in the SecurityConfig.class but I think you can put it in the web or app config class depending on how you set up your project.
#Bean
public CustomLogoutHandler customLogoutHandler() {
return new CustomLogoutHandler();
}
Then, create your CustomLogoutHandler.class, making sure to IMPLEMENT the LogoutHandler and OVERRIDE the logout method. Here you can use the Authentication class to access anything you have added to the users request scope.
public class CustomLogoutHandler implements LogoutHandler {
#Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
// business logic here
}
}
You should also take a look at this question and answer which talks about the order of custom handler mappings in Spring.
I hope this helps.