一般系统的请求认证授权思路【gateway网关+jwt+redis+请求头httpheader】

gateway:网关,我们都知道网关的作用就是对系统的所有请求,网关都会进行拦截,然后做一些操作(例如:设置每个请求的请求头httpHeader,身份认证等等)此时一般会使用到网关过滤器,创建一个过滤器去实现GlobalFilter接口

jwt:JSON-WEB-TOKEN,这里就不过多解释了,同学们可以自行搜索相关文章,它主要包含三个部分,更好的生成token的一种行业规范,主要作用就是令牌校验。

{

        header:xxx,

        payload:xxx,

        singature:xxx

}

redis的主要作用就是为了存放用户信息,该用户信息主要包含以下几个字段

{
    userCode:xxx,
    language:xxx,主要是为了进行国际化语言配置比如ZH-CN 、EN
    menuApu:["xxx","xxx",..."xxx"] 用户对应的菜单权限列表API
    jwtToken:xxx 必须拥有这个字段,为了防止同一个用户在不同机器上登录进行操作
}

httpHeader的主要作用就是存放userCode,用于任何请求都可以获取到当前操作的用户名,比如当我要添加一个新增接口或者更新接口,一般会有两个字段,一个是creator,一个是modifier,那么这两个值就可以直接取请求头的userCode。

还有就是存放language,为了进行国际化语言切换。

所以现在我先给大家画一个图,待会写的代码也是按照这个逻辑,方便大家加深理解与记忆。

现在看一下代码逻辑:

我们创建一个gateway全局过滤器:AuthTokenGlobalFilter

实现GlobalFilter接口,重写如下方法:

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();//得到前端访问请求时的请求头
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.putAll(headers);
        //先过滤掉登录请求,登录请求不需要进行拦截,直接放行
        if (ObjectUtil.equals(request.getURI().getPath(), "/login")) {
            //放行之前将前端发出请求时的请求头拿到,同时设置一下Accept-Language
            httpHeaders.setAcceptLanguage(httpHeaders.getAcceptLanguage());
            //放行
            chain.filter(exchange.mutate().request(decorateRequest(request, httpHeaders)).build());
        }
        //其他请求
        //判读1:请求的请求头中是否携带token
        if (CollectionUtils.isEmpty(headers.get("Authorization"))) {
            throw new ServiceException(Codes.NO_TOKEN_RECOGNIZED);//未识别到token
        }
        //判断2:token解析出来的数据是否能在redis中查询到,考虑到token有可能发生过期,或者是登出了,redis中的数据就会删除
        String authorization = headers.get("Authorization").get(0);
        //通过token 利用jwt反解析出数据 claims
        JSONObject jsonObject = JwtUtils.parseJwtToken(authorization, "user", NetworkUtils.getIp(request));
        if (ObjectUtil.isNull(jsonObject)) {
            throw new ServiceException(Codes.AUTHORIZATION_ERROR);
        }
        //如果是登出请求/logout,需要在请求头中加上userCode,为了后续放行之后,拿到该userCode作为redis的key的一部分去删除登录时存到redis中的数据
        if (ObjectUtil.equals(request.getURI().getPath(), "/logout")) {
            httpHeaders.set("userCode", jsonObject.get("userCode").toString());
            return chain.filter(exchange.mutate().request(decorateRequest(request, httpHeaders)).build());
        }
        //拿到userCode
        String userCode = (String) jsonObject.get("userCode");
        //去redis中获取数据 key=》 User:userCode 比如User:zkw
        String key = USER_CACAHE_PREFIX + userCode;
        String jsonString = stringRedisTemplate.opsForValue().get(key);
        LoginUser loginUser = JSONObject.parseObject(jsonString, LoginUser.class);
        if (ObjectUtils.isEmpty(loginUser)) {
            throw new ServiceException(Codes.LOGIN_EXPIRED);
        }
        //走到这里
        //1、发出的请求的请求头中携带了token
        //2、携带的token有效,没有过期或者是登出
        //那么此时还需要进行判断3,判断携带的token与redis中存储的token是否一致,因为有可能有这么一个情况
        // A用户在机器172.16.254.1上登录一次,成功之后,就会在redis中存放A对应的token以及其他一些字段数据,
        //那么此时在A发出另外一个请求之前,比如查询,此时A用户又在另外一台机器上172.16.254.2登录,成功之后,也会在redis中存放A在172.16.254.2对应的token以及其他一些字段数据,此时就会覆盖前面redis中存放A在172.16.254.1的token数据了,
        // 因为生成的token是有根据ip地址的,所以当A用户在机器上172.16.254.1发起请求的时候,携带的token就与此时redis中的token是不对的了,所以对于这种情况我们就不允许存在。
        if (!ObjectUtil.equals(authorization, loginUser.getJwtToken())) {
            throw new ServiceException(Codes.LOGIN_EXPIRED);
        }

        //如果token都满足情况了,就代表确确实实身份无误了,那么就需要进行用户的权限列表判断了。
        
    }

