Spring Security中的角色(roles)和权限(authorities)是有区别的。笔者这篇文章将和大家一起从Spring Security源码的角度探讨其区别在何处,以及合理的使用角色和权限,让我们在使用时做到知其然且知其所以然。

项目环境:jdk1.8,Springboot 2.1.0,IntelliJ idea2018

首先我们在内存中定义几个用户。一个用户名为"cj",角色为 “USER"的用户,另一个用户名为"admin”,权限为"admin"的用户。

spring security权限表设计 spring security 角色 权限_spring的ROLE_

当然这个方法定义在继承了WebSecurityConfigurerAdapter的自定义WebSecurityConfig类中

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        在内存中定义用户,方便测试使用
        auth
                .inMemoryAuthentication()
                .withUser("cj")
                .password(passwordEncoder().encode("123"))
                .roles("USER")
        .and()
                .withUser("admin")
                .password(passwordEncoder().encode("123"))
                .authorities("admin");
    }

然后我们写一个方法用于用户登录后获取用户信息。在一个Controller中定义如下方法:

@RequestMapping("getInfo")
    @ResponseBody
    public Authentication getInfo(){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication;
    }

启动程序:分别使用用户名cj密码123,用户名admin密码123登录。在浏览器中通过上面定义的getInfo方法获取用户信息。

cj的authentication对象(认证信息)如下图:

spring security权限表设计 spring security 角色 权限_用户名_02


可以很清楚的看到,这个authority字段前面多了一个“ROLE_”,程序中定义的是USER。Why?跟进源码…

spring security权限表设计 spring security 角色 权限_hasRole_03

通过阅读源码发现,Spring Security的 public User.UserBuilder roles(String… roles)方法是为了给User附上authorities信息,而且它在赋值的过程中还去判断了程序中开发者设置roles时有没有以“ROLE_”开头,有的话直接赋值。没有的话,呵呵。它自作主张的给role强行加上“ROLE_”前缀。这就是为什么我们获取cj的authentication对象时authority的值为“ROLE_USER”。至于为什么Spring Security的设计师要这样做,我还不清楚(大家谁知道可以留言一下)。

那我们在配置路径认证时的hasROLE()方法,该怎么配置呢。配置成hasRole(“ROLE_USER”)和hasRole(“USER”)都可以,why?跟进源码…

spring security权限表设计 spring security 角色 权限_spring的ROLE__04

在判断roleSet.contains(defaultedRole)之前,Spring Security会想调用getRoleWithDefaultPrefix(prefix, role)此方法,继续跟下去。

spring security权限表设计 spring security 角色 权限_用户名_05

Spring Security和之前在roles()方法中设定role时一样,如果没有设置ROLE_开头,强制给hasROLE()方法中的值设置前缀ROLE_,以此来达到跟之前roles()方法中设定的role匹配的目的。

接下来我们一起来看看authorities()方法中设置admin会怎样。用户名admin,密码123登录后,获取认证信息 http://localhost:9999/getInfo (自己的端口号自己定义)。

spring security权限表设计 spring security 角色 权限_spring的ROLE__06


可以清楚的看到authority为admin。在配置hasAuthority时只能配置成hasAuthority(“admin”),这里面的源码大家自己跟进去看看。和上面分析的差不多,只是authorties()和hasAuthory()方法都没有关于前缀ROLE_的处理。

从上面的分析可以看出,roles的设定其实就是向user的authorities赋值,只不过多了一步加ROLE_的处理,在认证路径的时候同样多了这一步的处理。

所以我们该如何决定使用roles还是authorities呢,我建议是选择authorities。为什么呢,因为我们在使用数据库中的roles往user赋权限时,必须写成这样new SimpleGrantedAuthority(“ROLE_” + role),也就是和源码中一样必须强制添加前缀ROLE_,才能在使用hasRoles,然而这是不合理的。所以笔者建议使用hasAuthority()方法对路径进行权限控制,这样更有逻辑性。