Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Security Expression

I'm studying tutorial how to create custom security expression and I created threes classes but I got error, I tried google everything, may be I am not updated or some. Can you explain what's going on?

Error:

    Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: Failed to evaluate expression 'isComprador()'] with root cause

    Method call: Method isComprador() cannot be found on type org.springframework.security.access.expression.method.MethodSecurityExpressionRoot

MethodSecurityConfig:

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

CustomMethodSecurityExpressionHandler:

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
    private final 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;
    }
}

CustomMethodSecurityExpressionRoot:

public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

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

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

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

    @Override
    public Object getFilterObject() {
        return filterObject;
    }

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

    @Override
    public Object getReturnObject() {
        return returnObject;
    }

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

    @Override
    public Object getThis() {
        return target;
    }

    //

    public boolean isComprador() {
        final Usuario usuario = ((UserDetailsImpl) this.getPrincipal()).getUsuario();
        return usuario.getPerfil() == Perfil.COMPRADOR;
    }

    public boolean isVendedor() {
        final Usuario usuario = ((UserDetailsImpl) this.getPrincipal()).getUsuario();
        return usuario.getPerfil() == Perfil.VENDEDOR;
    }
}

Thanks!

Att, Carlos Oliveira

like image 995
Carlos Oliveira Avatar asked Nov 28 '25 11:11

Carlos Oliveira


1 Answers

I'd really recommend using a custom bean rather than trying to integrate into the expression root. This is much easier to configure, decouples your code from Spring Security you just create a simple pojo, and allows your code to be more focused.

To use this approach start by creating a Spring Bean:

@Component
public class Authz {

    public boolean isComprador() {
        // Authentication is the currently logged in user
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication != null && "comprador".equals(authentication.getName());
    }
}

Then you can refer to methods in the Bean using @beanName.methodName. In our case, the Bean name is authz and our method is isComprador so the following would work:

@Service
public class MessageService {
    // we pass in the name argument into our custom expression Authz.isComprador
    @PreAuthorize("@authz.isComprador()")
    String greetForName(String name) {
        return "Hello " + name;
    }
}

Finally we just enable method security like normal:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
}

You can then write a few unit tests to prove that it works:

@SpringBootTest
class DemoApplicationTests {
    @Autowired
    MessageService service;

    @Test
    @WithMockUser // run the test as a user with the default username of user
    void secureWhenForbidden() {
        assertThatCode(() -> service.greetForName("Rob")).isInstanceOf(AccessDeniedException.class);
    }

    @Test
    @WithMockUser("comprador") // run the test as a user with the username of comprador
    void secureWhenGranted() {
        assertThatCode(() -> service.greetForName("Rob")).doesNotThrowAnyException();;
    }
}

You can find a complete sample at https://github.com/rwinch/spring-security-sample/tree/method-security-bean-expression

like image 142
Rob Winch Avatar answered Nov 30 '25 01:11

Rob Winch