Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security @PreAuthorization pass enums in directly

Tags:

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 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.

like image 348
user1751547 Avatar asked Oct 10 '13 18:10

user1751547


1 Answers

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<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

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

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

@Configuration @EnableGlobalMethodSecurity class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {      @Override     protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {         return new ScpSecurityMetadataSource();     }      @Override     protected AccessDecisionManager accessDecisionManager() {         return new AffirmativeBased(Collections.singletonList(new PermissionVoter()));     } } 
like image 116
Dima Korn Avatar answered Sep 21 '22 13:09

Dima Korn