上面是认证逻辑,现在是授权逻辑了。

redis中有两个key,一个key就是刚刚关于认证token的,另一个就是授权的。AccessControl:permissions,这个key的主要作用就是看看前端发出的请求是否是系统里面已经配置的菜单权限API,防止随意伪造请求API。

接下来我们看看授权的代码:

//***********************************************授权*************************************************************
        //异步获取统里面配置的存在的菜单api,在redis中有存放两个key,一个key就是刚刚前面关于token的,另外一个是关于菜单权限api的
        //AccessControl:permissionsMenu 这个值的主要作用就是为了判断你请求的api是否此时系统里面有,如果没有的话,有两个原因:
        //  1、你xjb自己随便别写一个请求 2、确确实实开发了这个接口,但是可能在系统菜单表里面忘记添加了
        //User:userCode
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            return stringRedisTemplate.opsForValue().get("AccessControl:permissionsMenu");
        });
        String permissionsMenu = future.join();
        //如果不为空,就代表此时系统里面已经配置了菜单权限api
        if (permissionsMenu != null) {
            String[] urls = permissionsMenu.split(",");
            //当前url不存在系统配置的菜单权限api里面 直接放行
            if (Arrays.stream(urls).noneMatch(part -> (part.trim()).equals(requestPath))) {
                return chain.filter(exchange.mutate().request(decorateRequest(request, httpHeaders)).build());
            }
        }
        //如果菜单为空,证明是第一次请求到,把系统里面配置的存在的api设置到该key上
        else {
            //通过openFeign远程调用获取系统配置的菜单权限API
            List<String> urls = permissionFeign.queryPermissionsMenu();
            stringRedisTemplate.opsForValue().set("AccessControl:permissionsMenu", urls.toString());
            //当前url不存在系统配置的菜单权限api里面 直接放行
            if (urls.stream().noneMatch(part -> (part.trim()).equals(requestPath))) {
                return chain.filter(exchange.mutate().request(decorateRequest(request, httpHeaders)).build());
            }
        }

        //如果存在,如果判断当前用户是否拥有这个菜单权限api
        boolean flag = sysMenuService.queryUserMenuButton(userCode).stream().anyMatch(arg -> ObjectUtil.equals(arg.getApi(), requestPath));
        if (!flag) {
            throw new ServiceException(Codes.USER_INSUFFICIENT_PERMISSIONS);
        }

        return chain.filter(exchange.mutate().request(decorateRequest(request, httpHeaders)).build());

完整代码:

注意:

登录接口是不需要进行拦截的,我们在登录接口的时候,如果登录成功,才会生成一个token(生成token以及代码中的反解析token在我上一篇文章的工具类有,同学们也可以去看一下)还有查询该用户对应的菜单权限API列表,然后会把相关信息存到redis中去。

总结:

最后:

如果大家觉得这篇文章对你们有所帮助的话,麻烦给个免费的赞赞,也祝各位码农在未来的IT道路上越走越远,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/374372.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

感悟笔记——2024年2月5日

今日阅读了一篇挺有深度的文章&#xff0c;主要阐述进入职场后的大部分人&#xff0c;是怎么逐渐沦为螺丝钉的?即使起点巨高的优等生&#xff0c;也不可避免。文章指路&#xff1a; 「优等生思维」正在将你变成「螺丝钉」和「老黄牛」从小到大&#xff0c;我一直都是那个「别…

随记-Java项目处理SQL注入问题

现象&#xff1a;http://10.xx.xx.xx:xx/services/xxService 存在SQL注入情况 加固意见&#xff1a; 需要对网站所有参数中提交的数据进行过滤&#xff0c;禁止输入“"、"xor"、"or"、”--“、”#“、”select“、”and“等特殊字符&#xff1b;所有…

专业排版设计软件:QuarkXPress 2024 for mac中文激活版

QuarkXPress 2024 for Mac是一款功能强大、易于使用、高质量输出的专业排版软件。无论您是出版业的专家还是初学者&#xff0c;都可以通过QuarkXPress 2024轻松创建出令人惊叹的出版物。 软件下载&#xff1a;QuarkXPress 2024 for mac中文激活版下载 QuarkXPress 2023 for Mac…

游戏后端如何实现服务器之间的负载均衡?

在当今的游戏行业中&#xff0c;随着游戏用户数量的不断增加&#xff0c;如何实现服务器之间的负载均衡成为了一个亟待解决的问题。游戏后端作为游戏的重要组成部分&#xff0c;承载着游戏逻辑处理和数据存储等功能&#xff0c;因此游戏后端的负载均衡问题尤为重要。本文将详细…

FINN: 使用神经网络对网络流进行指纹识别

文章信息 论文题目&#xff1a;FINN: Fingerprinting Network Flows using Neural Networks 期刊&#xff08;会议&#xff09;&#xff1a;Annual Computer Security Applications Conference 时间&#xff1a;2021 级别&#xff1a;CCF B 文章链接&#xff1a;https://dl.ac…

