[SpringSecurity] The difference between hasRole and hasAuthority

I believe that many small partners will be troubled by this problem when they first contact Spring Security, such as the following two configurations:

http.authorizeRequests()        .antMatchers("/admin/**").hasAuthority("admin")        .antMatchers("/user/**").hasAuthority("user")        .anyRequest().authenticated()

as well as

http.authorizeRequests()        .antMatchers("/admin/**").hasRole("admin")        .antMatchers("/user/**").hasRole("user")        .anyRequest().authenticated()

So what is the difference between these two configurations?

Today we will talk to you about this issue.

1. Source code analysis

Simply analyze from the source code, you will find that these two things seem to be the same, let's look at hasAuthority first.

public ExpressionInterceptUrlRegistry hasAuthority(String authority) { return access(ExpressionUrlAuthorizationConfigurer.hasAuthority(authority));}private static String hasAuthority(String authority) { return "hasAuthority('" + authority + "')";}

Finally, the access method is called and the authorization expression hasAuthority('xxx') is passed in.

Look at hasRole again:

public ExpressionInterceptUrlRegistry hasRole(String role) { return access(ExpressionUrlAuthorizationConfigurer.hasRole(role));}private static String hasRole(String role) { Assert.notNull(role, "role cannot be null"); if (role.startsWith("ROLE_")) {  throw new IllegalArgumentException(    "role should not start with 'ROLE_' since it is automatically inserted. Got '"      + role + "'"); } return "hasRole('ROLE_" + role + "')";}

You can see, hasRole processing logic and hasAuthority seem exactly the same, the difference is, hasRole here will automatically give the incoming string with ROLE_the prefix, so permissions string in the database need to add the ROLE_prefix. That is ROLE_admin, if the user role stored in the database is , it is admin here.

We call hasAuthorityupon the method, if the data is a query from the database, where the rights and the same can be stored in the database, you can not add ROLE_a prefix. That is, if the user role stored in the database is admin, it is admin here.

That is, using hasAuthoritymore consistent, you do not have to consider whether or not to add ROLE_the prefix, the database here is what what! And hasRoleit is different, if the code is written admin, the frame will automatically add ROLE_the prefix, so the database will have to be ROLE_admin.

HasRole difference hasAuthority looks and seems only in that there is no ROLE_prefix.

In the final permission comparison, it was even more excessive. Both hasAuthority and hasRole eventually called the hasAnyAuthorityName method (SecurityExpressionRoot class):

public final boolean hasAuthority(String authority) { return hasAnyAuthority(authority);}public final boolean hasAnyAuthority(String... authorities) { return hasAnyAuthorityName(null, authorities);}public final boolean hasRole(String role) { return hasAnyRole(role);}public final boolean hasAnyRole(String... roles) { return hasAnyAuthorityName(defaultRolePrefix, roles);}private boolean hasAnyAuthorityName(String prefix, String... roles) { Set<String> roleSet = getAuthoritySet(); for (String role : roles) {  String defaultedRole = getRoleWithDefaultPrefix(prefix, role);  if (roleSet.contains(defaultedRole)) {   return true;  } } return false;}

hasAnyRole set when calling hasAnyAuthorityName method ROLE_prefix, hasAnyAuthority not set a prefix when calling hasAnyAuthorityName method.

So we just from the source point of view, hasRoleand hasAuthoritythese two functions seem exactly the same, except for the prefix on no difference.

So why did Spring Security designers make two things that look exactly the same?

2. Design concept

In terms of design, these are two different things. The provision of role and authority at the same time is to facilitate developers to design permissions from two different dimensions, so there is no conflict.

Authority describes a specific authority, such as a query or delete authority for a certain item of data. It is a permission, such as read_employee, delete_employee, update_employee, etc. These are specific permissions, I believe everyone can understand.

role is a collection of permission, which naming convention is to ROLE_begin, for example ROLE our definition is ROLE_ADMIN, ROLE_USERand so on. We can see the special treatment for Role in Spring Security in many places, such as the article we are talking about voting and decision-making vessel , RoleVoter automatically added when dealing Role ROLE_prefix.

In the project, we can associate users with roles, roles with permissions, and permissions with resources.

Reflected in the code, it is as follows:

Suppose we use the representative authority of SimpleGrantedAuthority provided by Spring Security, and then we customize a Role as follows:

public class Role implements GrantedAuthority {    private String name;     private List<SimpleGrantedAuthority> allowedOperations = new ArrayList<>();     @Override    public String getAuthority() {        return name;    }     public List<SimpleGrantedAuthority> getAllowedOperations() {        return allowedOperations;    }     public void setAllowedOperations(List<SimpleGrantedAuthority> allowedOperations) {        this.allowedOperations = allowedOperations;    }}

A Role is a collection of certain authorities, and then define the roles collection in the User.

public class User implements UserDetails {    private List<Role> roles = new ArrayList<>();     public List<Role> getRoles() {        return roles;    }     public void setRoles(List<Role> roles) {        this.roles = roles;    }     @Override    public Collection<? extends GrantedAuthority> getAuthorities() {        List<SimpleGrantedAuthority> authorities = new ArrayList<>();        for (Role role : roles) {            authorities.addAll(role.getAllowedOperations());        }        return authorities.stream().distinct().collect(Collectors.toList());    }}

In the getAuthorities method, load the permissions in roles to remove the duplicates and then return.

Through this example, everyone should be able to understand Role and Authority.

Song Ge also saw a similar issue on Spring Security's issue: https://github.com/spring-projects/spring-security/issues/4912

From the author's reply to this question, we can also see some clues:

  1. The authors acknowledge the current plus ROLE_the prefix way to bring a certain degree of confusion to the developer, but this is a historical accumulation problem.
  2. The authors say if you do not like ROLE_, it can be used directly hasAuthorityin place hasRole, the implication is that these two functions are the same.
  3. The author also talked about some opinions about permissions. Permissions are typical control of objects, but Spring Security developers cannot add all permissions to Spring Security users, because in most systems, permissions are too complex and too large to be fully contained. In memory. Of course, if the developer needs it, he can customize the class inherited from GrantedAuthority to extend its functionality.

We can also see from the author's reply came out, hasAuthorityand hasRoleno difference on the function, the design level is really two different things.

3. History

In fact, before the Spring Security4, hasAuthorityand hasRolealmost exactly the same, even the ROLE_differences are not!

That hasRole("admin")and hasAuthority("admin")the same.

And after the Spring Security4, only the prefix ROLE_different.

If you are interested in this section, you can take a look at the migration document from Spring Security3 to Spring Security4:

  • http://docs.spring.io/spring-security/site/migrate/current/3-to-4/html5/migrate-3-to-4-jc.html#m3to4-role-prefixing

4. Summary

In a word, in terms of code, hasRole and hasAuthority have different prefixes when writing code, but the final execution is the same; in terms of design, role and authority are two levels of permission design ideas, one is role and the other is permission , The role is a collection of permissions.