Using typesafe Role enums with Spring Security

Spring Security is a seriously useful tool but one of the things that has been nagging away at the back of my mind about it is that it is so heavily reliant on magic strings. Take a typical example:

<http>
    <intercept-url pattern="/**" access="ROLE_USER"/>
    ...
</http>

<global-method-security pre-post-annotations="enabled"/>

This configures all URLs in the application to require that the user is logged in and has the privilege named ROLE_USER.

@PreAuthorize("hasRole('ROLE_SYSADMIN')")
public String getSensitiveInformation() {
    return "Only special people are allowed to see this information";
}

This ensures that anyone calling this method (either directly or indirectly) must be logged in and have the privilege named ROLE_SYSADMIN.

So far, so straightforward.

The problem is that these role names proliferate across the codebase. Chances are you will want to refer to the same role name in many different places in your application. If these role names were in code, you would typically refactor them out into a single representation as soon as you had two different references to them in the codebase. Unfortunately, this is not so simple with Spring Security.

The good news is that it is possible.

package com.example;

public enum Authority {
    USER,
    SYSADMIN
}

Which can be referred to in Spring’s expression language like this:

<http use-expressions="true">
    <intercept-url pattern="/**" access="hasRole(T(com.example.Authority).USER.toString())">
    ...
</http>

And similarly on method annotations

@PreAuthorize("hasRole(T(com.example.Authority).SYSADMIN.toString())")
public String getSensitiveInformation() {
    return "Still only special people are allowed to see this information";
}

Unfortunately IntelliJ Community Edition’s ‘Find Usages’ is not clever enough to return these Spring expression language references, but it does at least feel better than sprinkling identical magic strings across the codebase.

comments powered by Disqus