SpringBoot集成Spring Security(5)——权限控制
这里的权限只是对角色赋予权限,也就是说同一个角色的用户,权限是一样的。
1. 表结构
CREATE TABLE `sys_permission` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`url` varchar(50) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL,
`permission` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_roleId` (`role_id`),
CONSTRAINT `fk_roleId` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
添加两条数据,url+role_id+permission
唯一标识了一个角色访问某一 url 时的权限,其中权限暂定为 c、r、u、d,即增删改查。
2. 三层结构
2.1 实体类(enity)
/**
* 权限实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SysPermission implements Serializable {
static final long serialVersionUID = 1L;
//id
private int id;
//具有权限的地址
private String url;
//角色
private int roleId;
//权限
private String permission;
//权限集
private List permissions;
public List getPermissions(){
return Arrays.asList(this.permission.trim().split(","));
}
public void setPermissions(List permissions){
this.permissions=permissions;
}
}
由于在数据库中直接设定有个角色对应了很多权限,我们需要单独地提取出这些权限放在一个集合中来获取,方便后面的使用。故多了一个 permissions
属性,该字段将 permission
按逗号分割为了 list
。
2.2 mapper
2.2.1 添加SysPermissionMapper接口
@Mapper
@Repository
public interface SysPermissionMapper {
//根据用户查找出来权限
@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
@Service
public class SysPermissionService {
@Autowired
private SysPermissionMapper sysPermission;
/**
* 获得角色的所有权限
*/
public List<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')")
public String printAdminR(){
return "访问的/admin具有r权限";
}
@RequestMapping("/admin/c")
@ResponseBody
@PreAuthorize("hasPermission('/admin','c')")
public String printAdminC(){
return "访问的/admin具有c权限";
}
接着修改访问的接口,其中@PreAuthorize("hasPermission('/admin','r')")
是关键,参数1(/admin)指明了访问该接口需要的url,参数2®指明了访问该接口需要的权限。
3. PermissionEvaluator
对 hasPermission()
方法的处理,就需要自定义 PermissionEvaluator
,创建类 CustomPermissionEvaluato
r,实现 PermissionEvaluator
接口
/**
* 自定义权限处理
*/
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
@Autowired
private SysPermissionService permissionService;
@Autowired
private SysRoleService roleService;
//参数 1 代表用户的权限身份,参数 2 参数 3 分别和 @PreAuthorize("hasPermission('/admin','r')") 中的参数对应,即访问 url 和权限。
@Override
public boolean hasPermission(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);
//遍历所有权限,即遍历permissionList
for(SysPermission permission:permissionList){
//获取权限集
List permissions=permission.getPermissions();
// 如果访问的Url和权限用户符合的话,返回true
if(targetUrl.equals(permission.getUrl())
&& permissions.contains(targetPermission)){
return true;
}
}
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable serializable, String s, Object o) {
return false;
}
}
其中方法 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
*/
@Bean
public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler(){
DefaultWebSecurityExpressionHandler handler=new DefaultWebSecurityExpressionHandler();
handler.setPermissionEvaluator(new CustomPermissionEvaluator());
return handler;
}
在配置文件application.properties
中添加:
spring.main.allow-bean-definition-overriding=true
不然编译会报错