Spring Security @PreAuthorization pass enums in directly

前端 未结 4 1563
清歌不尽
清歌不尽 2021-02-07 03:29

My question is a duplicate of Custom annotation with spring security but it went unanswered and I believe there should be a simple solution to the problem.

Basically ins

4条回答
  •  春和景丽
    2021-02-07 04:03

    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

    class SecurityAttribute implements ConfigAttribute {
        private final List permissions;
    
        public SecurityAttribute(List 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

    class SecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
        @Override
        public Collection 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 getAllConfigAttributes() {
            return null;
        } 
    
    }
    

    At last declare the custom implementation org.springframework.security.access.AccessDecisionVoter

    public class PermissionVoter implements AccessDecisionVoter {
        @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 attributes) {
            Optional 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

    @Configuration
    @EnableGlobalMethodSecurity
    class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    
        @Override
        protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
            return new ScpSecurityMetadataSource();
        }
    
        @Override
        protected AccessDecisionManager accessDecisionManager() {
            return new AffirmativeBased(Collections.singletonList(new PermissionVoter()));
        }
    }
    

提交回复
热议问题