在业务场景中,我们不希望所有的功能可以随意地被使用,所以通常会将不同的功能让不同的用户由权限去使用,称为权限控制,我将介绍一种强大、易控制、耦合性低的方法去实现这个需求,即结合Spring Security的RBAC权限控制。
RBAC分为两种方式:
基于角色的访问控制(Role-Based Access Control)
基于资源的访问控制(Resource-Based Access Control)
基于角色的访问控制即是根据判断角色的身份来决定是否有执行或访问当前功能/页面的权限
基于资源的访问控制就是根据用户的不同,所看到的资源信息也就不同
以下是实现权限控制的具体流程
首先我们需要在需要被权限控制的服务上引入Spring Security的依赖,并编写配置类等配置信息
(Spring Security的实现不再演示)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
需要注意的是,我这里的token是借助JWT令牌实现的,所以要实现权限的控制,需要在令牌中有一个权限数组用来记录用户具有的权限
然后定义一些数据模型,用来控制角色的权限信息
其中
xc_user:用户表,存储了系统用户信息,用户类型包括:学生、老师、管理员等
xc_role:角色表,存储了系统的角色信息,学生、老师、教学管理员、系统管理员等。
xc_user_role:用户角色表,一个用户可拥有多个角色,一个角色可被多个用户所拥有
xc_menu:模块表,记录了菜单及菜单下的权限
xc_permission:角色权限表,一个角色可拥有多个权限,一个权限可被多个角色所拥有
以下是查询某个用户所具有权限的sql语句
SELECT * FROM xc_menu WHERE id IN(
SELECT menu_id FROM xc_permission WHERE role_id IN(
SELECT role_id FROM xc_user_role WHERE user_id = '49'
)
)
查询流程:根据用户id->用户所具有的角色信息->角色所具有的权限id->权限信息
生成权限模型的流程:
最终将权限信息保存到UserDetails实体类中并返回认证,最终生成JWT令牌
以下是认真过程中的权限关键方法:
public UserDetails getUserPrinciple(XcUserExt xcUser) {
//若查到了用户,将用户信息封装成一个UserDetails返回给框架,由框架进行密码比对
String password = xcUser.getPassword();
//权限
//查询权限,将权限加入到权限数组中
List<XcMenu> xcMenus = xcMenuMapper.selectPermissionByUserId(xcUser.getId());
String[] authorities = {};
if (!xcMenus.isEmpty()) {
List<String> permissions = xcMenus.stream().map(XcMenu::getCode).collect(Collectors.toList());
authorities = permissions.toArray(authorities);
}
//将用户信息转为json
xcUser.setPassword(null); //避免敏感数据
String userJson = JSON.toJSONString(xcUser);
UserDetails userDetails = User.withUsername(userJson).password(password).authorities(authorities).build();
return userDetails;
}
我们在接口的方法上加上@PreAuthorize()注解,表示次方法受到Spring Security权限控制。
@PreAuthorize("hasAnyAuthority('xc_teachmanager_course_list')")
这就表示该功能需要用户具备“xc_teachmanager_course_list”的权限才能够访问
如果访问被拒绝则会抛出AccessDeniedException异常,我已经在全局异常处理器中处理了这个异常,最终会返回“没有操作此功能的权限”异常信息。
在用户不具备访问权限前:
加入权限并重新登录后:
可以正常访问
至于资源的访问控制,可以依靠用户的组别在查询时分别展示不同的数据
在修改用户权限时可以在角色-权限映射表中添加/删除角色的所需要的对应权限
通过以上操作即可实现用户授权和访问控制