基于RBAC实现权限管理

基于RBAC实现权限管理

技术栈:SpringBoot、SpringMVC

RBAC

RBAC数据库表

主体

编号 账号 密码
001 admin 123456

资源

编号 资源名称 访问路径
001 查询用户列表 /user/list

权限

编号 权限标识 权限名称 资源编号
001 user:list 查看用户列表 001

角色

角色编号 角色名称
001 管理员

用户角色

编号 角色编号 用户编号
001 001 001

角色权限

编号 角色编号 权限编号
001 001 001

可以进行一些合并,优化表数量

将资源表和权限表进行合并为权限表

权限

编号 权限标识 权限名称 资源名称 资源访问地址
001 user:list 查询用户列表 用户列表 /user/list

基于角色的访问控制(Role-Based Access Control)

主要思想就是访问后台接口的时候判断该用户的角色是否为某某角色,是的话就放行,否则就拒绝访问。

这里我用的jwt,token里存储的由用户角色(ADMIN)

实现方式:自定义注解+拦截器

自定义注解
@Target({ElementType.METHOD})			//注解作用域方法上
@Retention(RetentionPolicy.RUNTIME)		//编译器将注解信息存储与class文件中,由JVM读取
@Documented
public @interface HasRole {
    String[] value() default {};
}
SpringMVC拦截器
@Component
public class AccessControlInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            if (handlerMethod.hasMethodAnnotation(HasRole.class)) {
                // 从request中获取token
                // 解析token获取claims
                // claims就是个map,直接获取用户角色
                String role = claims.get("userRole");
                String[] hasRole = handlerMethod.getMethodAnnotation(HasRole.class).value();
                boolean contains = Arrays.asList(hasRole).contains(role);
                if(!contains){
                    // 不符合条件,可以抛自定义异常,给客户端提示信息
                    return false;
                }
            }
        }
        return true;
    }
}

注意:最后要把拦截器加入到配置里面

这种方式很简单,应对简单的需求很实用,但是复杂的就行不通了


基于资源的访问控制(Resource-Based Access Control)

主要思想,由于资源是不会变的,我们给角色或者用户分配资源权限后,直接在拦截器里面进行资源权限的校验即可。且有变动的时候基本上不需要改动代码。

用户登录的后把用户的资源权限查出来放到redis里面

实现方式依旧:自定义注解+拦截器

自定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HasResourcePermission {
    String value() default "";
}
SpringMVC拦截器
@Component
public class RequestInterceptor implements HandlerInterceptor {
    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    
        @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            if (handlerMethod.hasMethodAnnotation(HasResourcePermission.class)) {
                String resourcePermissionSign = handlerMethod.getMethodAnnotation(HasResourcePermission.class).value();
                Object resourcePermission = redisTemplate.opsForValue().get("resourcePermission");
                List<String> resourcePermissionList = (List<String>) resourcePermission;
                if (resourcePermissionList.contains(resourcePermissionSign)) {
                    // 不符合条件,可以抛自定义异常,给客户端提示信息
                    return false;
                }
            }
        }
        return true;
    }
}

注意:最后要把拦截器加入到配置里面

基本上就是这样了,也可以根据需求再改,关于数据库表业务会需要加上用户组表的信息。

Tags: