英文:
Custom Security Expression
问题
我正在学习如何创建自定义安全表达式的教程,我创建了三个类,但是我遇到了错误,我尝试了谷歌上的一切,可能是我没有更新或其他原因。你能解释一下发生了什么吗?
错误信息:
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
方法调用:在类型 org.springframework.security.access.expression.method.MethodSecurityExpressionRoot 上找不到方法 isComprador()
MethodSecurityConfig 类:
```java
@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;
}
}
谢谢!
祝好,
Carlos Oliveira
<details>
<summary>英文:</summary>
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:
```java
@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
答案1
得分: 4
我真的强烈推荐使用自定义的 Bean,而不是尝试集成到表达式根中。这样做更容易配置,将您的代码与 Spring Security 解耦,您只需要创建一个简单的 POJO,这使得您的代码更加专注。
使用这种方法,首先创建一个 Spring Bean:
@Component
public class Authz {
public boolean isComprador() {
// Authentication 是当前登录的用户
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null && "comprador".equals(authentication.getName());
}
}
然后您可以使用 @beanName.methodName
来引用 Bean 中的方法。在我们的例子中,Bean 的名称是 authz
,方法名是 isComprador
,所以以下内容有效:
@Service
public class MessageService {
// 我们将 name 参数传递给我们的自定义表达式 Authz.isComprador
@PreAuthorize("@authz.isComprador()")
String greetForName(String name) {
return "Hello " + name;
}
}
最后,像正常情况下一样启用方法安全性:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
}
然后,您可以编写一些单元测试来验证其是否正常工作:
@SpringBootTest
class DemoApplicationTests {
@Autowired
MessageService service;
@Test
@WithMockUser // 以默认用户名 user 运行测试
void secureWhenForbidden() {
assertThatCode(() -> service.greetForName("Rob")).isInstanceOf(AccessDeniedException.class);
}
@Test
@WithMockUser("comprador") // 以用户名 comprador 运行测试
void secureWhenGranted() {
assertThatCode(() -> service.greetForName("Rob")).doesNotThrowAnyException();
}
}
您可以在 https://github.com/rwinch/spring-security-sample/tree/method-security-bean-expression 找到一个完整的示例。
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论