用户授权
用户授权
1 RBAC
基于角色的访问控制 (Role-Based Access Control)
- 按角色进行授权(比如 总经理 可以查询员工工资信息)
基于资源的访问控制 (Resource-Based Access Control)
- 按资源(或权限)进行授权 (比如用户必须具有 查询工资权限 才可以查询员工工资信息)
2 资源服务授权流程
(1) 配置nginx代理
#前端开发服务
upstream uidevserver{
server 127.0.0.1:8601 weight=10;
}
server {
listen 80;
server_name teacher.51xuecheng.cn;
#charset koi8-r;
ssi on;
ssi_silent_errors on;
#access_log logs/host.access.log main;
#location / {
# alias D:/itcast2022/xc_edu3.0/code_1/dist/;
# index index.html index.htm;
#}
location / {
proxy_pass http://uidevserver;
}
location /api/ {
proxy_pass http://gatewayserver/;
}
}
(2) 资源服务继承SpringSecurity
在需要授权的接口处使用 @PreAuthorize("hasAuthority('权限标识符')")进行控制
用户没有权限访问时抛出异常:org.springframework.security.access.AccessDeniedException: 不允许访问
(3) 原理
- 访问接口时,携带令牌访问
解析令牌,判断有无接口所需的权限
3 授权相关的数据模型
xc_user:用户表,存储了系统用户信息,用户类型包括:学生、老师、管理员等
xc_role:角色表,存储了系统的角色信息,学生、老师、教学管理员、系统管理员等。
xc_user_role:用户角色表,一个用户可拥有多个角色,一个角色可被多个用户所拥有
xc_menu:模块表,记录了菜单及菜单下的权限
xc_permission:角色权限表,一个角色可拥有多个权限,一个权限可被多个角色所拥有
-
基本查询
select u.id from xc_user u where u.username = 't1'; #结果为52 select m.code from xc_menu m where m.id in ( select p.menu_id from xc_permission p where p.role_id in ( select t.role_id from xc_user_role t where t.user_id = '52' ) );
4 授权测试
- 自定义UserDetailService
@Slf4j
@Component
public class UserServiceImpl implements UserDetailsService {
//实现UserDetailsService加载用户信息
@Autowired
private ApplicationContext applicationContext;
@Autowired
private XcMenuMapper xcMenuMapper;
@Override //传入的参数是AuthParamsDto的json字符串
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
AuthParamsDto authParamsDto = null;
try{
//转成AuthParamsDto对象
authParamsDto = JSON.parseObject(s, AuthParamsDto.class);
}catch (Exception e){
log.info("认证请求不符合项目要求:{}",s);
throw new RuntimeException("认证请求数据格式不对");
}
//获取认证类型
String authType = authParamsDto.getAuthType();
//根据认证类型从spring容器中取出指定的bean
String beanName = authType+"_authservice";
AuthService bean = applicationContext.getBean(beanName, AuthService.class);
//调用统一的excute方法
XcUserExt xcUserExt = bean.execute(authParamsDto);
//封装xcUSerExt用户信息为userDetails
UserDetails userDetails = getUserPrincipal(xcUserExt);
return userDetails;
}
/**
* 查询用户信息
* @param user
* @return
*/
public UserDetails getUserPrincipal(XcUserExt user){
String password = user.getPassword();
//新增------------------------------------------------------------------------------
//权限
String[] authorities = {"test"};
//根据用户id查询用户的权限
List<XcMenu> xcMenus = xcMenuMapper.selectPermissionByUserId(user.getId());
if(xcMenus.size()>0){
List<String> permissions= new ArrayList<>();
xcMenus.forEach(m->{
//拿到了用户拥有的权限标识符
permissions.add(m.getCode());
});
//将permissions转成数组
authorities = permissions.toArray(new String[0]);
}
//新增------------------------------------------------------------------------------
//令牌中不存放密码(为了安全)
user.setPassword(null);
//将user对象转成json
String userString = JSON.toJSONString(user);
UserDetails userDetails = User.withUsername(userString).password(password).authorities(authorities).build();
return userDetails;
}
}
- XcMenuMapper
public interface XcMenuMapper extends BaseMapper<XcMenu> {
@Select("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 = #{userId} ))")
List<XcMenu> selectPermissionByUserId(@Param("userId") String userId);
}
5 细粒度授权
-
细粒度授权:也叫数据范围授权,不同用户所拥有的操作权限相同,但能够操作的数据范围是不一样的
例:用户A和用户B都是教学机构,都拥有"我的课程"权限,但两个用户所查询到的数据是不一样的