Mateusz Maciejko
Mateusz Maciejko

Reputation: 29

Spring security pass SecurityExpressionRoot to custom method

I want to pass SecurityExpressionRoot class, which i can access inside @PreAuthorise() annotation, to my custom checkAccess() method which checks access to specific method, using some logic based on authorities, roles and a additional variables that i pass to this method. Inside @PreAuthorise() I can access methods from SecurityExpressionRoot, for example. hasAuthority()

Is there any way to do that?

Controller:

public class TestController {

    private final PreAuthorizeChecker preAuthorizeChecker;

    @Autowired
    public TestController(PreAuthorizeChecker preAuthorizeChecker) {
        this.preAuthorizeChecker = preAuthorizeChecker;
    }

    @GetMapping(path = "/test")
    @PreAuthorize("@preAuthorizeChecker.checkAccess(/*SecurityExpressionRoot.getSomehow()*/)")    //How to obtain SecurityExpressionRoot instance?
    public ResponseEntity<Void> get() {
        return;
    }

PreAuthorizeChecker:

@Component
public class PreAuthorizeChecker {

    @Autowired
    public PreAuthorizeChecker() {
    }

    public boolean checkAccess(SecurityExpressionRoot securityExpressionRoot) {
        //do sth with securityExpressionRoot
        return true;
    }
}

Upvotes: 0

Views: 2780

Answers (1)

Rich Tillis
Rich Tillis

Reputation: 1571

You might find that part 5 of this blog, A Custom Security Expression with Spring Security, on Baelding.com helpful. The author suggests extending SecurityExpressionRoot and adding your custom method to the new class like this:

public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

       public CustomMethodSecurityExpressionRoot(Authentication authentication) {
            super(authentication);
       }

       public boolean checkAccess() {
            //do sth with securityExpressionRoot
            return true;
       }

...
}

Then you will need to inject that new class into the expression handler like this:

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

      private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();

      @Override
      protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
           CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
           root.setPermissionEvaluator(getPermissionEvaluator());
           root.setTrustResolver(this.trustResolver);
           root.setRoleHierarchy(getRoleHierarchy());
           return root;
      }
}

Finally, you just need to create the expression handler in your security config like this:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        CustomMethodSecurityExpressionHandler expressionHandler = new CustomMethodSecurityExpressionHandler();
        return expressionHandler;
    }
}

Then your new expression should be available:

  @PreAuthorize("checkAccess()")
  public ResponseEntity<Void> get() {
      return;
  }

Upvotes: 1

Related Questions