I have a method secured with @PreAuthorize
@PreAuthorize("hasRole('ROLE_ADMIN') and (#action.userId != principal.id)")
public void execute(EditAction action)
Now I need to call this method from a background task. If I simply run this code - I catch an exception:
AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
Seems, I need to set required Authentication to SecurityContext. I can:
What is the right way?
Spring Security provides method level security using @PreAuthorize and @PostAuthorize annotations. This is expression-based access control. The @PreAuthorize can check for authorization before entering into method. The @PreAuthorize authorizes on the basis of role or the argument which is passed to the method.
Method-level security is implemented by placing the @PreAuthorize annotation on controller methods (actually one of a set of annotations available, but the most commonly used). This annotation contains a Spring Expression Language (SpEL) snippet that is assessed to determine if the request should be authenticated.
The @PreAuthorize annotation checks the given expression before entering the method, whereas the @PostAuthorize annotation verifies it after the execution of the method and could alter the result.
The difference between @Secured and @PreAuthorize are as follows : The main difference between @Secured and @PreAuthorize is that @PreAuthorize can work with Spring EL. We can access methods and properties of SecurityExpressionRoot while using @PreAuthorize but not with @Secured.
In this case Manual Workaround is an option:
(1) If this is an independent job,
Create an Authentication object and set it to the security context before the secured method is invoked. Remove the Authentication object from the security context after the secured method is done execution.
public final class AuthenticationUtil {
//Ensures that this class cannot be instantiated
private AuthenticationUtil() {
}
public static void clearAuthentication() {
SecurityContextHolder.getContext().setAuthentication(null);
}
public static void configureAuthentication(String role) {
Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(role);
Authentication authentication = new UsernamePasswordAuthenticationToken(
"user",
role,
authorities
);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
So it would look like
AuthenticationUtil.configureAuthentication(role);
// Call to the secured method
AuthenticationUtil.clearAuthentication();
(2) For web application, where we can't make authentication object as null, don't call
AuthenticationUtil.configureAuthentication(role);
// call to the secured method
You can register an Authentication token yourself in the current thread and session(if used in a web application):
SecurityContextHolder.getContext().setAuthentication(token);
session.put(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
You can use the standard UsernamePasswordAuthenticationToken for this as long as you add the appropriate roles.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With