Use methods directly that back spring security's built-in expressions - java

I'd like to use some (Java) methods in my controllers that are similiar to the built-in expressions provided by Spring Security e.g. hasRole([role]) or isFullyAuthenticated().
Do you know where I can find these methods and how to call them within a Java method of a controller (I don't want to use Spring EL, I want to use plain Java)? E.g something like
SomeStaticSpringSecutityClass.isFullyAuthenticated();
EDIT:
SecurityContextHolder.getContext().getAuthentication().isAuthenticated()
Actually doesn't really work. This method also returns true if the user is authenticated as "Anonymous". See the link to the spring security docs from above:
isAuthenticated() Returns true if the user is not anonymous
Instead you have to use something like that:
public boolean isAuthenticated() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return !(authentication == null || authentication instanceof AnonymousAuthenticationToken);
}
But anyway: I don't really want to implement logic again, that is already implemented somewhere in Spring Security. Additionally
SecurityContextHolder.getContext().getAuthentication()
does not provided methods like isFullyAuthenticated() or hasRole().

Take a look at org.springframework.security.core.contex.SecurityContextHolder. For instance to check if the current user is authenticated:
SecurityContextHolder.getContext().getAuthentication().isAuthenticated()

I found the SecurityContextHolderAwareRequestFilter that use the interface:
AuthenticationTrustResolver.isAnonymous(Authentication authentication);
If you want to use the Spring source code, you can check the class SecurityContextHolderAwareRequestWrapper that instantiates this interfaces as below:
private final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
public Authentication getAuthentication() {
if (!authenticationTrustResolver.isAnonymous(auth)) {
return auth;
}
return null;
}
I looked into documentation and I didn't find a utility class to do it.

Related

Spring Security Custom Auth Manager approach

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

Spring Stomp SimpUserRegistry Anonymous Users

