SpringBoot集成Spring Security(5)——权限控制
这里的权限只是对角色赋予权限,也就是说同一个角色的用户,权限是一样的。
1. 表结构
CREATETABLE`sys_permission`(`id`int(11)NOTNULLAUTO_INCREMENT,`url`varchar(50)DEFAULTNULL,`role_id`int(11)DEFAULTNULL,`permission`varchar(20)DEFAULTNULL,PRIMARYKEY(`id`),KEY`fk_roleId`(`role_id`),CONSTRAINT`fk_roleId`FOREIGNKEY(`role_id`)REFERENCES`sys_role`(`id`)ONDELETECASCADEONUPDATECASCADE)ENGINE=InnoDBAUTO_INCREMENT=5DEFAULTCHARSET=utf8; 添加两条数据,url+role_id+permission 唯一标识了一个角色访问某一 url 时的权限,其中权限暂定为 c、r、u、d,即增删改查。
2. 三层结构
2.1 实体类(enity)
/**
* 权限实体类
*/@Data@AllArgsConstructor@NoArgsConstructorpublicclassSysPermissionimplementsSerializable{staticfinallong serialVersionUID=1L;//idprivateint id;//具有权限的地址privateString url;//角色privateint roleId;//权限privateString permission;//权限集privateList permissions;publicListgetPermissions(){returnArrays.asList(this.permission.trim().split(","));}publicvoidsetPermissions(List permissions){this.permissions=permissions;}} 由于在数据库中直接设定有个角色对应了很多权限,我们需要单独地提取出这些权限放在一个集合中来获取,方便后面的使用。故多了一个permissions 属性,该字段将permission 按逗号分割为了list。
2.2 mapper
2.2.1 添加SysPermissionMapper接口
@Mapper@RepositorypublicinterfaceSysPermissionMapper{//根据用户查找出来权限@Select("select * from sys_permission where role_id=#{roleId}")List<SysPermission>listByRoleId(int roleId);}2.2.2 修改SysUserMapper
2.3 service
2.3.1 添加SysPermissionService类SysPermissionService 中有一个方法,根据 roleId 获取所有的SysPermission
@ServicepublicclassSysPermissionService{@AutowiredprivateSysPermissionMapper sysPermission;/**
* 获得角色的所有权限
*/publicList<SysPermission>listByRoleId(int roleId){return sysPermission.listByRoleId(roleId);}}2.3.2 修改SysRoleService类
2.4 controller
在LoginController中添加权限处理,但得把之前的认证处理注释掉,不然会报创建bean异常,因为有相同的RequestMapping
//权限处理@RequestMapping("/admin")@ResponseBody@PreAuthorize("hasPermission('/admin','r')")publicStringprintAdminR(){return"访问的/admin具有r权限";}@RequestMapping("/admin/c")@ResponseBody@PreAuthorize("hasPermission('/admin','c')")publicStringprintAdminC(){return"访问的/admin具有c权限";} 接着修改访问的接口,其中@PreAuthorize("hasPermission('/admin','r')")是关键,参数1(/admin)指明了访问该接口需要的url,参数2®指明了访问该接口需要的权限。
3. PermissionEvaluator
对hasPermission() 方法的处理,就需要自定义PermissionEvaluator,创建类CustomPermissionEvaluator,实现PermissionEvaluator 接口
/**
* 自定义权限处理
*/@ComponentpublicclassCustomPermissionEvaluatorimplementsPermissionEvaluator{@AutowiredprivateSysPermissionService permissionService;@AutowiredprivateSysRoleService roleService;//参数 1 代表用户的权限身份,参数 2 参数 3 分别和 @PreAuthorize("hasPermission('/admin','r')") 中的参数对应,即访问 url 和权限。@OverridepublicbooleanhasPermission(Authentication authentication,Object targetUrl,Object targetPermission){//获得loadUserByUsername()方法的结果User user=(User) authentication.getPrincipal();//获得loadUserByUsername()中注入的角色Collection<GrantedAuthority> authorities=user.getAuthorities();//遍历所有角色for(GrantedAuthority authority:authorities){String roleName=authority.getAuthority();int roleId=roleService.selectByRole(roleName).getId();//得到角色的所有权限List<SysPermission> permissionList=permissionService.listByRoleId(roleId);//遍历所有权限,即遍历permissionListfor(SysPermission permission:permissionList){//获取权限集List permissions=permission.getPermissions();// 如果访问的Url和权限用户符合的话,返回trueif(targetUrl.equals(permission.getUrl())&& permissions.contains(targetPermission)){returntrue;}}}returnfalse;}@OverridepublicbooleanhasPermission(Authentication authentication,Serializable serializable,String s,Object o){returnfalse;}} 其中方法hasPermission(Authentication authentication, Object targetUrl, Object targetPermission),第一个参数authentication(用户的权限身份),第二个参数Object targetUrl(访问的url,即/admin),第三个参数Object targetPermission(这个url需要的权限)
思路如下:
- 通过
Authentication取出登录用户的所有role - 遍历每一个
role,获取到每个role的所有permission - 遍历每一个
permission,只要有一个permission的 url 和传入的url相同,且该permission中包含传入的权限,返回 true - 如果遍历都结束,还没有找到,返回false
在WebSecurityConfig 中注册添加CustomPermissionEvaluator:
/**
* 自定义注入权限PermissionEvaluator
*/@BeanpublicDefaultWebSecurityExpressionHandlerwebSecurityExpressionHandler(){DefaultWebSecurityExpressionHandler handler=newDefaultWebSecurityExpressionHandler();
handler.setPermissionEvaluator(newCustomPermissionEvaluator());return handler;} 在配置文件application.properties中添加:
spring.main.allow-bean-definition-overriding=true不然编译会报错
4. 程序运行
