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
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
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