logo 范 · 拾光录
网址收集 关于作者 Github Gitee
杂文随笔5
Hexo博客:基础使用Hexo博客:Next主题Hexo博客:Next进阶使用Hexo博客:Next高级配置基于Node的WIKI管理
前端知识16
HTML常用知识CSS常用知识CSS美化checkbox复选框JavaScript常用知识JavaScript格式化时间戳JavaScript窗口宽高处理JavaScript黑夜主题切换实现方案JavaScript数字转大写简易图片查看器TypeScript基础知识Threejs基础三要素Threejs网格辅助和轨道控制器Threejs物体绘制Electron基础使用Nodejs基础知识animate.css页面动画
Vue框架19
Vite的使用及扩展Vue3父子组件Vue3使用Marked解析MarkdownMermaid图表生成库初始化页面加载动画Axios表单提交二维码解决方案NProgress加载进度条Vue3动态菜单实现Vue3使用ECharts图表Vue3处理Excel导入导出keep-alive页面缓存及setup问题Element:文件上传Element:结合Pinia实现动态菜单Element:图片上传组件Element:自定义统一弹窗组件Element:表格自定义指令控制按钮显示(鉴权)可视化大屏使用缩放适配分辨率
UniApp15
UniApp的基础使用封装网络请求工具及文件上传uni-app的开发记录微信小程序分享原生文件上传Pinia取消滚动条(兼容小程序)tabbar消息数量显示scroll-view上滑到底部加载数据状态栏高度动态设配数据共享与传递uview-plus导航栏实现背景融合Wot UIWot UI实现顶部背景图融合uni-app x
Java基础知识10
基础知识面向对象Lambda表达式常用API常用知识积累try-with-resource注解反射多线程经纬度距离计算
SpringBoot31
application配置Maven创建聚合项目全局异常处理锁机制项目启动初始化数据方式邮件功能集成原生定时任务异步集成阿里云OSS阿里OSS预签名上传基于hutool读excelJSR303WebSocketWebSocket版AI接口流式调用Smart-Doc接口文档生成器application配置信息加密雪花算法工具AOP实现请求参数脱敏思路JWT生成Token及工具类SpringBoot默认JSON与对象转换若依框架:安装使用若依框架:优化和调整文件上传若依框架:管理后台页面优化若依框架:后端接口代码优化SpringAISpringBoot实现AI接口流式调用服务启动时创建MySQL连接自建项目工程树形结构处理工具微信支付代码微信手机号登录
SpringMVC14
跨域处理拦截器RESTful风格伪前后端分离Jackson转换器调整Thymeleaf基于拦截器做权限校验AOP打印接口请求响应日志AOP打印接口请求响应耗时文件上传和回显POST请求加解密实现(AES)POST请求加解密实现(RSA+AES)参数动态校验实现方案真实IP和归属地
MyBatis8
MyBatis基本使用与配置Mapper使用相关MaBatis多数据源配置MyBatisPlus数据统计类处理方案MyBatisPlus条件查询正向工程的实现(H2)mybatis-plus-join
SpringCloud15
Netflix:微服务与搭建Netflix:服务的消费与提供Netflix:EurekaNetflix:ActuatorNetflix:RibbonNetflix:FeignNetflix:HystrixNetflix:ZuulAlibaba:简介与搭建Alibaba:Nacos注册中心Alibaba:RibbonAlibaba:OpenFeignAlibaba:Nacos配置中心Alibaba:GetewayAlibaba:Sentinel
MySQL6
MySQL基础知识MySQL多表查询与事务MySQL常用函数及解决方案MySQL视图MySQL索引安装MySQL
Redis7
Redis介绍和安装Redis配置文件Redis持久化Redis集群Redis语法基础Redis相关问题及解决方案SpringBoot集成Redis使用记录
MongoDB10
Linux安装MongoDBMongoDB基础语法MongoTemplate及SpringBoot配置MongoTemplate中Update操作MongoTemplate中聚合查询MongoTemplate日期归档示例项目使用相关知识归纳地理位置存储与距离查询MongoDB副本集与事务获取类名和属性名工具类
其他数据库1
H2数据库
Python编程6
Python基础知识Python语法yolo目标检测OpenCV的使用及树莓派平台condauv
工具集合13
IDEAMavenGradleGitNginx安装Nginx配置VSCodeJMeter压测DockerOllamaRustFSPicGoObs录制
Linux知识11
Linux常用命令Jar启动脚本VirtualBox安装CentOSVirtualBox安装Ubuntu树莓派安装及使用frp内网穿透ArchLinux:基础系统安装ArchLInux:图形化界面安装ArchLinux:常用软件ArchLinux:深度优化ArchLinux:Niri
创意设计2
Blender:入门知识UI设计基础知识
AI相关9
Claude CodeHermes AgentOpenAI基本使用OpenAI工具调用OpenAI记忆管理OpenAI推理执行OpenAI开发框架Langchainllama.cpp

