3

I have read spring security docs and learned that I can use the following annotation to check if the subject had access to edit user.

@PreAuthorize("hasPermission('USER_EDIT')")
public String editUSer(User user);

What I would like to do is to write my custom annotation MyAutorizationCheck and use it like below

@MyAuthorizationCheck(Application.USER_MANAGEMENT, AccessLevel.EDIT)
public String editUSer(User user);

Where Application and AccessLevel are enum.

enum Application{
    USER_MANAGEMENT, ORDER_MANAGEMENT
}

enum AccessLevel{
    READ, CREATE, UPDATE, DELETE
}

Handler for this annotation should be able to decide if the user has permission or not.

Any pointers how to achieve this?

Thank you.

Murali
  • 3,412
  • 5
  • 36
  • 34

2 Answers2

1

Spring security use PrePostAnnotationSecurityMetadataSource to find the @PreAuthorize and convert the Spring-EL expression to ConfigAttribute.

You can implement your MyAuthorizationCheckAnnotationSecurityMetadataSource and override getAttributes method to convert your enums to ConfigAttribute too;

write your code like this:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAuthorizationCheck {
    Application app();
    AccessLevel level();
}
public class MyAuthorizationCheckAnnotationSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {

    private final PrePostInvocationAttributeFactory attributeFactory;

    public MyAuthorizationCheckAnnotationSecurityMetadataSource(PrePostInvocationAttributeFactory attributeFactory) {
        this.attributeFactory = attributeFactory;
    }

        @Override
    public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
        if (method.getDeclaringClass() == Object.class) {
            return Collections.emptyList();
        }
        this.logger.trace(LogMessage.format("Looking for FddApi annotations for method '%s' on target class '%s'",
                method.getName(), targetClass));
        MyAuthorizationCheck myAuthorization = findAnnotation(method, targetClass, MyAuthorizationCheck.class);
        if (myAuthorization == null) {
            this.logger.trace("No expression annotations found");
            return Collections.emptyList();
        }
        Application app = myAuthorization.app();
        AccessLevel level = myAuthorization.level();
        // build the Spring-EL expression from enums
        String expr = "hasPermission('" + app.name() + "_" + level.name() + "')";
        PreInvocationAttribute pre = this.attributeFactory.createPreInvocationAttribute(null, null, expr);
        return CollUtil.newArrayList(pre);
    }
    // other method can copy from PrePostAnnotationSecurityMetadataSource
    ...

}

then registe the MyAuthorizationCheckAnnotationSecurityMetadataSource.

our code need PreInvocationAuthorizationAdviceVoter to check permission, so need enable the prePostEnabled

@EnableGlobalMethodSecurity(prePostEnabled = true)
public class CustomSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Override
    protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
        ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(
                getExpressionHandler());
        return new MyAuthorizationCheckAnnotationSecurityMetadataSource(attributeFactory);
    }
}

Finally you can use the @MyAuthorizationCheck like this:

@MyAuthorizationCheck(app = Application.USER_MANAGEMENT, level = AccessLevel.EDIT)
public String editUSer(User user);
wyulong
  • 11
  • 2
0

It is not a direct response to your question.

As a workoround you can continue to use built-in annotations:

@PreAuthorize("hasPermission('USER_MANAGEMENT_READ')")
@PreAuthorize("hasPermission('USER_MANAGEMENT_CREATE')")
@PreAuthorize("hasPermission('USER_MANAGEMENT_UPDATE')")
@PreAuthorize("hasPermission('USER_MANAGEMENT_DELETE')")

@PreAuthorize("hasPermission('ORDER_MANAGEMENT_READ')")
@PreAuthorize("hasPermission('ORDER_MANAGEMENT_CREATE')")
@PreAuthorize("hasPermission('ORDER_MANAGEMENT_UPDATE')")
@PreAuthorize("hasPermission('ORDER_MANAGEMENT_DELETE')")
Maksym Demidas
  • 7,707
  • 1
  • 29
  • 36