centos7的git使用方法

下载git yum install git git克隆 git clone https...(图片中复制的内容) git提交到远程仓库 git add filename git commit -m "提交日志" git push git首次使用要配置邮箱和用户名 查看提交日志 git log 查看当前提交状态 git status

JAVASE进阶:一文精通Stream流+函数式编程

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;JAVASE进阶&#xff1a;源码精读——HashMap源码详细解析 &#x1f4da;订阅专栏&#xff1a;JAVASE进阶 希望文章对你们有所帮助…

【第三十五节】idea项目的创建以及setting和Project Structure的设置

项目创建 Project Structure的设置 点击file ~ Project Structure 进入

RBAC权限控制实现方案

上一文章讲述了利用RBAC实现访问控制的思路&#xff08;RBAC实现思路&#xff09;&#xff0c;本文主要详细讲解利用vuex实现RBAC权限控制。 一、准备工作 从后台获取到权限对照表&#xff0c;如下&#xff1a; 1、添加/编辑楼宇 park:building:add_edit 2、楼宇管理 pa…

3. ⼤语⾔模型深度学习背景知识

1. LLM⼤语⾔模型⼀般训练过程 #mermaid-svg-8kci1fjEPiVolPue {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-8kci1fjEPiVolPue .error-icon{fill:#552222;}#mermaid-svg-8kci1fjEPiVolPue .error-text{fill:#5522…

管理类联考-复试-全流程演练-导航页

文章目录 整体第一步&#xff1a;学校导师两手抓——知己知彼是关键学校校训历史 导师你对导师的研究方向有什么认知。 第二步&#xff1a;面试问题提前背——押题助沟通自我介绍——出现概率&#xff1a;100%为什么选择这个专业&#xff1f;今后如何打算&#xff1f;你认为自己…

最小覆盖子串[困难]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给你一个字符串s、一个字符串t。返回s中涵盖t所有字符的最小子串。如果s中不存在涵盖t所有字符的子串&#xff0c;则返回空字符串"" 。 对于t中重复字符&#xff0c;我们寻找的子字符串中该字符数量必须不少于t中该字符数量…

AI新工具(20240206) Qwen1.5;法唠;Boximator 是由字节跳动研究团队开发的创新视频生成工具;秒画 等

Qwen1.5 - Qwen1.5更新了六种尺寸的基础和聊天模型&#xff0c;并在Hugging Face转换器集成了其代码&#xff0c;以提升开发者体验&#xff0c;并支持多种语言和长上下文处理。 Qwen1.5是一个大规模语言模型的最新迭代&#xff0c;它由Qwen团队开发。这个更新在中国新年前夕发…

QT exec阻塞UI

connect必须放在exec前

尚硅谷 Java 基础实战—Bank 项目—实验题目 3

实验题目 修改 withdraw 方法以返回一个布尔值&#xff0c;指示交易是否成功。 实验目的 使用有返回值的方法。 提示 修改 Account 类 修改 deposit 方法返回 true&#xff08;意味所有存款是成功的&#xff09;。修改 withdraw 方法来检查提款数目是否大于余额。如果amt小…

idea2023创建spring项目无法选择Java8

idea2023创建spring项目无法选择Java8 今天下载了新版的idea 2023.3.2&#xff0c;但是在创建springboot项目的时候只能选择Java17和Java21&#xff0c;没法选择其他的版本。 使用下面阿里云的地址替换Server URL中的start.spring.io的地址即可 https://start.aliyun.com/替…

OnlyOffice:释放无限创意,打造高效协作新体验

Onlyoffice &#x1f496;前言一、&#x1f4ab;开发者版本介绍二、&#x1f4ab;开发者版本特点三、&#x1f4ab;最新版重磅来袭&#xff0c;8.0版本介绍1.显示协作者头像2.插件 UI 界面更新 四、✨Windows部署ONLYOFFICE1.安装Erlang2.安装RabbitMQ3.安装Redis4.安装Postgre…

标准库 STM32+EC11编码器+I2C ssd1306多级菜单例程

标准库 STM32EC11编码器I2C ssd1306多级菜单例程 &#x1f4cc;原创项目来源于&#xff1a;https://github.com/AdamLoong/Embedded_Menu_Simple&#x1f4cd;相关功能演示观看&#xff1a;https://space.bilibili.com/74495335 单片机多级菜单v1.2 &#x1f449;本次采用的是原…

Postman发送带登录信息的请求

环境&#xff1a;win10Postman10.17.7 假设有个请求是这样的&#xff1a; RequiresPermissions("tool:add") PostMapping(value"/predict") ResponseBody /** * xxx * param seqOrderJson json格式的参数 * return */ public String predictSampleIds(Req…

071:vue中过滤器filters的使用方法(图文示例)

第071个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使用&#xff0c;computed&a…