权限校验

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

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

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

实现的方案就是,可以将其表设计简化,大体用户、角色、权限关系模型依然没有变,但是可以将每个接口规定一个权限值(数字),并直接放在角色表中即可,大大简化了复杂度,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(),因为此时是个数组

以下内容是二次升级后记录

说明

可实现一下功能:

实现方式

定义一个权限列表注解类

import java.lang.annotation.*;

/**
 * 自定义注解实现鉴权
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Auth {

    String[] keyType();

    /**
     * 登录后的权限
     */
    String COMMON_LOGIN = "COMMON_LOGIN";

    /**
     * 测试接口权限
     */
    String COMMON_TEST = "COMMON_TEST";

}

定义拦截器拦截做权限判断

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gxzn.commonVo.Result;
import com.gxzn.vo.Auth;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

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

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        // 获取请求头的token
        String token = request.getHeader("token");
        // 这里根据token查询到用户信息和权限列表
        List<String> auths = new ArrayList<>();
        auths.add("COMMON_LOGIN");
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Auth auth = handlerMethod.getMethodAnnotation(Auth.class);
        // 判断权限值是否包含了接口所需要的值,有一个即可
        if (Objects.nonNull(auth) && Arrays.stream(auth.keyType()).anyMatch(auths::contains)) {
            // 有权限放行
            return true;
        }
        // 没有权限不放行并设置一个状态码给前端做判断
        response.setStatus(200);
        response.setContentType("application/json;charset=UTF-8");
        Result<String> result = new Result<>();
        result.setCode(Result.CODE_UNAUTHORIZED);
        result.setMsg("未授权");
        response.getWriter().write(new ObjectMapper().writeValueAsString(result));
        return false;
    }

}

注册拦截器,指定哪些接口需要拦截

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 拦截器注册
 */
@Configuration
public class Interceptor implements WebMvcConfigurer {

    @Bean
    ReqInterceptor reqInterceptor() {
        return new ReqInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(reqInterceptor()).addPathPatterns("/**")
                .excludePathPatterns("/login");

    }

}

使用方式

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

    /**
     * 示例
     */
    //    @Auth(keyType = Auth.COMMON_LOGIN) // 单个权限
    @Auth(keyType = {Auth.COMMON_LOGIN, Auth.COMMON_TEST}) // 多个权限有一个即可
    @RequestMapping("/demo")
    public Result<String> demo(@RequestBody SystemAdminUser systemAdminUser) {
        System.out.println(systemAdminUser);
        return Result.successMsg("成功");
    }

}

信息传递

在拦截器中将用户相关的信息放入Attribute中,然后使用工具类去获取即可,例如在拦截器中加入数据:

// 将信息加入到请求头,便于controller使用
request.setAttribute("adminType", userCacheVo.getType());
request.setAttribute("adminId", userCacheVo.getId());

使用工具类进行获取:

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * 用户信息工具
 */
public class UserInfoUtil {

    /**
     * 获取管理员ID
     */
    public static String adminUserId() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (Objects.nonNull(requestAttributes)) {
            HttpServletRequest request = requestAttributes.getRequest();
            System.out.println(request.getAttribute("adminId"));
            return (String) request.getAttribute("adminId");
        }
        return "";
    }

    /**
     * 获取管理员类型
     */
    public static Integer adminUserType() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (Objects.nonNull(requestAttributes)) {
            HttpServletRequest request = requestAttributes.getRequest();
            System.out.println(request.getAttribute("adminId"));
            return (Integer) request.getAttribute("adminType");
        }
        return 0;
    }

}
权限校验
实现方式
复杂业务
以下内容是二次升级后记录
说明
使用方式
信息传递