用户权限管理体系及其SpringBoot实现
用户管理体系的演变
最早期的实现
流程
不同的用户有不同的角色,用一个role_status表示
比如0代表学生1代表老师
用户根据用户名密码登录后程序通过if else等简单的逻辑判断看用户的role_status从而判断用户是否可以访问相关页面,操作相关菜单按钮。

缺陷
- 耦合性太高,每个方法中都会有大量的if else来判断用户的role_status从而决定什么给用户展现
- 扩展性太低,如果新加入一种role_status,那势必要大量的更改代码,几乎重写所有的方法
拦截器拦截URL
流程
通过拦截器拦截所有的请求这些请求就是@GetMapping等注解上的url
并设置拦截的规则,如不会拦截登录请求,不会拦截静态资源。
不同的用户可以方法不同的url,数据库中维护着所有的url,并维护着用户和他能访问的url的对应关系(就是那些用户能访问那些url)
缺点
没有统一的标准
粒度太细,在资源层面上无法统一。

RBAC权限模型
流程
RBAC是基于角色的访问控制,是用户通过角色与权限进行关联,为什么不直接给用户分配权限呢?简单来说,一个用户拥有多个角色,每个角色拥有若干权限。这样就构成了“用户-角色-权限”的授权模型。在这个模型中,用户与角色、角色与权限之间是多对多的关系。(这样解决上面的粒度太细的问题)
根据角色授权的思想,我们需要设计五张表
(1)三张主表
用户表(user)
角色表(role)
资源表(resource)
(2)两张中间表
用户角色表(user_role)
角色资源表(role_resource)
一个用户可以有多种角色,一种角色可以访问多种资源。当一个用户使用系统时会通过联表查询他的角色,查询他的角色具有的资源,从而判断他是否可以使用某些共能,查看某些界面

使用springboot实现权限验证
流程图下

首先是登录,验证登录信息并生成token
为了方便用户名和密码直接写死了
@PostMapping("login")
public ResultJson login(@RequestParam String userName, @RequestParam String password){
User user=null;
if(userName.equals("lala")&&password.equals("lala")){
List<String> au=new ArrayList<>();
au.add("look");
user=new User("lala","lala",au);
}
if(user!=null){
//生成令牌
String token=UUID.randomUUID()+"";
System.out.println(token);
//存入redis中
redisTemplate.opsForValue().set(token,user,30l,TimeUnit.MINUTES);
return ResultJson.ok(token);
}
return ResultJson.failure(ResultCode.NOT_FOUND);
}
这里为了方便,用户名和密码直接写死了,并未建立RBAC权限模型几张表,而是直接其具有的权限作为了一个写死的字段
自定义一个注解用于标记访问那些url需要的权限
其中value值为权限
@Retention(value = RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
@Inherited
@Documented
public @interface PreAuthorize {
String value();
}
将该注解用于方法上,其中的value代表访问该方法需要的权限
再通过AOP的环绕通知的方式以上面定义的注解为切点,定义一个方法,在用户通过url访问时,先执行该方法。
该方法会获取注解上的权限字段,通过token从redis中取出用户信息,然后判断该用户有无权限访问该url
@Aspect
@Component
public class AuthorizeAspect {
@Resource
private RedisTemplate<String,Object> redisTemplate;
@Pointcut("@annotation(com.wu.myAnnotaion.PreAuthorize)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
Object[] args = pjp.getArgs();
String name = pjp.getSignature().getName();
HttpServletRequest arg=(HttpServletRequest)args[0];
String token=arg.getHeader("token");//得到token
Signature signature = pjp.getSignature();
MethodSignature msg=(MethodSignature) signature;
Object target = pjp.getTarget();
//获取注解标注的方法
Method method = target.getClass().getMethod(msg.getName(), msg.getParameterTypes());
//通过方法获取注解
PreAuthorize annotation = method.getAnnotation(PreAuthorize.class);
Object proceed = null;
try {
User user= (User) redisTemplate.opsForValue().get(token);
if(user==null){
proceed=ResultJson.failure(ResultCode.NOT_LOGIN,"该用户未登录");
return proceed;
}
//annotation.value()获取注解的value值
if(!user.getAuthority().contains(annotation.value())){
proceed=ResultJson.failure(ResultCode.NOT_AUTH,"该用户没有该权限");
}else {
proceed = pjp.proceed(args);
}
} catch (Exception e) {
System.out.println("【环绕异常通知】【"+name+"】方法出现异常,异常信息:"+e);
throw new RuntimeException(e);
} finally{
System.out.println("【环绕后置通知】【"+name+"】方法结束");
}
return proceed;
}
}
运行结果
首先使用用户名lala和密码lala登录获取token

在上面我给用户设置了权限"look"
此时访问有"look2"权限的方法
将@PreAuthorize(“look2”)放在方法上
@GetMapping("getUserOfLogin")
@PreAuthorize("look2")
public ResultJson getUserOfLogin(HttpServletRequest request){
return ResultJson.ok();
}

将上面的权限换为"look"后再访问
@GetMapping("getUserOfLogin")
@PreAuthorize("look")
public ResultJson getUserOfLogin(HttpServletRequest request){
return ResultJson.ok();
}

这篇博客探讨了用户权限管理体系的演变,从早期的简单逻辑判断到URL拦截,再到RBAC模型的引入。详细介绍了如何使用SpringBoot实现基于角色的访问控制,包括用户表、角色表、资源表的设计,以及用户角色、角色资源的关联。通过自定义注解和AOP实现权限验证,确保用户只能访问匹配其权限的URL。
1188

被折叠的 条评论
为什么被折叠?



