Java Spring Security @PreAuthorization 直接传入枚举
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19303584/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Security @PreAuthorization pass enums in directly
提问by user1751547
My question is a duplicate of Custom annotation with spring securitybut it went unanswered and I believe there should be a simple solution to the problem.
我的问题是带有 spring 安全性的自定义注释的副本,但没有得到解答,我相信应该有一个简单的解决方案。
Basically instead of doing:
基本上而不是这样做:
@PreAuthorize("hasPermission(T(fully.qualified.Someclass).WHATEVER, T(fully.qualified.Permission).READ")
I would like to do:
我想要做:
@PreAuthorize(Someclass.WHATEVER, Permission.READ)
or possibly some custom annotation that will wire up easily with spring security
或者可能是一些自定义注释,可以很容易地与弹簧安全连接
This seems much cleaner to me and I would like to be able to do it if I can.
这对我来说似乎更清洁,如果可以的话,我希望能够做到。
回答by Eric B.
Facing the same issue, I ended up with a hybrid solution. I am using Spring-El and a custom bean to provide my own hasPermission()
method which accepts an Enum. Given that Spring does an automatic string->enum
conversion, at runtime, I will get a runtime exception that a particular enum does not exist if there is a typo in the string. Not the ideal solution (would have rather had something that failed at compile-time), but an acceptable compromise. It gives me some semi-type safety.
面对同样的问题,我最终得到了一个混合解决方案。我正在使用 Spring-El 和自定义 bean 来提供我自己的hasPermission()
接受枚举的方法。鉴于 Spring 进行了自动string->enum
转换,在运行时,如果字符串中有拼写错误,我将收到一个运行时异常,即特定枚举不存在。不是理想的解决方案(宁愿有一些在编译时失败的东西),而是一个可以接受的妥协。它给了我一些半类型的安全性。
@Component("securityService")
public class SecurityService {
public boolean hasPermission( Permission...permissions){
// loop over each submitted role and validate the user has at least one
Collection<? extends GrantedAuthority> userAuthorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
for( Permission permission : permissions){
if( userAuthorities.contains( new SimpleGrantedAuthority(permission.name())))
return true;
}
// no matching role found
return false;
}
}
Used as follows:
用法如下:
@PreAuthorize("@securityService.hasPermission({'USER_ADD'})")
public User addUser(User user){
// create the user
return userRepository.save( user );
}
Where Permission is just a normal enum definition:
其中 Permission 只是一个普通的枚举定义:
public enum Permission {
USER_LIST,
USER_EDIT,
USER_ADD,
USER_ROLE_EDIT
}
Hope this can help someone else out in the future.
希望这可以在将来帮助其他人。
回答by e.g78
I did that way :
我是这样做的:
1 - Define your enum referencing a public final static String "VALUE" like this
1 - 定义您的枚举引用公共最终静态字符串“VALUE”,如下所示
public enum MyEnum {
ENUM_A(Names.ENUM_A);
private String value;
private MyEnum (String value) {
this.value = value;
}
public static class Names {
public final static String ENUM_A = "ENUM_A";
}
}
2 - Concat MyEnum values in @PreAuthorize
2 - @PreAuthorize 中的 Concat MyEnum 值
@PreAuthorize("hasPermission('myDomain', '"+ MyEnum.Names.ENUM_A+"')")
回答by Dima Korn
Indeed you can implement a custom strongly typed security annotation, though this is rather bothersome. Declare your annotation
实际上,您可以实现自定义的强类型安全注释,尽管这很麻烦。声明您的注释
enum Permission {
USER_LIST,
USER_EDIT,
USER_ADD,
USER_ROLE_EDIT
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Permissions {
Permission[] value();
}
Declare the custom implementation of org.springframework.security.access.ConfigAttribute
to be used by security pipeline
声明org.springframework.security.access.ConfigAttribute
要由安全管道使用的自定义实现
class SecurityAttribute implements ConfigAttribute {
private final List<Permission> permissions;
public SecurityAttribute(List<Permission> permissions) {
this.permissions = permissions;
}
@Override
public String getAttribute() {
return permissions.stream().map(p -> p.name()).collect(Collectors.joining(","));
}
}
Declare the custom implementation of org.springframework.security.access.method.MethodSecurityMetadataSource
to create the instances of SecurityAttribute
from annotations
声明自定义实现 org.springframework.security.access.method.MethodSecurityMetadataSource
以创建SecurityAttribute
来自注解的实例
class SecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
@Override
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
//consult https://github.com/spring-projects/spring-security/blob/master/core/src/main/java/org/springframework/security/access/prepost/PrePostAnnotationSecurityMetadataSource.java
//to implement findAnnotation
Permissions annotation = findAnnotation(method, targetClass, Permissions.class);
if (annotation != null) {
return Collections.singletonList(new SecurityAttribute(asList(annotation.value())));
}
return Collections.emptyList();
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
}
At last declare the custom implementation org.springframework.security.access.AccessDecisionVoter
最后声明自定义实现 org.springframework.security.access.AccessDecisionVoter
public class PermissionVoter implements AccessDecisionVoter<MethodInvocation> {
@Override
public boolean supports(ConfigAttribute attribute) {
return attribute instanceof SecurityAttribute;
}
@Override
public boolean supports(Class<?> clazz) {
return MethodInvocation.class.isAssignableFrom(clazz);
}
@Override
public int vote(Authentication authentication, MethodInvocation object, Collection<ConfigAttribute> attributes) {
Optional<SecurityAttribute> securityAttribute = attributes.stream()
.filter(attr -> attr instanceof SecurityAttribute).map(SecurityAttribute.class::cast).findFirst();
if(!securityAttribute.isPresent()){
return AccessDecisionVoter.ACCESS_ABSTAIN;
}
//authorize your principal from authentication object
//against permissions and return ACCESS_GRANTED or ACCESS_DENIED
}
}
and now bring them all together in your MethodSecurityConfig
现在把它们放在你的 MethodSecurityConfig
@Configuration
@EnableGlobalMethodSecurity
class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
return new ScpSecurityMetadataSource();
}
@Override
protected AccessDecisionManager accessDecisionManager() {
return new AffirmativeBased(Collections.singletonList(new PermissionVoter()));
}
}
回答by Gondy
You can create static annotations like this:
您可以像这样创建静态注释:
@ReadPermission
By moving @PreAuthorize
annotation to @ReadPermission
definition:
通过将@PreAuthorize
注释移动到@ReadPermission
定义:
@Inherited @PreAuthorize("hasRole(T(fully.qualified.Permission).READ.roleName())") public @interface ReadPermission { }
Benefit of this is, that you can then change Spring SPEL expression in one place, instead of modifying it on every method. One more plus is, that you can use this annotation on Class level - every method then would be secured with this annotation. It's useful for AdminControllers etc..
这样做的好处是,您可以在一个地方更改 Spring SPEL 表达式,而不是在每个方法上修改它。另一个优点是,您可以在类级别使用此注释——然后每个方法都将使用此注释进行保护。它对 AdminControllers 等很有用。