我们的认证部分(登录验证)已经完成,现在来做用户授权。授权即是判断当前用户可以访问哪些页面,哪些功能方法,哪些按钮。
1、启用注解
这样我们就可以使用@PreAuthorize注解了
2、@PreAuthorize注解
我们常用的注解有三个,一个是hasRole,判断当前用户具有某个角色才能访问,hasAuthority,判断当前用户具有某个权限字符才能访问,isAuthenticated用户只要登录了就能访问。
上面说hasRole和hasAuthority是一样的,这并不准确,从我的解释中就可以看出两者并不一样。在shiro里面,两者是分开处理的,而在security里面做了一个隐藏开关。
之前我们在介绍UserDetails接口的时候看到只有getAuthorities方法,并没有getRoles方法,因为在security里面,authority和role合并处理了,但是,我们如何区分authority和role呢,通过"ROLE_"前缀来区分,这就是我说的隐藏开关。
我们修改一下User里面的getAuthorities方法
为了方便测试,这里我们再添加一个固定的角色和字符串。
回到UserController类,我们来添加注解
上面的注解表示只有具有admin角色的用户才能访问用户列表页面,我们重启应用,进行测试。
这时,我们会发现,如果我们直接访问用户列表页面localhost:8080/security/user/list页面,将会自动跳转到登录页面,因为security检测发现当前页面需要判断用户是否具有admin角色,而用户没有登录,必须先登录才能进行判断,登录完,发现可以正常访问。
为了进一步验证,我们修改赋予的默认角色,改成ROLE_admin1,我们重新进入登录页面登录(为什么不修改UserController里面的注解呢,因为修改注解我们需要重启应用才能生效),
会显示403页面,即无权限禁止访问。
我们再用同样的方法测试hasAuthority
效果是一样的。
对于hasAuthority,hasRole,有相应的扩展方法hasAnyAuthority,和hasAnyAuthority,例如我们可以这样使用:
@PreAuthorize("hasAnyAuthority('jbf:security:user:list', 'jbf:security:user:save')")
还可以这样:
@PreAuthorize("hasAuthority('jbf:security:user:list') or hasAuthority('jbf:security:user:save')")
当然or可以换成and,注解的用户,security比shiro更加灵活,更多的使用方法大家可以在网上查询。
对于权限字符串security没有特殊要求,但我们一般给定义一个规则为佳,例如jbf:security:user:list,jbf表示系统的名称,security表示功能模块的名称,user表示实体的名称,list表示功能名称。
3、后台授权
这里我们使用如下字符串对用户后台功能进行授权:
jbf:security:user:list
jbf:security:user:save
jbf:security:user:edit
jbf:security:user:delete
jbf:security:user:assignRole
这里我们使用如下字符串对角色后台功能进行授权:
jbf:security:role:list
jbf:security:role:save
jbf:security:role:edit
jbf:security:role:delete
jbf:security:role:assignPerm
这里我们使用如下字符串对权限后台功能进行授权:
jbf:security:perm:list
jbf:security:perm:save
jbf:security:perm:edit
jbf:security:perm:delete
这里我们使用如下字符串对文章后台功能进行授权:
jbf:cms:post:list
jbf:cms:post:save
jbf:cms:post:edit
jbf:cms:post:delete
我们在系统里面将这些权限字符串建好,并创建一个admin的角色,和admin用户,让admin用户具有admin角色,admin角色具有所有权限,在没绑定好之前,我们可以先禁用@EnableGlobalMethodSecurity(prePostEnabled = true)注解,方便我们录入数据。我们也可以直接在数据库录入:
录完数据,使用admin用户登录理论上所有功能都正常。
4、SecurityContext
在讲前台授权前,我们不得不先提下SecurityContext,SecurityContext提供给了我们一个关于当前用户权限的上下文。我们可以通过下列代码获取当前用户权限信息:
Authentication authentication= SecurityContextHolder.getContext().getAuthentication()
authenticated表示当前用户是否认证通过,authorities存储当前用户的权限集合,principal则是我们的user对象。这样我们除了之前使用注解判断权限,其实还可以使用Authentication硬编码去判断,一般在封装通用方法的时候我们会使用这种方式。
5、前台授权
对于我们这种非前后端分离的架构来说,页面权限其实我们已经控制了,因为我们所有的页面都是经过后台Controller映射的,控制了映射方法的权限就控制了页面的权限,现在我们来看看,我们如何控制按钮的权限。
上面讲到了我们可以用过Authentication获取当前用户的权限列表,因为我们使用的是freemarker模板,我们可以直接将权限列表传入到模板变量里面,在页面直接就可以获取列表,判断是否包含某个字符即可。但是为了模拟前后端分离的场景,笔者决定还是换种方法。对于前后端分离的应用,我们可以增加一个后台服务,获取当前登录用户的权限列表,或者在登录的时候将权限列表返回(我们这里登录接口其实返回了权限列表,但是由于登录页面都是security默认的,我们无法获取信息),在获取列表信息后,将信息缓存到浏览器,一般可以是localStorage或者是sessionStorage。
这里我们做最简单的,增加一个后台服务,传入需要判断的权限字符串,返回是否具有权限。
增加一个api.js,添加查询权限的方法,并将方法绑定到全局的vue对象上。
页面引入api.js
定义变量,绑定按钮可见值
默认值都给false
在页面加载时,判断是否显示
这样用户列表页面的按钮权限就绑定好了。
请将其他几个页面也参照完成。
6、总结
一般来说,我们使用权限字符串就可以完成所有的授权功能。因为只有权限字符串是固定的,这是开发阶段,我们线下约定好的,而角色是动态增加的,在项目实际运行过程中,我们角色可以由用户自由设定,但是权限字符串不能让用户去修改,这个应该是出厂内置的。
代码:
https://github.com/www15119258/springboot-study/tree/branch24
网友评论