【开源风云】从若依系列脚手架汲取编程之道(三)

📕开源风云系列

  • 🍊本系列将从开源名将若依出发,探究优质开源项目脚手架汲取编程之道。
  • 🍉从不分离版本开写到前后端分离版,再到微服务版本,乃至其中好玩的一系列增强Plus操作
  • 🍈希望你具备如下技术栈
    • 🍎Spring
    • 🍍SpringMVC
    • 🍐Mybatis/Mybatis-plus
    • 🍅Thymeleaf
    • 🥝SpringBoot
    • 🍓Shiro
    • 🍏SpringSecurity
    • 🍌SpringCloud
    • 🍒云服务器相关知识
  • 本篇是分离版第一篇

在这里插入图片描述

  • 文章同时同步到我的个人站点🍊欢迎来访!
    • 🍎国内:https://www.linxiaoqin.netlify.app
    • 🍏国际:https://www.linxiaoqin.vercel.app

后期会考虑将项目和安全方面的文章迁移至vuepress,找一个好看的主题,视觉效果++!

1、环境准备

1.1、云服务器

  1. 云服务器的系统可以直接安装有宝塔Linux面板的系统

在这里插入图片描述

当然也可以自己安装Centos7.6,然后再安装宝塔面板

在这里插入图片描述

  1. 登录进宝塔选择安装即可

在这里插入图片描述

其中还需要安装 redis

在这里插入图片描述

  1. 设置MySQL数据库的root密码,同时新建数据库,访问权限选择所有人

在这里插入图片描述

  1. 使用Navicat连接服务器的数据库,并将 sql 语句刷入

在这里插入图片描述

注意:记得开两次安全组

  • 腾讯云控制台的防火墙开放3306端口
  • 宝塔面板的安全 - 开放 3306 端口
  1. 设置redis远程连接,并且必须设置密码

在这里插入图片描述

然后点击服务 - 重载配置,我们使用Another Redis Desktop Manager 工具连接一下 redis,看redis是否正常

在这里插入图片描述

  1. application-druid.yml里面更改连接的ip、数据库名、账号、密码
  2. application.yml里面更改 redis.host 为虚拟服务器的ip,密码为 redis 的密码

1.2、虚拟机环境

  1. 在VmWare上启动一个虚拟机Linux
  2. 安装Docker
  3. 拉取一个redis镜像
  4. 运行一个redis容器
docker pull redisdocker run -d -v redis_data:/data -p 6379:6379 --restart always redis:latest redis-server --appendonly yes# 查看正在运行的容器
docker ps
  1. 创建数据库,并执行若依sql语句
  2. application-druid.yml里面更改连接的数据库名、账号、密码
    • 当然:直接打开本地的 redis 也可以,本地连接的host为 localhost,并且不用设置redis的密码
  3. application.yml里面更改 redis.host 为虚拟服务器的ip

也可在application.ymllogback.xml里面更改日志的存放路径

  1. 启动后端项目:如下,则说明我们的redis、mysql这些环境都没什么问题!

在这里插入图片描述

  1. 将前端ruoyi-ui在WebStrom中打开
npm installnpm run dev
  1. 大功告成:访问 localhost:8080即可!

2、常见使用

2.1、axios发送get请求

我们可以看下例,打开F12开发者工具,点击网络 - 切换到Fetch/XHR,这样只会捕获网页中的ajax请求,当我们不输入参数的时候点击搜索,发现请求网址是:

  • http://localhost/dev-api/monitor/online/list

在这里插入图片描述

当我们带上参数,例如在登录地址处填写127.0.0.1,再点击搜索,发现请求地址是:

  • http://localhost/dev-api/monitor/online/list?ipaddr=127.0.0.1

ruoyi的前端对axios进行了封装,让我们发get请求或者是post请求更加方便了。ruoyi对axios的封装在下面文件中:

在这里插入图片描述

打开文件,可以看到它有三个显眼的方法,分别是**request拦截器、response拦截器和通用下载方法。**request拦截器对我们发送的请求进行了封装:

在这里插入图片描述

上图代码可以让get请求自动变为我们熟悉的形式http://xxxxx:port/aaa?key1=value1&key2=value2,也就是进行一个 query 拼接,那么如果我们自己发送 get 请求要怎么发呢?

我们先来看下方若依代码是如何发送的:查询在线用户列表list方法,对于带参数的get请求,若依使用 params来获取用户输入的内容,随后一起发送给后端接口。

