use*_*547 11 java spring authorization spring-security spring-annotations
我的问题是带有弹簧安全性的自定义注释的副本,但它没有得到答复,我相信应该有一个简单的问题解决方案.
基本上不是做:
@PreAuthorize("hasPermission(T(fully.qualified.Someclass).WHATEVER, T(fully.qualified.Permission).READ")
Run Code Online (Sandbox Code Playgroud)
我想要做:
@PreAuthorize(Someclass.WHATEVER, Permission.READ)
Run Code Online (Sandbox Code Playgroud)
或者可能是一些自定义注释,可以很容易地与弹簧安全连接
这对我来说似乎更清洁,如果可以,我希望能够做到这一点.
Gon*_*ndy 10
您可以像这样创建静态注释:
@ReadPermission
Run Code Online (Sandbox Code Playgroud)
通过将@PreAuthorize注释移动到@ReadPermission定义:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole(T(fully.qualified.Permission).READ.roleName())")
public @interface ReadPermission {
}
Run Code Online (Sandbox Code Playgroud)
这样做的好处是,您可以在一个地方更改 Spring SPEL 表达式,而不是在每个方法上修改它。
另一个优点是,您可以在类级别使用此注释——然后每个方法都将使用此注释进行保护。它对 AdminControllers 等很有用。
面对同样的问题,我最终得到了一个混合解决方案.我使用Spring-El和自定义bean来提供我自己的hasPermission()接受Enum 的方法.鉴于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;
}
}
Run Code Online (Sandbox Code Playgroud)
使用如下:
@PreAuthorize("@securityService.hasPermission({'USER_ADD'})")
public User addUser(User user){
// create the user
return userRepository.save( user );
}
Run Code Online (Sandbox Code Playgroud)
权限只是一个正常的枚举定义:
public enum Permission {
USER_LIST,
USER_EDIT,
USER_ADD,
USER_ROLE_EDIT
}
Run Code Online (Sandbox Code Playgroud)
希望这可以在将来帮助其他人.
小智 5
确实,您可以实现自定义的强类型安全注释,尽管这很麻烦。声明您的注释
enum Permission {
USER_LIST,
USER_EDIT,
USER_ADD,
USER_ROLE_EDIT
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Permissions {
Permission[] value();
}
Run Code Online (Sandbox Code Playgroud)
声明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(","));
}
}
Run Code Online (Sandbox Code Playgroud)
声明的自定义实现 org.springframework.security.access.method.MethodSecurityMetadataSource以创建SecurityAttributefrom注释的实例
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;
}
}
Run Code Online (Sandbox Code Playgroud)
最后声明自定义实现 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
}
}
Run Code Online (Sandbox Code Playgroud)
现在将它们放在一起 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()));
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8300 次 |
| 最近记录: |