我正在使用spring的PreAuthorize注释,如下所示:
@PreAuthorize("hasRole('role')");
但是,我已经在另一个类上将'role'定义为静态String。如果我尝试使用此值:
@PreAuthorize("hasRole(OtherClass.ROLE)");
我收到一个错误:
org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 14): Field or property 'OtherClass' cannot be found on object of type 'org.springframework.security.access.expression.method.MethodSecurityExpressionRoot'
是否可以通过PreAuthorize注释访问像这样的静态变量?
尝试以下使用Spring Expression Language评估类型的方法:
@PreAuthorize("hasRole(T(fully.qualified.OtherClass).ROLE)");
请确保指定完全限定的类名称。
为了可以编写没有包名称的表达式:
<sec:global-method-security>
<sec:expression-handler ref="methodSecurityExpressionHandler"/>
</sec:global-method-security>
<bean id="methodSecurityExpressionHandler" class="my.example.DefaultMethodSecurityExpressionHandler"/>
然后扩展DefaultMethodSecurityExpressionHandler:
public class DefaultMethodSecurityExpressionHandler extends org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler {
@Override
public StandardEvaluationContext createEvaluationContextInternal(final Authentication auth, final MethodInvocation mi) {
StandardEvaluationContext standardEvaluationContext = super.createEvaluationContextInternal(auth, mi);
((StandardTypeLocator) standardEvaluationContext.getTypeLocator()).registerImport("my.example");
return standardEvaluationContext;
}
}
现在创建my.example.Roles.java:
public class Roles {
public static final String ROLE_UNAUTHENTICATED = "ROLE_UNAUTHENTICATED";
public static final String ROLE_AUTHENTICATED = "ROLE_AUTHENTICATED";
}
并且在批注中没有包名称的情况下引用它:
@PreAuthorize("hasRole(T(Roles).ROLE_AUTHENTICATED)")
而不是:
@PreAuthorize("hasRole(T(my.example.Roles).ROLE_AUTHENTICATED)")
使它更具可读性,恕我直言。现在还可以输入角色。写:
@PreAuthorize("hasRole(T(Roles).ROLE_AUTHENTICATEDDDD)")
如果您写了,您将收到原本不会出现的启动错误:
@PreAuthorize("hasRole('ROLE_AUTHENTICATEDDDD')")
凯文·鲍沃索克斯(Kevin Bowersox)接受的答案起作用了,但是我不喜欢T(fully.qualified.path),所以我一直在寻找。我首先使用James Watkins的答案在此处创建自定义安全方法:
How to create custom methods for use in spring security expression language annotations
但是,我使用我的enums.Permissions类作为参数类型:
@Component
public class MySecurityService {
public boolean hasPermission(enums.Permissions permission) {
...do some work here...
return true;
}
}
现在,最简洁的部分是,当我从注释中调用hasPermission时,我不必键入整个路径,但是我必须将其用单引号引起来:
@PreAuthorize("@mySecurityService.hasPermission('SOME_ROLE_NAME')")
因为hasPermission方法需要一个Enum,它将自动找到具有该名称的Enum值。如果找不到,则会出现异常:
org.springframework.expression.spel.SpelEvaluationException: Type conversion problem, cannot convert from java.lang.String to enums.Permissions
您可以将hasPermission重命名为hasRole,在这种情况下唯一的权衡是,您要用T(fully.qualified.path)交换@mySecurityService和额外的单引号。
不确定是否有更好的方法,但是有。由于无论如何这些都不会在编译时验证值,因此我的下一步是制作注释处理器。
我还必须感谢krosenvold指出spring可以自动转换为枚举:https://stackoverflow.com/a/516899/618881
尝试这样的事情:
@PreAuthorize("hasRole(T(com.company.enumpackage.OtherClass).ROLE.name())");
如果您的OtherClass枚举被声明为公共静态,则需要使用$符号:
@PreAuthorize("hasRole(T(com.company.ParentTopLevelClass$OtherClass).ROLE.name())");
[name()
以防止在以后再次覆盖toString()
时出现更多问题,]
您还可以创建具有角色的bean容器,例如: