🥝FANMR.CN热爱,追求
基于拦截器做权限校验

权限校验

在通用的权限管理中,会出现用户、角色、权限三个表,并且设置大多保存菜单列表给前端,如果将粒度改为按钮级别,表中的数据是很多的,每次查询都要关联到很多数据

虽然三者的关系看似多对多(表设计),其实在真正应用时会发现,一个用户进入系统只会选择一个角色,而一个角色对应的也只有一套权限,从这个大方向上看,三者的关系是单一的

那么要做到细粒度的权限控制,只需要规定好每个接口都需要一个权限值,而每个角色能拥有某些权限值,一个用户绑定一个角色即可

实现的方案就是,可以将其表设计简化,大体用户、角色、权限关系模型依然没有变,但是可以将每个接口规定一个权限值(数字),并直接放在角色表中即可,大大简化了复杂度,session中不在缓存那么多数据(session数据量大也会耗内存)

实现方式

定义一个权限列表注解类

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Auth {

    int keyType();

    /**
     * 登录后的权限
     */
    int AUTH_COMMON_LOGIN = 100;

    /**
     * 测试接口权限
     */
    int AUTH_COMMON_TEST = 101;

}

定义拦截器拦截权限

**
 * 请求拦截
 */
public class ReqInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1.通过session或toten拿到用户角色(ID或名称)
        // 2.通过角色在缓存或数据库中拿到角色对应的权限值(字符)        
        // 3.拿到接口所需要的权限值
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Auth auth = handlerMethod.getMethodAnnotation(Auth.class);
        // 判断权限值是否包含了接口所需要的值
        if (auths.contains(String.valueOf(auth.keyType()))) {
            // 有权限放行
            return true;
        }
        // 没有权限不放行并设置一个状态码给前端做判断
        response.setStatus(500);
        return false;

    }
}

使用方式,在接口上加权限注解

@RestController
@RequestMapping("/test")
public class TestController {

    @PostMapping("say")
    @Auth(keyType = Auth.AUTH_COMMON_TEST)
    public R<String> test(@RequestBody JSONObject params) {
        System.out.println(params);
        return R.ok("测试成功");
    }

}

复杂业务

有这样一个场景,例如公告管理接口,公告存在于不同的版块中,这对于接口端来说就是一个字段的区别,但是在前端,不同的模块放在了不同的地方,并且对于不同的角色看到的模块不一样,如果基于接口来做的权限,也就是这个接口是否有权访问,如果这个接口有权访问,那么所有模块都有权访问,这不符合需求,如果再写一个接口(就是同样的代码),这就是低级程序员干的事,所以现在需要实现的是接口与权限的一对多关系

使用上面的方式做的权限校验可以轻松的实现

调整注解类

int[] keyType();

使用的时候指定多个权限

@Auth(keyType = {Auth.AUTH_COMMON_NOTICE_LIST,Auth.AUTH_COMMON_LOGIN})

调整拦截器的auth.keyType(),因为此时是个数组