SimpUserRegistry lets you retrieve the details of all authenticated Stomp sessions, is there any such class that will let me iterate over anonymous user sessions?
Like howie described in his answer only non anonymous users will be added to the SimpUserRegistry.
But if you really want to add anonymous users also you just have to sub-class the DefaultHandshakeHandler class and override the determineUser method like stated in the Spring Doc for Version 5.0.0.M1 - Chapter 22. This should also be working for 5.1.5.Release which you are currently on when using Spring Boot 2.1.3.RELEASE:
In some cases it may be useful to assign an identity to a WebSocket session even when the user has not been formally authenticated. For example, a mobile app might assign some identity to anonymous users, perhaps based on geographical location. The do that currently, an application can sub-class DefaultHandshakeHandler and override the determineUser method. The custom handshake handler can then be plugged in (see examples in Section 22.2.4, “Deployment Considerations”).
Here is an answer (Spring websockets without principal) which shows you how you can achieve to create an AnonymousPrincipal and determine it within the custom handshake handler.
And at last you have to add your an instance of your custom handshake handler to your registered endpoint but this is depending on whether you use STOMP or not.
Following are some of the code snippets from StompSubProtocolHandler -
The handleMessageFromClient method adds the user to the stompAuthentications map and publishes a SessionConnectEvent event -
public void handleMessageFromClient(WebSocketSession session, WebSocketMessage<?> webSocketMessage, MessageChannel outputChannel) {
//...
SimpAttributesContextHolder.setAttributesFromMessage(message);
boolean sent = outputChannel.send(message);
if (sent) {
if (isConnect) {
Principal user = headerAccessor.getUser();
if (user != null && user != session.getPrincipal()) {
this.stompAuthentications.put(session.getId(), user);
}else{
//TODO try to handle here for anonymous user
}
}
if (this.eventPublisher != null) {
if (isConnect) {
publishEvent(new SessionConnectEvent(this, message, getUser(session)));
}
//...
I think you have to Check this socure code StompSubProtocolHandler, and customize it.

Spring Security: provide access depending on service call

I have spring boot application which exposes REST endpoints protected by spring security.
I need to restrict access to some paths depending on service call. Let's say I have a service like this:
#Service
public class AccessService {
boolean hasAccess(String requestedPath) {
// some business logic here
}
}
The service will check user roles, some business conditions and return true or false.
Now I need to integrate this service call into my security configuration.
So far I have configuration like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.and().authorizeRequests()
.anyRequest().hasRole("USER");
}
I see no way of adding the service call here (as it is completely static).
What I'm trying:
Currently I'm thinking of overriding my AuthenticationProvider and extending it with the additional functionality.
The other option would be to extend my REST controllers from a class which would do some sort of authorization, but I'm not sure if it is possible.
Question: How can I protect REST endpoints based on service method call? What is the proper way of doing that?
This is explained in the reference guide. Basically you need to use the access expression instead of the hasRole. You can then write powerful security expressions.
Something like the following should do the trick:
anyRequest()
.access("#accessService.hasAccess(request.reques‌​tURI) && hasRole('USER')");
This restricts access to user with the role ROLE_USER and which have access according to your own custom logic.
I think a good way to to this is to use #PreAuthorize
Some documentation can be found here: Expression-Based Access Control.
You are also able to add your own evaluator class/methods to customize to your specific needs:
#PreAuthorize("#customPermissionEvaluator.accessMethod(variable)")
Example class:
#Service(value = "customPermissionEvaluator")
public class CustomPermissionEvaluatorImpl implements CustomPermissionEvaluator {
#Override
public boolean accessMethod(int variable) {
if (variable == 1) {
return true;
}
return false;
}
}

Spring REST Api -- access User details in repository

The setup of the RESPApi project is:
SpringBoot
Spring's OAuth2
In the project we have many clients, so SQL queries almost always have "... and clientId = ?" in the where clause.
We store clientId in the SecurityContext with other user details (we extend Spring's User class).
The question is: how to get the User object in the #Repository?
Possible solutions we can think of:
In every repository implementation add
SecurityContextHolder.getContext().getAuthentication()
cast the result to our custom UserDetails implementation and use it.
Cons: somehow I feel there's a better solution.
Add #AuthenticationPrincipal annotated parameters to the controllers and then pass the parameters to the service layer and then to the repository layer.
Cons: passing the paremeter though 2 layers only to obtain clientId doesn't seem reasonable.
I thought about #Autowired paramter MyUser user in the #Repository class. The first try was to create #Configuration annotated class in which there will be a method
#Bean
public MyUser getUser() {
SecurityContext context = SecurityContextHolder.getContext();
if (context != null) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
return (MyUser) authentication.getPrincipal();
}
}
return null;
}
But the bean is null and I cannot use it.
For now we've ended up with solution nr 1 but I feel there must be a better way.
Any ideas how to solve this problem?
If you're using Spring Data (or have the time to switch to using it), you can use the SecurityEvaluationContextExtension and use principal directly in your queries:
https://stackoverflow.com/a/29692158/1777072
If not, you could hide the static access if it offends (or if you want more control over it changing in future):
#Component
public class AuthenticationHelper {
public Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
Then inject that class into your Repository.
Or your Service. That's probably a better fit than the Repository.
I like to keep Repositories dumb (ultimately using Spring Data to avoid writing them entirely).
And I like to think of Services as being separated out of the web layer, running on separate boxes (even if they aren't). In that situation, you would never pass the Authentication details over HTTP from Controller to Service. The service would obtain authentication details for itself, rather than just trusting what the web layer sent it.
So I think the Service should get the details itself, rather than the Controller passing them through.
Your bean is null because by default beans are singleton and they are created when the application starts, and as you can imagine, you are not going to have a SecurityContext at that point.
Try declaring your bean with request scope, in this way:
#Bean
#Scope(value=WebApplicationContext.SCOPE_REQUEST, proxyMode=ScopedProxyMode.TARGET_CLASS)
public MyUser getUser() {
.....
}

Spring security check if user has access to mentioned url

I have started using spring security, and after a lot of research I am not able to find an answer for:
If I explicitly want to check if user A have access to stuff B. I can check this with JSP tag support Spring Security - check if web url is secure / protected like
<sec:authorize url="stuff/B">
But what if I want to check the same thing in the controller(java class). I am not finding any spring function here to check if a login user has access to mentioned url(https://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html)
Hint from the javadoc:
to use this tag there must also be an instance of WebInvocationPrivilegeEvaluator in your application context. If you are using the namespace, one will automatically be registered. This is an instance of DefaultWebInvocationPrivilegeEvaluator,"
And in the javadoc of DefaultWebInvocationPrivilegeEvaluator, we can see a isAllowed method that should do the job:
// privilegeEvaluator is a WebInvocationPrivilegeEvaluator "autowired"
boolean allowed = privilegeEvaluator.isAllowed("/stuff/B", yourAuthentication);
Why not to use annotations like this:
#PreAuthorize("hasRole('ROLE_USER')")
public void create(Contact contact);
Annotations are standard way for Spring 3+
You are looking at the right place, the link you attached mentions what you need. Since you want access-control on your controller and check per user (not role) you can use the '#PreAuthorize' annotation with "hasPermission" expression or similar.
You could check here for expression-based access control and here for examples of custom security expression example in case you want to customize the solution.
1) First we need to know whether the user may enter the URL at all. This can be very easily achieved using WebInvocationPrivilegeEvaluator.
privilegeEvaluator.isAllowed(contextPath, url, "GET", currentUser);
2) Now we need to identifying whether the user may access the handler method
private boolean isAllowedByAnnotation(Authentication currentUser, HandlerMethod method) {
PreInvocationAuthorizationAdvice advice = new ExpressionBasedPreInvocationAdvice();
PreInvocationAuthorizationAdviceVoter voter = new PreInvocationAuthorizationAdviceVoter(advice);
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PrePostInvocationAttributeFactory factory = new ExpressionBasedAnnotationAttributeFactory(expressionHandler);
PrePostAnnotationSecurityMetadataSource metadataSource = new PrePostAnnotationSecurityMetadataSource(factory);
Class<?> controller = method.getBeanType();
MethodInvocation mi = MethodInvocationUtils.createFromClass(controller, method.getMethod().getName());
Collection<ConfigAttribute> attributes = metadataSource.getAttributes(method.getMethod(), controller);
return PreInvocationAuthorizationAdviceVoter.ACCESS_GRANTED == voter.vote(currentUser, mi, attributes);
}
We can create a custom PermissionEvaluator and use
hasPermission(Authentication authentication, Object domainObject,
Object permission).
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
final DefaultMethodSecurityExpressionHandler expressionHandler =
new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
return expressionHandler;
}
#Bean
public aclServiceImpl aclService() {
final AclServiceImpl mutableAclService = new AclServiceImpl
(authorizationStrategy(), grantingStrategy());
return mutableAclService;
}
AclServiceImpl is the implementation of MutableAclService
The most obviously useful annotation is #PreAuthorize which decides whether a method can actually be invoked or not. For example (from the"Contacts" sample application)
#PreAuthorize("hasRole('USER')")
public void create(Contact contact);
which means that access will only be allowed for users with the role "ROLE_USER". Obviously the same thing could easily be achieved using a traditional configuration and a simple configuration attribute for the required role. But what about:
#PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);
Here we’re actually using a method argument as part of the expression to decide whether the current user has the "admin"permission for the given contact. The built-in hasPermission() expression is linked into the Spring Security ACL module through the application context.
For More Detailed Explanation please refer this Link

Categories