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

news2024/11/15 11:00:24

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道路上越走越远,谢谢!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【chromium】vs2022 中 clang构建

vs2022 “D:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\devenv.exe”看起来没装安装 clang 组件 vs2019 装的是12:报错了 Build started... 1>------ Build started: Project: ebBase, Configuration: Debug Win32 ------ 1>d:\Program Files…

【并发编程】手写线程池阻塞队列

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;并发编程 ⛺️稳重求进&#xff0c;晒太阳 示意图 步骤1&#xff1a;自定义任务队列 变量定义 用Deque双端队列来承接任务用ReentrantLock 来做锁并声明两个条件变量 Condition fullWai…

AE2023 After Effects 2023

After Effects 2023是一款非常强大的视频编辑软件&#xff0c;提供了许多新功能和改进&#xff0c;使得视频编辑和合成更加高效和灵活。以下是一些After Effects 2023的特色功能&#xff1a; 新合成预设列表&#xff1a;After Effects 2023彻底修改了预设列表&#xff0c;使其…

《权力的游戏》AI创作大电影 震撼来袭

《权力的游戏》AI创作大电影 震撼来袭 Westeros, a world of power, intrigue, and dark magic. In the frozen North, the army of the dead marches towards Winterfell. The Seven Kingdoms are divided by war and bloodshed, as the Great Houses battle for supremacy.…

必看!嵌入式基于UART的通信协议-RS232、RS485协议解析

这两种都是串口通讯的变种&#xff0c;为了提升串口通信的距离和稳定性。通常来说&#xff0c;正常的串口通信使用的是TTL电平&#xff0c;即高电平为2.4-5V&#xff0c;低电平为0-0.4V。高低电平之间的范围很小&#xff0c;如果有静电或者其他外界的干扰&#xff0c;很快会将低…

了解这六种最佳移动自动化测试工具吗?

最好的移动自动化测试工具 在本文章关于移动应用程序测试的这一部分中&#xff0c;我们将研究 2023 年 6 种最佳移动自动化测试工具。 1、Appium Appium 是一个非常流行的开源自动化测试框架&#xff0c;支持各种操作系统的自动化。它可以与本机、混合和移动 Web 应用程序一…

春运开始,北斗卫星助力盲区来车预警提示

春运开始&#xff0c;北斗卫星助力盲区来车预警提示 近期春运开始&#xff0c;高德地图启动了2024年的“温暖回家路”服务计划&#xff0c;通过数字化服务创新保障春运出行。除了具备自学习能力的新能源导航首发亮相外&#xff0c;还重点升级了盲区会车预警服务。在山区弯道、…

docker复习笔记01(小滴课堂)安装+部署mysql

查看内核版本。 关闭防火墙&#xff1a; 查看docker版本&#xff1a; 下载阿里yum源&#xff1a; 再看一下yum版本都有哪些&#xff1a; 我们可以看的docker-ce了。 安装它&#xff1a; 设置docker服务开机启动&#xff1a; 更新日志文件&#xff1a; 启动docker&#xff1a; …

ETL是什么,有哪些ETL工具?就业前景如何?

ETL是什么 ETL&#xff08;Extract-Transform-Load&#xff09;&#xff0c;用来描述将数据从来源端经过抽取(extract)、转换(transform)、加载(load)至目标端的过程。ETL一词较常用在数据仓库&#xff0c;但其对象并不限于数据仓库。它可以自动化数据处理过程&#xff0c;减少…

2024/2/5总结

微信小程序 监听对象中所有属性的变化 如果某个对象中需要被监听的属性太多&#xff0c;为了方便&#xff0c;可以使用 通配符 ** 来监听 对象中所有属性的变化 什么是纯数字字段 概念&#xff1a;纯数字字段指的是那些不用于界面渲染的 data 字段。 好处&#xff1a;提升界面…

2024.02 国内认知大模型汇总

概述 大模型&#xff0c;又称为大规模机器学习模型&#xff0c;是一种基于大数据的人工智能技术。它通过深度学习和机器学习的方法&#xff0c;对大量数据进行训练&#xff0c;以实现对复杂问题的高效解决。大模型技术在语音识别、图像识别、自然语言处理等领域有着广泛的应用…

sqli.bypass靶场本地小皮环境(1-5关)

1、第一关 http://sqli.bypass/index1.php 单引号报错id1 双引号正常id1&#xff0c;应该是单引号闭合 id1--注释符用不了&#xff0c;%20和都用不了 %0a可以用 没有报错&#xff0c;用布尔盲注&#xff0c;POC&#xff1a;id1%0aand%0asubstr(ss,1,1)s%0aand%0a11 脚本跑数…

JavaScript流程控制详解之顺序结构和选择结构

流程控制 流程控制&#xff0c;指的是控制程序按照怎样的顺序执行 在JavaScript中&#xff0c;共有3种流程控制方式 顺序结构选择结构循环结构 顺序结构 在JavaScript中&#xff0c;顺序结构是最基本的结构&#xff0c;所谓的顺序结构&#xff0c;指的是代码按照从上到下、…

【python数据分析基础】—dataframe中index的相关操作(添加、修改index的列名、修改index索引值等)

文章目录 前言一、添加、修改index的列名二、修改index索引值 前言 本文主要讲dataframe结构中index的相关操作&#xff0c;index相当于是数据表的行。 一、添加、修改index的列名 新建一个dataframe表&#xff0c;我们可以自定义index的值&#xff0c;如下&#xff1a; imp…

Webpack源码浅析

webpack启动方式 webpack有两种启动方式&#xff1a; 通过webpack-cli脚手架来启动&#xff0c;即可以在Terminal终端直接运行&#xff1b; webpack ./debug/index.js --config ./debug/webpack.config.js通过require(webpack)引入包的方式执行&#xff1b;其实第一种方式最终…

sqli-labs-master靶场训练笔记(38-53|boss战)

2024.2.4 level-38 &#xff08;堆叠注入&#xff09; 这题乍一看感觉又是来卖萌的&#xff0c;这不是和level-1一模一样吗 然后仔细看了一下源代码&#xff0c;根据 mysqli_multi_query 猜测这题的本意应该是堆叠注入 mysqli_multi_query() 是 PHP 中用于执行多个 SQL 查…

Sysbench 性能测试(小白快速上手)

文末有惊喜哦 &#xff01; Sysbench 介绍 Sysbench 是一个在Linux系统上进行性能测试和基准测试的工具。它可以用于评估计算机系统的各种性能指标&#xff0c;如 CPU 性能、内存性能、文件 I/O性 能和数据库性能等。Sysbench 提供了多种测试模式和选项&#xff0c;可以帮助用户…

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

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

EMC测试报告怎么看 PK、QP、AV

EMC测试报告怎么看 报告中的字母辐射报告1辐射报告2 测试条件 报告中的字母 1.PK.PEAK&#xff0c;是指峰值&#xff08;单位时间内的最高值&#xff09;&#xff1b; 2.QP&#xff08;QUASI-PEAK)是指准峰值&#xff08;单位时间内的平均值&#xff09;&#xff1b; 3.AV(AVE…

数据采集接口分类:数据采集、数据的采集有哪些?

中国的人工智能会面临着前所未有的发展机遇&#xff0c;她也将会以真正解决人类钢需载入史册&#xff0c;我们也期待着在天津跟在座的各位合作伙伴共同努力&#xff0c;真正的用人工智能建设美好世界。 API接口数据采集 主流电商数据采集 一、 什么是数据采集 确立一个算法模…