Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create typesafe user roles for Spring Security?

I'd like to make use of spring-security with ROLE_ADMIN and ROLE_USER roles.

I therefore try to create a typesafe enum class, but the @Secured annotation requires a constant String, which I cannot achieve by using an enum class.

What could I change in the following code?

public enum UserRole {
    ADMIN("ROLE_ADMIN");

    private String role;

    public UserRole(String role) {  
        this.role = role;
    }
}

//error: The value for annotation attribute Secured.value must be a constant expression
@Secured(USerRole.ADMIN.value())
public class SecuredView {

}
like image 275
membersound Avatar asked May 13 '14 11:05

membersound


3 Answers

This question is a bit old, but this is my take on it:

public enum Role implements GrantedAuthority {
    ROLE_USER, ROLE_ADMIN;

    @Override
    public String getAuthority() {
        return name();
    }
}

You can then use this together with @PreAuthorize and Spring Expression Language to authorize your methods and classes like so:

@PreAuthorize("hasRole(T(<package name>.Role).ROLE_ADMIN)")
public void doSomeThing() {
    ...
}

Note: The package name has to be the entire package name (org.company.project) and without the < and >.

As you can see, this isn't type safe per definition, as SpEL expressions are still strings, but IDEs like IntelliJ recognizes them, and will let you know of any errors.

You can use @PreAuthorize with multiple roles using hasAnyRole().

Of course, this may become a bit verbose with many roles, but you can make it prettier by creating your own annotation like this:

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@PreAuthorize("hasRole(T(<package name>.Role).ROLE_ADMIN)")
public @interface AdminAuthorization {
}

Following this, you can authorize your methods like so:

@AdminAuthorization
public void doSomething() {
    ...
}
like image 88
Håkon M. T. Avatar answered Oct 03 '22 08:10

Håkon M. T.


Partial solution:

public enum Role implements GrantedAuthority {

    ADMIN(Code.ADMIN),
    USER(Code.USER);

    private final String authority;

    Role(String authority) {
        this.authority = authority;
    }

    @Override
    public String getAuthority() {
        return authority;
    }

    public class Code {
        public static final String ADMIN = "ROLE_ADMIN";
        public static final String USER = "ROLE_USER";
    }
}

Results in:

@Secured(Role.Code.ADMIN)
like image 18
Terran Avatar answered Oct 03 '22 07:10

Terran


If you are using Lombok you can use the @FieldNameConstants annotation:

@FieldNameConstants
public class UserRoles {
    private String ROLE_USER, ROLE_ADMIN;
}

And then use it like this:

@Secured(UserRoles.Fields.ROLE_ADMIN)

Maybe in the future, there will be the @EnumNameConstants annotation, which would fit even better.

like image 1
Alwin07 Avatar answered Oct 03 '22 08:10

Alwin07