user2141969 user2141969 - 3 months ago 83
Java Question

Spring 4 - Custom SecurityExpression with Service

In one of my controllers, I have the need to secure a method with more granular means than a role.

I've got a

MethodSecurityExpressionHandler
created, but I can't figure out how to access one of my
@Services
in it.

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = false)
public class CustomMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
@Autowired
ApplicationContext applicationContext;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
CustomMethodSecurityExpressionHandler handler = new CustomMethodSecurityExpressionHandler();
handler.setApplicationContext(applicationContext);
return super.createExpressionHandler();
}
@Bean
public MethodSecurityExpressionHandler expressionHandler() {
return new CustomMethodSecurityExpressionHandler();
}
}

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();

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

return root;
}
}


public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

private Object filterObject;
private Object returnObject;
private Object target;

//**This is what I need to work**
@Autowired
private RepositoryService repositoryService;

public boolean canViewFolder(String uuid){
User currentUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return repositoryService.checkFolderPermissions(currentUser.getUsername(), uuid);
}

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

public void setFilterObject(Object filterObject) {
this.filterObject = filterObject;
}

public Object getFilterObject() {
return filterObject;
}

public void setReturnObject(Object returnObject) {
this.returnObject = returnObject;
}

public Object getReturnObject() {
return returnObject;
}

void setThis(Object target) {
this.target = target;
}

public Object getThis() {
return target;
}

}

Answer

Create a setter for your RepositoryService in CustomMethodSecurityExpressionRoot

Override setApplicationContext in CustomMethodSecurityExpressionHandler to have the applicationContext you pass in at an accessible level.

In createSecurityExpressionRoot using the applicationContext pass in the RepositoryService bean from your context. Code is below. Also note the changes I made to createExpressionHandler() and expressionHandler(). In create ExpressionHandler you are calling the super which just uses a default implementation, not the object you just newed two lines earlier. In expressionHandler() you are creating a new instance of CustomMethodSecurityExpressionHandler() not retrieving the one you are creating in createExpressionHandler().

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = false)
    public class CustomMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
    @Autowired
    ApplicationContext applicationContext;
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        CustomMethodSecurityExpressionHandler handler = new  CustomMethodSecurityExpressionHandler();
        handler.setApplicationContext(applicationContext);
        return handler;
    }
    @Bean
    public MethodSecurityExpressionHandler expressionHandler() {
        return createExpressionHandler();
    }
}

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

    private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
    private ApplicationContext applicationContext;
    @Override
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
                                                                              MethodInvocation invocation) {
        final CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
        root.setThis(invocation.getThis());
        root.setPermissionEvaluator(getPermissionEvaluator());
        root.setTrustResolver(this.trustResolver);
        root.setRoleHierarchy(getRoleHierarchy());
        root.setRepositoryService(applicationContext.getBean(RepositoryService.class);
        return root;
    }

    @Override
    protected void setApplicationContext(applicationContext){
        super.setApplicationContext(applicationContext);
        this.applicationContext = applicationContext;
    }
}


public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    private Object filterObject;
    private Object returnObject;
    private Object target;
    private RepositoryService repositoryService;

    public boolean canViewFolder(String uuid){
        User currentUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
         return repositoryService.checkFolderPermissions(currentUser.getUsername(), uuid);
    }

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

    public void setFilterObject(Object filterObject) {
        this.filterObject = filterObject;
    }

    public Object getFilterObject() {
        return filterObject;
    }

    public void setReturnObject(Object returnObject) {
        this.returnObject = returnObject;
    }

    public Object getReturnObject() {
        return returnObject;
    }

    void setThis(Object target) {
        this.target = target;
    }

    public Object getThis() {
        return target;
    }

    public void setRepositoryService(RepositoryService repositoryService){
        this.repositoryService = repositoryService;
    }
}