Using static variables in Spring annotations

16,880

Solution 1

Try the following which uses Spring Expression Language to evaluate the type:

@PreAuthorize("hasRole(T(fully.qualified.OtherClass).ROLE)");

Be sure to specify the fully qualified class name.

Documentation

Solution 2

You can also create a bean container with roles, like:

@Component("R")
public final class RoleContainer {
  public static final String ROLE_A = "ROLE_A";
}

then on controller you can use:

@PreAuthorize("hasRole(@R.ROLE_A)")

Solution 3

To make it possible to write expressions without package names:

<sec:global-method-security>
    <sec:expression-handler ref="methodSecurityExpressionHandler"/>
</sec:global-method-security>

<bean id="methodSecurityExpressionHandler" class="my.example.DefaultMethodSecurityExpressionHandler"/>

Then extend the DefaultMethodSecurityExpressionHandler:

public class DefaultMethodSecurityExpressionHandler extends org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler {

    @Override
    public StandardEvaluationContext createEvaluationContextInternal(final Authentication auth, final MethodInvocation mi) {
        StandardEvaluationContext standardEvaluationContext = super.createEvaluationContextInternal(auth, mi);
        ((StandardTypeLocator) standardEvaluationContext.getTypeLocator()).registerImport("my.example");
        return standardEvaluationContext;
    }
}

Now create my.example.Roles.java :

public class Roles {

    public static final String ROLE_UNAUTHENTICATED = "ROLE_UNAUTHENTICATED";

    public static final String ROLE_AUTHENTICATED = "ROLE_AUTHENTICATED";
}

And refer to it without package name in annotations:

@PreAuthorize("hasRole(T(Roles).ROLE_AUTHENTICATED)")

instead of:

@PreAuthorize("hasRole(T(my.example.Roles).ROLE_AUTHENTICATED)")

Makes it more readable imho. Also roles are now typed. Write:

@PreAuthorize("hasRole(T(Roles).ROLE_AUTHENTICATEDDDD)")

and you will get startup errors that wouldn't have been there if you wrote:

    @PreAuthorize("hasRole('ROLE_AUTHENTICATEDDDD')")

Solution 4

Try something like this:

@PreAuthorize("hasRole(T(com.company.enumpackage.OtherClass).ROLE.name())");

If your OtherClass enum is declared as public static, then you need to use $ sign:

@PreAuthorize("hasRole(T(com.company.ParentTopLevelClass$OtherClass).ROLE.name())");

name() to prevent futer problems if toString() will be overriden later

Solution 5

The accepted answer from Kevin Bowersox works, but I didn't like having the T(fully.qualified.path) stuff so I kept looking. I started by creating a custom security method using the answer from James Watkins here:

How to create custom methods for use in spring security expression language annotations

However, instead of a String, I used my enums.Permissions class as the parameter type:

@Component
public class MySecurityService {
    public boolean hasPermission(enums.Permissions permission) {

        ...do some work here...

        return true;
    }
}

Now the neat part is that when I call the hasPermission from an annotation, I don't have to have to type the whole path, but I do have to enclose it in single quotes:

@PreAuthorize("@mySecurityService.hasPermission('SOME_ROLE_NAME')")

Because the hasPermission method expects an Enum, it will automatically find the Enum value with that name. If it doesn't find it you'll get an exception:

org.springframework.expression.spel.SpelEvaluationException: Type conversion problem, cannot convert from java.lang.String to enums.Permissions

You can rename hasPermission to hasRole, in which case the only trade off is that you are trading T(fully.qualified.path) for @mySecurityService and extra single quotes.

Not sure if it is any better, but there it is. Since none of this is going to verify the values at compile time anyways, my next step is to make an annotation processor.

I also have to give credit to krosenvold for pointing out that spring can automatically convert to an enum: https://stackoverflow.com/a/516899/618881

Share:
16,880

Related videos on Youtube

RobEarl
Author by

RobEarl

Updated on June 17, 2022

Comments

  • RobEarl
    RobEarl almost 2 years

    I'm using spring's PreAuthorize annotation as follows:

    @PreAuthorize("hasRole('role')");
    

    However, I already have 'role' defined as a static String on another class. If I try to use this value:

    @PreAuthorize("hasRole(OtherClass.ROLE)");
    

    I get an error:

    org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 14): Field or property 'OtherClass' cannot be found on object of type 'org.springframework.security.access.expression.method.MethodSecurityExpressionRoot'
    

    Is there a way to access static variables like this with a PreAuthorize annotation?

  • yglodt
    yglodt almost 8 years
    Works, but it's still an interpreted string, so it will not be "seen" by Eclipse when you refactor e.g. the name I guess.
  • chris
    chris over 6 years
    Good solution. Having startup errors would be nice, but I don't think you can achieve that like this. The evaluation still happens during runtime. Haven't found a better solution yet tough...
  • hermeslm
    hermeslm over 5 years
    I think the Kevin Bowersox answer is a good answer but the @MagnusHeino answer is a more complete solution because having the class including the full package looks a little ugly, nevertheless, both answers are correct.
  • Alexander Melnichuk
    Alexander Melnichuk over 3 years
    The shortest and the neatest answer!