'Custom security expression root not working on docker

I have problem with working with custom spring security expressions in Docker.

I have functionality for using custom expressions in @PreAuthorize functions.

@PreAuthorize("hasAuthority('cashmanagement_reports') AND isAccountsBelongsToCustomerAsMonitoringType(#filter)")

Also I have MethodSecurityConfig

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        LOG.info("Started creating expression handler for security");
        CustomMethodSecurityExpressionHandler expressionHandler =
                new CustomMethodSecurityExpressionHandler();
        expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
        expressionHandler.setApplicationContext(applicationContext);
        return expressionHandler;
    }

}

and CustomMethodSecurityExpressionHandler

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
private ApplicationContext applicationContext;

public CustomMethodSecurityExpressionHandler() {
    super();
}

@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
                                                                          MethodInvocation invocation) {
    LOG.info("Started creating expression root for security");
    CustomMethodSecurityExpressionRoot root =
            new CustomMethodSecurityExpressionRoot(authentication);
    root.setPermissionEvaluator(getPermissionEvaluator());
    root.setTrustResolver(this.trustResolver);
    root.setRoleHierarchy(getRoleHierarchy());
    return root;
}

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

And CustomMethodSecurityExpressionRoot class

   public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    @Getter
    private Object filterObject;

    @Getter
    private Object returnObject;

    @Getter
    @Setter
    private Object target;
    @Setter
    private IUserUtils userUtils;
    @Setter
    private MonitoringRequestDao monitoringRequestDao;
    @Setter
    private ClassifierDao classifierDao;
    @Setter
    private MonitoringAccountDao monitoringAccountDao;
    @Setter
    private TransferRequestDao transferRequestDao;

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

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

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

    /**
     * Sets the "this" property for use in expressions. Typically this will be the "this"
     * property of the {@code JoinPoint} representing the method invocation which is being
     * protected.
     *
     * @param target the target object on which the method in is being invoked.
     */
    void setThis(Object target) {
        this.target = target;
    }

    public Object getThis() {
        return target;
    }

    /**
     * Returns true if user is belongs to {@link MonitoringRequest} as child organization
     *
     * @param monitoringRequestId {@link MonitoringRequest#getId()}
     * @return true if user is the member of {@link Customer}, else false
     */
    public boolean isAccountsBelongsToCustomerAsMonitoringType(ReportFilter) {
.....
    }

All is working OK, if I run using IntellijIDEA. But when I run service using docker image, I am getting this error.

    Failed to evaluate expression 'hasAuthority('cashmanagement_reports') AND isAccountsBelongsToCustomerAsMonitoringType(#filter)'

java.lang.IllegalArgumentException: Failed to evaluate expression 'hasAuthority('cashmanagement_reports') AND isAccountsBelongsToCustomerAsMonitoringType(#filter)'
.............................
..............................
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method isAccountsBelongsToCustomerAsMonitoringType(com.infin.it.ibank.dto.ReportFilter) cannot be found on org.springframework.security.access.expression.method.MethodSecurityExpressionRoot type


Solution 1:[1]

Adding proxyTargetClass to EnableGlobalMethodSecurity annotation helped me.

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        CustomMethodSecurityExpressionHandler expressionHandler =
                new CustomMethodSecurityExpressionHandler();
        expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
        expressionHandler.setApplicationContext(applicationContext);
        return expressionHandler;
    }

}

UPDATE

The problem reappears. I decided to remove custom expressions.

Solution 2:[2]

I had the same issue while upgrading to Spring Boot 2.6 (from 2.3) on a project using OAuth2.

It appears that recent versions of Spring Boot Security OAuth2 declare a MethodSecurityExpressionHandler bean (in OAuth2MethodSecurityExpressionHandlerConfiguration) if there isn’t already one. This bean gets injected into GlobalMethodSecurityConfiguration.setMethodSecurityExpressionHandler(), which prevents createExpressionHandler() from being called.

To fix this, createExpressionHandler() should be annotated with @Bean, and you don’t need to extend GlobalMethodSecurityConfiguration any more:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig {

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        CustomMethodSecurityExpressionHandler expressionHandler =
                new CustomMethodSecurityExpressionHandler();
        expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
        expressionHandler.setApplicationContext(applicationContext);
        return expressionHandler;
    }

}

This, of course, disables the OAuth2MethodSecurityExpressionHandler, so you won’t be able to use expressions such as #oauth2.clientHasRole('ROLE_ADMIN'). I suppose it should be possible to combine the two but I didn’t need it.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Didier L