import request from '@/utils/request'// 查询在线用户列表(带参数的get请求)
export function list(query) {return request({url: '/monitor/online/list',method: 'get',// 参数要通过 params 传递  params: query})
}// 获取验证码(不带参数的get请求)
export function getCodeImg() {return request({url: '/captchaImage',headers: {isToken: false},method: 'get',timeout: 20000})
}

现在我们可以借助若依的封装自己发送get请求了,我们首先导入import request from '@/utils/request'导入若依封装的request.js,之后按照如下代码修改为自己的接口进行发送请求:

import request from '@/utils/request'// 查询在线用户列表(带参数的get请求)
export function list(query) {return request({url: '/monitor/online/list',method: 'get',// 参数要通过 params 传递  params: query})
}

那么发送的后端接口是/monitor/online/list,我们在 IDEA - 编辑 - 查找 - 在文件中查找这个接口(若查找不到,可以查找monitor/online),可以找到后端的接口方法:

在这里插入图片描述

提问Tips:话说若依的这个登录监控是怎么实现的呢?

  • 回答:若依的在线用户存在redis中,每次一个人登录,就会把它的登录信息存在redis中,当我们去查询在线用户,无非就是去redis中取一下有哪一些用户罢了。也就是有几个token,就说明有几个在线用户。

我来看一下Another Redis Desktop Manager看一下redis的信息:

在这里插入图片描述

查看发现,键是login_tokens:uuid,值是 json 对象,我将其摘出来看一下,如下在redis中存了很多信息

{// java类型"@type": "com.kuang.common.core.domain.model.LoginUser",// 浏览器类型"browser": "Chrome 12",// 部门ID是103"deptId": 103 L,// 过期时间"expireTime": 1717569585545,// ip地址"ipaddr": "127.0.0.1",// 登录位置"loginLocation": "内网IP",// 登录时间"loginTime": 1717567785545,// 操作系统"os": "Windows 10",// 用户权限"permissions": Set["*:*:*"],// 登录token"token": "520a22eb-f247-46a6-a0f7-23e00d7520bd","user": {"admin": true,"avatar": "","createBy": "admin","createTime": "2024-04-16 22:36:47","delFlag": "0","dept": {"ancestors": "0,100,101","children": [],"deptId": 103 L,"deptName": "研发部门","leader": "若依","orderNum": 1,"params": {"@type": "java.util.HashMap"},"parentId": 101 L,"status": "0"},"deptId": 103 L,"email": "ry@163.com","loginDate": "2024-06-05 14:09:33","loginIp": "127.0.0.1","nickName": "若依","params": {"@type": "java.util.HashMap"},"password": "$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2","phonenumber": "15888888888","remark": "管理员","roles": [{"admin": true,"dataScope": "1","deptCheckStrictly": false,"flag": false,"menuCheckStrictly": false,"params": {"@type": "java.util.HashMap"},"roleId": 1 L,"roleKey": "admin","roleName": "超级管理员","roleSort": 1,"status": "0"}],"sex": "1","status": "0","userId": 1 L,"userName": "admin"},"userId": 1 L,"username": "admin"
}

那么后端到底是怎么实现的呢,我们打个断点,以Debug形式启动后端,然后再次点击前端的在线用户菜单

在这里插入图片描述

查看断点,在我们点击在线用户菜单的时候,调用后端的/monitor/online/list方法,首先去redis缓存里面找到所有的key,并且这个key是以CacheConstants.LOGIN_TOKEN_KEY也就是login_tokens:为前缀进行匹配的,我们点击步过进入下一行,然后看我们的keys是什么,我们就恍然大悟了,这个就是redis的以login_tokens:为前缀的键key。

在这里插入图片描述

我们继续步过,遍历这个key,根据这个key找到一个user对象,做一系列判断,最后将这个 user 对象的信息返回给前端,由前端进行展示。

在这里插入图片描述

提问Tips: 点击在线用户菜单的时候从 redis 缓存中取 key,根据key遍历出登录用户,那么用户信息是啥时候存到 redis 中的呢?

  • 回答:是登录的时候存到 redis 中的,登录一个用户则存一个 redis

在IDEA中按两下shift键,搜索RedisCache,里面有个方法设置缓存对象setCacheObject,包括设置缓存的键、缓存的值、过期时间、时间单位

在这里插入图片描述

同时对于超时时间的设置可以在application.yml里面设置 token 令牌的有效期

在这里插入图片描述

好的接下来我们回头继续看RedisCache里面的设置缓存对象setCacheObject方法,在方法上右键 - 查找用法,仔细找找可以找到在TokenService类里面有设置 loginUser 缓存。这就是在登录的时候把登录用户的信息存入 redis 了

在这里插入图片描述

提问Tips:若依的强退功能是如何实现的呢?

  • 其实是把 redis 登录用户的信息删除掉了,若依前端的任何菜单按钮请求,都需要携带 token,后端拦截器会验证这个 token 是否有相关权限,如果没有 token 则请求会失败

如下图我们强退用户,会发现发送了:http://localhost/dev-api/monitor/online/520a22eb-f247-46a6-a0f7-23e00d7520bd 请求,这个后面跟着一串乱序是什么呢?是token吗?

在这里插入图片描述

在后端查看强退用户的接口方法,发现其实是删除了login_tokens:uuid,也就是上面的乱序其实是 uuid,我们之前讲过redis 中的keylogin_tokens:uuid所对应的值里面有登录用户的很多信息,包括登录的 token,那么这里直接将键删了,对应的值也就删了。

在这里插入图片描述

同时在 redis 中看,已经看不到 login_tokens的键值对了,可见思想是正确的。

在这里插入图片描述

2.2、axios发送post请求

那么如何自己发送Post请求呢?我们看下若依的封装:通读代码下来可以发现,如果是 Post 或者 Put 请求,方法首先将发送的 url、data、time封装成一个对象,首先对请求数据的大小进行限制,如果超出5M则无法提交。

前端防止重复提交:我们仔细看一下下方52行之后的代码,这块的代码是为了防止重复提交的,我们看一下逻辑,第一次进行提交的时候,第52行代码执行,sessionObj 一定不为空,因此直接执行第56行代码,拿到请求地址、请求数据、请求时间,当请求数据相同、请求地址相同、请求时间小于间隔时间1s,则抛出 数据正在处理,请勿重复提交。其他情况执行第65行代码。这个整体就是若依前端防止重复提交的逻辑

在这里插入图片描述

示例:如果我们点击多次确定提交,1s之内点击的话就会弹出:

在这里插入图片描述

其实若依给我们自己留了开关,可以自行设置是否需要防止重复提交,默认是防止重复提交

在这里插入图片描述

那么我们如果不需要防止,我们只需要在请求的时候加上headers: {repeatSubmit: false}

import request from '@/utils/request'// 注册方法(POST请求带参数)
export function register(data) {return request({url: '/register',headers: {// 不需要防止重复提交repeatSubmit: false},method: 'post',// POST请求带参数使用data字段  data: data})
}

2.3、通用下载方法

ruoyi许多地方用到下载,比如说字典管理,事实上很多 .vue文件里面都有导出按钮操作。我们这里定位到字典管理的vue文件

在这里插入图片描述

发现如上图代码,this 说明是从当前的组件实例或者Vue的组件实例去拿东西去了,但是download 方法肯定是没有的,所以一定是全局挂载了 download 这个方法。

main.js中可以找到在vue的原型上挂载了 download 方法,如果是 webstrom 编辑器,则ctrl+点击可以进入方法,就可以发现是吧 request.js 的通用下载方法挂载到这里了。挂载上去之后,就可以在其他地方随时使用 this.download(url,params,filename)方法来调用 download 方法。

在这里插入图片描述

  • 其中...this.queryParams 带参是我们选择的数据,比如我们选择两条数据导出

在这里插入图片描述

  • type_${new Date().getTime()}.xlsx 是文件名格式

请求的后端接口system/dict/type/export,在后端搜索接口路径,并且定位到文件

在这里插入图片描述

我们在导出方法加上断点,在字典管理菜单搜索字典名称为用户性别,进行导出,我们发现导出就只有用户性别这一列。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

提问Tips:这些表头是怎么生成的呢?

我们来看导出方法的第二个参数dictType就是我们筛选的数据,例如我们筛选用户性别,我们进去SysDictType 类,可以发现有@Excel的注解,这个注解可以决定我们导出的表格表头。

在这里插入图片描述

假如我们给字典名称加一个type = Excel.Type.IMPORT,也就是要求字典名称列只在导入的时候有,导出的时候没有,也就是导入的时候这个字典名称列必须有,但是导出的时候这个字典名称列没有。这样在字典菜单导出的时候,就不会出现字典名称列了。

在这里插入图片描述

同时我们可以关注状态列,readConverterExp="0=正常,1=停用",是决定数据库中的存储数据是0的话就是正常,是1的话就是停用。

我们也可以更改导出的.xlsx文件的大标题ExcelUtils类里面有导出excel表单的方法,其中StringUtils.EMPTY是传入了空字符串,我们可以自己设置为 "狂神天机平台",这样子导出的.xlsx文件的大标题就是 "狂神天机平台"了。

在这里插入图片描述

同时这里还有一个表格的Sheet名是 字典类型,这个是在哪里控制的呢?看如下代码:

在这里插入图片描述

2.4、前端向后端传参方式

ruoyi中存在restful风格传参和直接通过get或post携带的param、data传参。

我们点击参数设置,检查其ajax请求:

在这里插入图片描述

定位到后端代码:

在这里插入图片描述

示例:

/*** 根据参数编号获取详细信息*/
@PreAuthorize("@ss.hasPermi('system:config:query')")
@GetMapping(value = "/{configId}")
public AjaxResult getInfo(@PathVariable Long configId)
{return success(configService.selectConfigById(configId));
}

我们如果自己发送 restful 请求要怎么发呢,示例如下:

@GetMapping(value = "/abcd/{configId111}")
public void getInfo1(@PathVariable Long configId111)
{System.out.println("自己发送restful请求");
}

我们使用 apifox 进行请求发送,请求地址为:上方若依自己的接口地址http://localhost/dev-api/system/config/2,会出现401认证失败。

在这里插入图片描述

我们需要添加token发送,我们复制一下admin的token:

在这里插入图片描述

然后在 apifox 的 auth 字段中粘贴 token,就可以发送请求成功

在这里插入图片描述

那么我们就可以请求自己的接口:

  • http://localhost/dev-api/system/config/abcd/5201314

控制台打印出:自己发送restful请求 即为成功。

我们再来简单看一下通过get或post携带的param、data传参。

参数设置菜单为例,前端向后端发送带参数的 get 请求,我们找到后端对应接口打上断点。

在这里插入图片描述

我们在前端点击参数设置菜单,此时由于我们只是查询参数列表,没有携带参数,所以后端接收的参数都是null

在这里插入图片描述

点击恢复程序,这次我们输入参数名称再次搜索,会发现后端接收到了我们的参数。

在这里插入图片描述

在这里插入图片描述

假如我们增加搜索信息,也可以在后端接收到参数。

在这里插入图片描述

2.5、验证码开关原理

若依的验证码可以通过配置进行开关设置,我们来看验证码的前端代码,关键代码v-if="captchaEnabled"captchaEnabled 这个值是 true,那么验证码这部分代码界面显示,如果是flase,那么这部分界面不显示。

在这里插入图片描述

可是下滑代码发现 captchaEnabled 这个值写死了 true,奇怪,这是怎么回事呢?

在这里插入图片描述

肯定是有人改了这个值,我们继续下滑代码查看,发现了这么一句代码:

this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled;

这不就是被 res.captchaEnabled 修改了吗!也就是后端给前端传了一个值来修改的

在这里插入图片描述

我们刷新登录界面,抓包查看请求,果然可以看到后端返回了一个 captchaEnabled 为 false

在这里插入图片描述

那么后端怎么知道值是 true 还是 false 呢?我们数据库有张表sys_config,可以看到验证码开关显示的是 false,那么我们合理猜测后端是去数据库中查询了一下然后返回给前端的。

在这里插入图片描述

我们来验证一下,验证码的控制器中有生成验证码的方法,我们打个断点,然后退出系统,可以看到有个表达式configService.selectCaptchaEnabled() 赋值给了 captchaEnabled,我们右键 - 评估表达式,可以得出这个表达式是 false 赋值给了 captchaEnabled

在这里插入图片描述

那么它是怎么赋值的呢,我们ctrl+点击进入selectCaptchaEnabled()方法,然后点击进入方法的实现

在这里插入图片描述

我们给方法的实现打上断点,其实我们不难发现,它是去获取了sys.account.captchaEnabled的值,我们点击恢复程序,并且光标移动到打断点的那行,调试就会直接跳到我们打断点的这行代码。

在这里插入图片描述

评估一下表达式,值果然是 false,我们可以直接进入selectConfigByKey 方法看看它是怎么执行的,点击步入

在这里插入图片描述

可以发现它其实是从 redis 中获取 key 为 sys.account.captchaEnabled 的值,我们直接在 redis 中看,发现键对应的值果然是 false,至此,我们可以梳理一下整个验证码开关的原理了!

在这里插入图片描述

在第一次登录的时候,先从redis中获取sys.account.captchaEnabled验证码开关的值,第一次登录redis中获取不到,于是从数据库MySQL中获取,获取到后存入redis一份,这样第二次再获取就直接从redis中获取值了。流程图如下:

在这里插入图片描述

关于这块的代码,我们先在SysConfigController参数配置控制层里面看看,发现控制层没有明显的注释显示初始化参数配置,那么有可能在参数配置的Service层里面,仔细找一下,果然发现有:

在这里插入图片描述

如上代码,init初始化方法上方加了@PostConstruct注解,这个注解会在项目启动时执行该方法,实际上我们知道若依启动的时候存入redis的不止参数配置,还有字典配置和登录tokens。

在这里插入图片描述

那么我们举一反三,找到字典配置的Service层,果然也发现了初始化字典的方法

在这里插入图片描述

2.6、IP开关

application.yml里面可以配置获取 ip 地址开关,我们因为是本地运行,登录地址一直显示是 127.0.0.1,那么我们去官网示例看一下:

在这里插入图片描述

可以看到,若依将 ip 地址转化为了登录地点,这一点很有意思,若依可以控制是否转化ip地址到登录地址,这里如果大家是用云服务器部署的若依项目,并且在application.yml里面没有开启ip地址开关,那么登录地址是会显示xx xx,所以我们直接在IDEA - 编辑 - 查找 - 在文件中查找 xx xx

在这里插入图片描述

可以发现是一个静态常量,下面有个方法getRealAddressByIp 通过ip地址获取真实地址,可见这个方法就是用来将ip地址转化为真实地址的,右键 - 查找用法,会发现有个 recordLogininfor 记录登录信息的方法调用了ip地址转化的方法

在这里插入图片描述

那么又是谁调用了recordLogininfor记录登录信息的方法呢,我们在方法右键 - 查找用法,可以发现在登录方法login中调用了记录登录信息的方法。
在这里插入图片描述

仔细通读这个login登录方法,可以发现若依将账号密码不匹配也会记录日志,其余登录不成功通通捕获为一个异常返回给前端。

在这里插入图片描述

这个时候我们再回头看记录登录信息recordLogininfor方法,可以发现,其实若依在登录的时候,会获取登录用户的很多信息,包括操作系统、ip地址等,然后将这些信息封装好统一插入数据库中。

在这里插入图片描述

在我们点击登录日志菜单的时候,若依负责从数据库中读取这些信息并展示

在这里插入图片描述

提问Tips:那么到底是在什么时候将 ip 地址转换为真实地址的呢?

我们来看AddressUtils获取地址类,有几个 if 语句需要关注,内网不查询我们进入 internalIp 方法很容易看到如果是127.0.0.1,则统一返回内网IP

在这里插入图片描述

对于RuoYiConfig.isAddressEnabled() 方法,我们进入方法内部查看,发现RuoYiConfig类有个注解@ConfigurationProperties(prefix = "ruoyi") ,可以看出是在读取配置文件yml,并且前缀以 ruoyi开头

在这里插入图片描述

这样我们就知道若依支持用户配置ip地址开关的逻辑了,也就是我们在yml配置文件中配置值,RuoYiConfig配置类读取值,AddressUtils 获取地址类根据RuoYiConfig.isAddressEnabled() 的值来决定是否需要转化 ip 地址到真实地址。

提问tips:若依这么牛逼吗?通过ip地址得到我的省市地址,其实若依也是借助了http://whois.pconline.com.cn/ipJson.jsp?ip=xxx

假如我们输入https://whois.pconline.com.cn/ipJson.jsp?ip=14.145.8.136%22&json=true,得到如下json对象

在这里插入图片描述

我们来看若依代码如何做的,首先发送Get请求,将返回的字符串转换为Json对象,然后读取省份和城市返回给前端

在这里插入图片描述

2.7、前端页面的布局

ruoyi的前端页面目前存在两种情况,一种是依赖layout组件,一种是不依赖layout组件。

比如说登录页面,注册页面,404页面就不依赖于layout组件,layout组件实际上就是一个vue的组件。这种组件比较简单,就是跟写HTML基本也没啥两样。关于login.vue页面的对应组件树如下:

在这里插入图片描述

当我们成功登录,接下来基本上每一个页面都是出于Layout组件之下,也就是说,它是layout组件的子组件!

在这里插入图片描述

此时此刻的组件树如上。

  • Sidebar:对应左边菜单区。

  • Navbar:导航区。

  • TagsView:页签区。

  • RightPanel:右面板区,平常不可见。

  • AppMain:内容页面区,我们一般业务页面会出现在此区域。

下面我们写一个小小的案例来具体讲解一下如何新增自己的业务界面在AppMain区域中。

  1. 直接在views文件夹下面新增weather.vue

在这里插入图片描述

  1. 新建主类目
    • 路由地址可以随便填,其实也就是localhost/kuang/xxx,这个路由地址就是 kuang

在这里插入图片描述

  1. 新建菜单 :
  • 路由地址可以随便写,例如我写的是kuang_weather,之后会拼接上级菜单的路由,最后形成localhost:/kuang/kuang_weather
  • 组件路径不可以随便写,组件路径必须对应前端页面,我是在views文件夹下面新增weather.vue,所以组件路径默认从views目录读,即weather.vue ,后缀可写可不写。
  • 权限字符、路由参数后面再说,点击确定

在这里插入图片描述

  1. 这样我们的天气页面就完成了一部分,当我们把鼠标放到狂神天气处时,页面左下角会有我们的路由地址

在这里插入图片描述

2.8、JWT

[!NOTE]

好文推荐:阮一峰

那么若依是怎么使用JWT的呢?当我们登录的时候,会生成一个token,客户端存储在cookie里面,访问其他任何页面都需要这个token

在这里插入图片描述

问题一:不登录的时候有token吗?

  • 没有,所以只能在login页面,凡是想跳转其他界面,都被重定向到登录,硬生生让你登录。前端src/permission.js有全局前置路由,全局前置路由守卫:初始化的时候被调用、每次路由切换之前被调用。
  • 如果不在白名单路由里面,那么没有token的情况下访问其余路由就会被重定向到登录页面

在这里插入图片描述

  • 并且登录的路由会被若依记住,假如登录成功就会被允许输入的路由。例如在没有登录的情况下输入localhost:80/role,会被重定向为:http://localhost/login?redirect=%2Frole ,其中 %2是url编码,代表/

在这里插入图片描述

在这里插入图片描述

问题二:token是什么时候生成的?

  • 登录的时候生成的,在login控制器的service层代码中,有login方法,login 方法不仅仅生成了 token,还把登录的用户信息存到了 redis 中,键是login_tokens: + 当前的tokenId,tokenId是一个uuid,值是用户的各种信息。
  • 这样下次用户带着token来的时候,后端可以根据 tokenId 来 redis 中匹配

问题三,用户登录后,发请求是怎么自动带上token的?

  • 前端在登录的时候,存了一份token在cookie中

在这里插入图片描述

其中代码有一个setToken方法,里面就是将 token 放入 cookie的:

// 将token存入cookie
export function setToken(token) {return Cookies.set(TokenKey, token)
}

接下来如果要发请求,request.js会自动带上此 token,默认发送请求是带 token 的,给请求头里面加一个Authorization键,值是Bearer + token

在这里插入图片描述

// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
if (getToken() && !isToken) {config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}

我们来解释一下上面的代码,config.headers不为空就会取它的isToken属性,看是不是和 false相等,如果相等则 isToken 常量为 true。config.headers为空或者 undefined则不取它,而是取 {}大括号的isToken属性。

如果getToken获得token,并且!isToken是true,才会走if代码块的代码,给发送的请求头里面加一个Authorization键,值是Bearer + token

我们可以 debugger 一下,在 request.js 加上 debugger,并且在vue.config.js里面配置devtool: "source-map"

在这里插入图片描述

在这里插入图片描述

这样在登录页面就会debugger,可以点击查看调试。

在这里插入图片描述

问题四:我再次请求的时候带上了token,后端在哪问我带没带token呢?

其实后端有一个过滤器,总是在悄悄检查。在SecurityConfig里面加了过滤器,也可以说是拦截器,在UsernamePasswordAuthenticationFilter过滤器之前加了authenticationTokenFilter过滤器

在这里插入图片描述

我们打断点进行调试,在token过滤器的service层获取登录用户,我们查看service层获取登录用户方法的实现

在这里插入图片描述

发现获取用户身份信息getLoginUser方法首先获取请求携带的令牌,我们步入看看是怎么个获取法

在这里插入图片描述

我们发现获取请求token它先获取了请求头,这个请求头通过IDEA调试可以看到是Authorization

在这里插入图片描述

传入的请求头header,其实是通过配置文件yml注入的,我们ctrl+单机 header就可以发现了,这样就从请求头里面的键拿到对应的token值!

在这里插入图片描述

2.8.1、总结JWT

那么现在我们来总结一下若依的JWT

  1. 在登录的时候,token就会生成,并且将token存在redis中,存到redis中的键是login_tokens:uuid,值是登录用户的所有信息,所有信息里面也包含token,并且这个token和uuid是相同的。
  2. 后端将token返回给前端,前端将token存到cookie里面,下次发请求就会默认带上token,要想不带token也很简单,只要在请求头里面headers:{isToken: false},例如登录方法请求不需要带 token,代码如下
// 登录方法
export function login(username, password, code, uuid) {const data = {username,password,code,uuid}return request({url: '/login',headers: {isToken: false,repeatSubmit: false},method: 'post',data: data})
}

其实带token发请求的含义也就是请求头带上键为Authorization,值为token的载荷

  1. 后端在接收发送的请求时,会先在token过滤器里面拿到请求的token,而这个token又对应redis中的uuid,从而可以根据token拿到redis中登录用户的所有信息
  2. 将登录用户的token设置进SpringSecurity的安全持有者的上下文设置鉴权、认证

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

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

相关文章

基于zigbee的蔬菜大棚温湿度监测系统(论文+源码)

1 系统的功能及方案设计 本次基于zigbee的蔬菜大棚温湿度监测系统主要包括传感器节点、协调器节点和监控中心三个功能模块。 其中协调器节点:由cc2530作为主控芯片,负责接收终端一和终端二发送过来的温湿度数据,并将其通过ch340串行转USB输…

【王树森】RNN模型与NLP应用(8/9):Attention(个人向笔记)

前言 基于RNN的Seq2Seq模型无法记住长序列Attentnion机制可以大幅度提升Seq2Seq模型 Seq2Seq Model with Attention Attention可以让句子在逐步变长的时候不忘记前面的输入信息Attention还可以告诉Decoder应该关注哪一个状态优点:Attention可以大幅度提高准确率缺…

【Java】实体类Javabean

文章目录 前言一、实体类Javabean是什么?二、代码总结 前言 记录实体类的基本语法 一、实体类Javabean是什么? 其实就是一种特殊形式的类,这种类特殊点在于: 1、这个类中的成员变量都要私有,并且要对外提供相应的ge…

Dubbo ZooKeeper Spring Boot整合

依赖配置 1. Dubbo 起步依赖 Dubbo 是一款高性能的 Java RPC 框架&#xff0c;用于快速开发高性能的服务。 <dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>${dubbo.ver…

【功能自动化】使用HTMLTestRunner生成测试报告

配置环境&#xff1a; 部署webtours网站 准备数据 user.txt 在软件开发过程中&#xff0c;测试是非常重要的环节&#xff0c;通过测试可以验证代码的正确性和稳定性。而生成测试报告则是测试的一个重要环节&#xff0c;通过测试报告可以清晰地了解测试的结果、覆盖率等信息。…

第九届世界渲染大赛国内参赛者作品在哪里可以看?

第九届世界渲染大赛汇聚了全球顶尖的CG艺术家&#xff0c;其中国内选手的表现尤为引人注目。他们凭借独特的创意视角和精湛的技术&#xff0c;将浓郁的国风元素融入作品之中&#xff0c;为大赛增添了一抹独特的东方色彩。接下来&#xff0c;就让我们一探究竟&#xff0c;看看这…

datagrip链接sql server2005报错

错误信息 第一次报 DBMS: Microsoft SQL Server (no ver.) Case sensitivity: plainmixed, delimitedexact [08S01] 驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。错误:“The server selected protocol version TLS10 is not accepted by client pr…

C++学习笔记----6、内存管理(一)---- 使用动态内存(4)

3.6、多维自由内存空间上的数组 如果需要在运行时决定多维数组的维度&#xff0c;可以使用在自由内存空间上的数组。与一维动态分配的数组通过指针访问一样&#xff0c;多维动态分配的数组也可以通过指针访问。不同的地方在于在二维数组中&#xff0c;需要用一个指向指针的指针…

基于精益六西格玛管理方法进行生产线综合改善

生产线精益六西格玛改善是一个系统工程&#xff0c;只有对其进行系统的策划与组织&#xff0c;才能收到良好的改善效果。一般来说&#xff0c;需要成立一个专门的精益六西格玛推进组织&#xff0c;由其完成一系列的组织、准备工作。具体如下&#xff1a; &#xff08;1&#xf…

详解si5338 si53xx 设计使用及STM32 iic驱动设计

背景 在实际项目中经常使用si5338 si53xx&#xff0c;进行多路时钟的倍频以生成想要的时钟信号&#xff0c;但是针对si5338 si53xx设计使用缺少相关的资料&#xff0c;本文详解si5338 si53xx 设计使用及STM32 iic驱动设计&#xff0c;本文使用工程在项目中得到测试&#xff0c…

【pycharm】汉化及翻译插件

汉化插件 翻译插件 使用 选中右键翻译

通信算法之232: 无线发射功率和信号强度,常用单位dB、dBm、dBi和dBd介绍

[转载] 无线功率和信号强度的基本概念 在无线网络中&#xff0c;使用AP设备和天线来实现有线和无线信号互相转换。如下图所示&#xff1a; 有线网络侧的数据从AP设备的有线接口进入AP后&#xff0c;经AP处理为射频信号&#xff0c;从AP的发送端&#xff08;TX&#xff09;经过…

DCMM认证等级以及费用多少?

DCMM&#xff08;数据管理能力成熟度&#xff09;特别适合拥有较多数据量的组织、以及期望在初期就进行数据管理规范的组织。它旨在帮助组织对象发现自身问题&#xff0c;为数据管理能力的建设和提升指明方向。 DCMM将组织的数据管理划分为 8大能力域&#xff0c;细分为28个能…

如何将代理IP设置为ISP:详细指南

在当今互联网时代&#xff0c;代理IP已经成为许多用户保护隐私和提升网络体验的重要工具。而ISP&#xff08;Internet Service Provider&#xff09;的代理IP更是因为其高质量和稳定性备受青睐。本文将详细介绍如何将代理IP设置为ISP&#xff0c;让你在网络世界中享受更优质的上…

如何在D盘创建虚拟环境?包括安装PyTorch和配置PyCharm

摘要&#xff1a;本文首先在D盘创建了虚拟环境&#xff0c;然后在虚拟环境中安装了PyTorch&#xff0c;最后配置了pycharm的解释器。 1. 在 D 盘创建虚拟环境 打开Anaconda Prompt 输入conda info --envs查看当前已有环境 创建自己的虚拟环境&#xff0c;打算命名为py310&…

不同分辨率下页面自适应方法

首先在utils文件下新建一个js文件命名为screenSize.js 然后在需要做自适应的页面文件中引入import {screenSize} from ‘/utils/screenSize’ 最外层div中用ref命名 最后在mounted中使用该方法 记得style中给login宽1920px,高1080px

CPP继承(下)

目录 继承与友元 继承与静态成员 复杂的菱形继承及菱形虚拟继承 单继承 多继承 菱形继承 菱形继承的问题 虚继承 虚拟继承解决数据冗余和二义性的原理 继承的总结和反思 笔试面试题 继承与友元 友元关系不能继承&#xff0c;也就是说基类友元不能访问子类私有和保护…

高效易用的仓库进销存管理软件盘点,总有一款适合你!

本文将盘点10款主流的仓库进销存管理软件&#xff0c;为企业选型提供参考&#xff01; 仓库进销存管理软件就如同企业仓库的智慧大脑&#xff0c;能把企业的库存管理得井井有条。 有了它&#xff0c;首先能准确掌握库存情况&#xff0c;就像有了一双敏锐的眼睛&#xff0c;随时…

[SWPUCTF 2023 秋季新生赛]Pingpingping

这种是ctf中比较简单的一类题&#xff0c;主要解法基本上也就那些形式。 这道题我给它提出来主要是涉及了一下比较零散的知识点&#xff0c;觉得想要跟大家分享一下。 <?php highlight_file(__FILE__); error_reporting(0); $_ping $_GET[Ping_ip.exe]; if(isset($_ping…

新160个crackme -047-surre

运行分析 打开程序&#xff0c;标题显示Unreg未注册点击Try it&#xff0c;让我们选择一个文件&#xff0c;若选择文件无反应&#xff0c;不选择文件之后弹出错误提示 PE分析 Delphi程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 ida找到已注册字符串&#xff0…