三、若依登录功能(按钮)前、后端代码详解

1、ruoyi-ui项目中login.vue的< el-input >标签讲解(前端)

	<el-input> 标签内属性介绍:
		<el-input
   			v-model="listQuery.orderId"  数据绑定
   			placeholder="orderId"  当输入框内容为空时的占位符
   			style="width: 200px;"  输入框宽度
   			class="filter-item"  class名称
   			@keyup.enter.native="handleFilter"  当按下回车时触发事件调用方法
   			@keyup.native="handleFilter" 当按钮回弹时触发的方法
 		>
 	</el-input>



<!--	@click与@keyup的区别	※※※
	1、其中@click用于绑定监听功能:
		<button @click="test1">test1</button>
  		<button @click="test2('abc')">test2</button>
  		<button @click="test3('abcd', $event)">test3</button>
 
 		 methods: {
      		test1(eve) {//test1函数没有参数,默认传递 $event 
        		alert(eve.target.innerHTML)  //test1
      		},
      		test2 (msg) { //test1函数有参数,传递该参数
        		alert(msg)  // abc
      		},
      		test3 (msg, event) { //有参数,如果想获取到enevt,则函数中需要写 $event
        		alert(msg+'---'+event.target.textContent)  // abcd---test3
      		}
   		}
	
	2、而@keyup用于按键修饰符:
		//按下enter时,执行方法test7
		<input type="text" @keyup.enter="test7">

		methods: {
      		test7 (event) {
        		console.log(event.keyCode)
        		alert(event.target.value)
      		}

		}
-->
<!--
	这里关于<el-input></el-input>的不理解,是因为还未学习ElementUI,关于该框架的学习见后文
-->

2、login.vue中在< el-input >标签内出现的< svg-icon >标签讲解(前端)

	<el-form-item prop="username">
    	<el-input
          v-model="loginForm.username"
          type="text"
          auto-complete="off"
          placeholder="账号"
        >
          <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
        </el-input>
    </el-form-item>
  1. 首先要明白< svg-icon >标签是一个全局组件,要在对应的icons/index.js中定义并注册成立全局组件;需要svg-sprite-loader的配合,并在config.js中填写相关配置;其图片文件都放在icons/svg文件夹下面。

  2. 如何使用< svg-icon >组件:

    2.1. 安装svg-sprite-loader依赖,而这个在若依的准备工作,运行npm install时就给安装好了。

    2.2. 配置vue.config.js:

	//开头
	const path = require('path')

	function resolve(dir) {
  		return path.join(__dirname, dir)
	}
	
	//这里只列一部分,具体配置参考文档
	module.exports = {
  		chainWebpack(config) {
    		config.plugins.delete('preload') // TODO: need test
   			config.plugins.delete('prefetch') // TODO: need test

   		 // set svg-sprite-loader
    		config.module
      			.rule('svg')
      			.exclude.add(resolve('src/assets/icons'))
      			.end()
    		config.module
      			.rule('icons')
      			.test(/\.svg$/)
     			.include.add(resolve('src/assets/icons'))
      			.end()
      			.use('svg-sprite-loader')
      			.loader('svg-sprite-loader')
      			.options({
        			symbolId: 'icon-[name]'
     			 })
      			.end()
      	//这里后面仍可以按需添加config.module操作
      	}
   }

   2.3. 创建相关文件及文件夹(以若依框架中为例):
          在这里插入图片描述
         在这里插入图片描述
           在这里插入图片描述
   2.4.在main/js中引入icons:
         在这里插入图片描述
   2.5.使用< svg-icon >组件(以若依框架中为例):

 这里< svg-icon >用在< el-input >< /el-input >内部,其中slot表示所引用图片显示的位置:prefix为input框之前,suffix为框尾部;icon-class表示它引用的是src/assets/icons/svg下的哪一张图片;class为对应css中所定义的样式(同时注意base、head、html、meta、param、script、title这几个不支持使用class)。

<el-form-item prop="username">
        <el-input
          v-model="loginForm.username"
          type="text"
          auto-complete="off"
          placeholder="账号"
        >
          <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />    //suffix表示尾部
        </el-input>
      </el-form-item>

 框前:
                在这里插入图片描述
 class所用的input-icon样式:
                       在这里插入图片描述

3、登录按钮对应代码(前端 --> 后端 --> 前端)

	//登录按钮对应的keyup回车响应事件
	handleLogin() {
      	this.$refs.loginForm.validate(valid => {
        	if (valid) {
          	this.loading = true;
          	//如果勾选了“记住密码”选项,就把相关信息存到Cookie中
          	if (this.loginForm.rememberMe) {
            	Cookies.set("username", this.loginForm.username, { expires: 30 });
            	Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });
            	Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });
          	} else {
          	//如果没有勾选“记住密码”选项,就把相关信息在Cookie中移除
            	Cookies.remove("username");
            	Cookies.remove("password");
            	Cookies.remove('rememberMe');
          	}
          	this.$store.dispatch("Login", this.loginForm).then(() => {
            	this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
          	}).catch(() => {
            	this.loading = false;
            	if (this.captchaEnabled) {
              		this.getCode();
            	}
          	});
        }
      });
    }

 关于$store.dispatch方法介绍如下:点我,其中“Login”追溯过去,可以发现其位置与内容如下图所示:
         在这里插入图片描述
 其功能大致为获取表单信息,然后又封装了一个对象Promise,用于实现异步处理,因为Promise中又调用了另一个js中的Login函数,如下图所示:
         在这里插入图片描述
 上图将传过来的数据封装到data中,然后return表示的是带着data数据对url进行post请求,如下图所示,便是在前端抓包抓到的login.js文件中data数据内容:
  在这里插入图片描述
 下图为return requset内容:
  在这里插入图片描述
 且通过上图可以看出,这里又使用了反向代理操作,映射到8080(一看到return request操作,就要想到(一)中讲解的理论请求后端,结果请求前端的反向代理操作)。并且可以在后端找到login注解所在函数位置。
在这里插入图片描述
 此时若在AjaxResult ajax = AjaxResult.success();处打一断点,你会发现此时login函数所传入的 LoginBody 变量的内容,正是前端所填写与生成的内容:
   在这里插入图片描述
(其中AjaxResult ajax = AjaxResult.success();为SpringBoot [后端]的通用返回类,以此来返回状态码信息)

3.1、在后端的login函数中接收到前端传过来的数据后,后端要做哪些事清呢?大体上可总结为三件事:

  1. 验证校验码:
    在longin函数中调用了一个封装函数loginService.login()来进行验证操作码方式
    在这里插入图片描述
    点进去后可发现其函数代码如下问所示:
public void validateCaptcha(String username, String code, String uuid)
    {
    	//用于获取验证码开关
        boolean captchaEnabled = configService.selectCaptchaEnabled();//用于获取验证码开关
        if (captchaEnabled)
        {
        	//这一步是通过使用固定前缀与uuid的组合,使其能够在Redis中找到对应的缓存键值
            String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
            //获取缓存键值对应的对象
            String captcha = redisCache.getCacheObject(verifyKey);
            //删除Redis缓存中对应缓存键值的对象
            redisCache.deleteObject(verifyKey);
            //当Rdeis中对应缓存对象为空时(即存入Redis后超过2分钟)
            if (captcha == null)
            {
            	//execute是与线程相关,这句用于异步记录日志,使用工厂方法,可实现解耦和,此句与下面的抛出过期异常可同时执行
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
                //抛出过期异常
                throw new CaptchaExpireException();
            }
            //当code值与captcha值不相等时
            if (!code.equalsIgnoreCase(captcha))
            {
            	//同上面所讲(业务层面需要)
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
                throw new CaptchaException();
            }
        }
    }

 这里之所以没有对正常情况进行处理,是因为若正常他就会直接往下面走,执行下一个验证用户名和密码操作;而不是被抛出的异常而打断该方法进程

  1. 验证用户名和密码:
    2.1:进行完验证码的校验操作后,
    进行登录的前置校验,目的是将用户名与密码中那些不合乎规定的直接排除在外,代码如下所示:
 /**
     * 登录前置校验
     * @param username 用户名
     * @param password 用户密码
     */
    public void loginPreCheck(String username, String password)
    {
        // 用户名或密码为空 错误
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
            throw new UserNotExistsException();
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // IP黑名单校验
        String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
            throw new BlackListException();
        }
    }

  2.2:而后使用Spring Security进行用户验证(本质就是设置Filter过滤器),代码如下所示:

// 用户验证
        Authentication authentication = null;
        try
        {
        	//这里是创建一个UsernamePasswordAuthenticationToken对象,并将username与password进行赋值操作,就是进行一个包装操作
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            //
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        }
        finally
        {
            AuthenticationContextHolder.clearContext();
        }
        //如果成功,异步写操作日志,是写道sys_logininfo这张表中
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        //拿到登录用户
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        //记录账户信息,写到日志中;这一步修改的是数据库中sys_user表中的login_date这一项
        //且这里你对recordLoginUnfo一步步追踪,可发现他获取到了userid、ip以及登陆时间;
        recordLoginInfo(loginUser.getUserId());

  2.3:校验用户名和密码中部分知识点讲解:
   2.3.1:在recordLoginInfo(loginUser.getUserId());如何获取的Ip?(Spring Boot项目在不同文件夹下,两种调用java类的方式
    在SysLoginService.java文件中可遭到recordLoginInfo的代码,如下图所示:
      在这里插入图片描述
    再追溯过去,可发现他是对原方法进行了一层封装,在IpUtils.getIpAddr()中通过ServletUtils工具类中的getRequest()方法获取到了IP。
        在这里插入图片描述
    但是!!你会发现IpUtils.java文件是在ruoyi-common文件夹中的,那么在ruoyi-admin中的SysLoginController.java是如何调用到IpUtils.Java这个工具类的呢?
      在这里插入图片描述
    答案在ruoyi-admin中的pom文件中,已经提前对ruoyi-framework、ruoyi-quartz以及ruoyi-generator另外三个包配置了依赖,因而在SysLoginController.java中可以调用ruoyi-framwork包中的SysLoginService.java,又因为在SysLoginService.java有对ruoyi-common包的导入,因而可以调用IpUtils.Java工具类。(Spring Boot项目在不同文件夹下,两种调用java类的方式
    这里ruoyi-admin引入了上文提到的三个包,因而在Controller类使用时就要引入其他未添加依赖且需要的包:
     在这里插入图片描述
在这里插入图片描述
    这里ruoyi-framework仅引入了ruoyi-system包,因而在Service类使用时就要引入其他未添加依赖且需要的包:
在这里插入图片描述在这里插入图片描述

   2.3.2:更新用户信息
         在这里插入图片描述
    跟进去,代码如下所示:
         在这里插入图片描述
    这里是调用Mapper,当这个出现的时候,证明它开始与mybatis一起发力了,再跟进去会看到mybatis的相关代码(需在idea下载MyBatisX插件),如下图所示:
    在这里插入图片描述

  1. 生成Token
    3.1.在SysLoginService.java中return tokenService.createToken(loginUser);用于生成token
    3.2.跟进去可以发现下列代码:
    /**
     * 创建令牌
     *
     * @param loginUser 用户信息
     * @return 令牌
     */
    public String createToken(LoginUser loginUser)
    {
    	//获取uuid
        String token = IdUtils.fastUUID();
        //把获取到的uuid存到用户里面
        loginUser.setToken(token);
        //用于获取ip、浏览器和os等,并存到用户里面(跟进去发现的)
        setUserAgent(loginUser);
        //刷新token令牌有效期,并将用户登陆信息loginUser存到Redis中
        refreshToken(loginUser);
		
		//这里的claims是jwt中加载所用到的用户信息
        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.LOGIN_USER_KEY, token);
        //用于从数据声明生成令牌,生成点进createToken中会发现调用的是jwt,如下面代码所示
        return createToken(claims);
    }



    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private String createToken(Map<String, Object> claims)
    {
        String token = Jwts.builder()
                .setClaims(claims)
                //用这个算法将信息生成字符串(加密操作)
                .signWith(SignatureAlgorithm.HS512, secret).compact();
        return token;
    }

   最终生成的token通过ajax返回前端。

3.2、handLogin()方法流程小总结

  首先看到前端对应返回的代码处,跟着Login这个action点进去
在这里插入图片描述
 会有下图所示代码:
               在这里插入图片描述
 其中的login()方法你依次追进,会发现如下代码:
                  在这里插入图片描述
                在这里插入图片描述
 对应着后台调用的方法,因而其方法内的 setToken(res.token)(经点进去可发现)是用于将token存到Cookies中(因为将token令牌存至了Cookies中,因而此用户在30min内再次登录【不点击退出的那种登录】,浏览器会自动登录此用户,无需输入密码验证码)。

3.3、那么回到前端后,前端再如何运行呢?

    在这里插入图片描述
 红框1中所标内容便是带着前台数据到后台进行操作,而红框2中便是后端在对表单信息内容全部检验合格后进行的操作(原因:从前面操作可看出若是验证码校验和用户校验不合格时,会抛出异常,因而也就走不到这里),即跳转到下一个页面,流程如下图所示:
在这里插入图片描述

3.4、由上述过程中进而衍生出了一个问题,watch()监听方法中route的值是如何得到的?

 watch()监听代码:

  watch: {
    $route: {
      handler: function(route) {
          console.log("route的值:   " + route);
          console.log("route.query 和 route.query.redirect 的值:   " + route.query +"   " + route.query.redirect);
        this.redirect = route.query && route.query.redirect;
      },
      immediate: true
    }
  },

 首先,我们知道$route是获取当前路由信息对象,包含路由的状态信息、URL解析得到的信息,还有URL匹配到的路由信息。其次route.query表示URL查询参数(若没有查询参数,为空),如下图所示,可看到这里的route.query的值为\index
在这里插入图片描述
      在这里插入图片描述
(在查看URLEncode编码之后的数据可以知晓 ‘ %2F ’ 就表示 ’ / ')

3.5、那么URL路由中%2Findex的值是从哪里开始定义的呢?(全局路由守卫和判定)

(若依框架的前端路由控制的核心是在src/permission.js中)
 首先找到了ruoyi-ui/src/permission.js中,增加这么一句:
    在这里插入图片描述
 会发现这里的to.fulPath便是URL中/login?redirect=%2Findex

        在这里插入图片描述

permission代码及解读注释如下:

 (注意:当未登陆过,首次打开login.vue页面时,是执行if ( getToken() ) { } else{ }中else内的内容,因为token令牌是在登录按钮点击后才生成的 )

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import { isRelogin } from '@/utils/request'

//这玩意是进度条
NProgress.configure({ showSpinner: false })

const whiteList = ['/login', '/register']

//挂载路由导航守卫:这里是全局前置路由守卫————初始化及每次路由切换之前调用
router.beforeEach((to, from, next) => {
	//to  将要访问的路径
	//from  从哪个路径跳转而来
	//next是一个函数,表示放行;next() 放行,next('/login') 强制跳转

  console.log(" default test:   " + to.fullPath);
  console.log(" to.path :   " + to.path);

  //请求路由进度条开始
  NProgress.start()
  
  //如果有token令牌(即登陆成功)
  if (getToken()) {
  	//设置页面标题
    to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
    /* has token*/
    //如果要访问登录页面,直接放行
    if (to.path === '/login') {
    	//放行至首页,这是取决于你的路由重定向路径
      next({ path: '/' })
      //请求路由进度条结束
      NProgress.done()
    }else {//如果要访问的不是登录页面 
    	//判断是否已经获取用户信息
      if (store.getters.roles.length === 0) {    //如果为0,表示未获取用户信息,需要重新获取
        //重新登陆弹窗
        isRelogin.show = true
        // 判断当前用户是否已拉取完user_info信息
        store.dispatch('GetInfo').then(() => {
          isRelogin.show = false
          //从vue中触发GenerateRoutes方法,获取后台返回的路由信息(也就是在这里获取到了路由的对应字符串)
          store.dispatch('GenerateRoutes').then(accessRoutes => {
            // 根据roles权限生成可访问的路由表(即上面两个方法完成后,动态添加到路由中,并渲染到页面上)
            router.addRoutes(accessRoutes) // 动态添加可访问路由表
            next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
          })
        }).catch(err => {
            //获取用户信息失败,清除token,跳转到登录页面进行重新登陆操作
            store.dispatch('LogOut').then(() => {
              Message.error(err)
              next({ path: '/' })
            })
          })
      } else {
        next()
      }
    }
  } else {		// 如果没有token令牌
    if (whiteList.indexOf(to.path) !== -1) {
      // 在免登录白名单,直接放行
      next()
    } else {
      next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
      //console.log(" default test:   " + to.fullPath);
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  //每次请求结束后,进度条结束
  NProgress.done()
})

 这里经在控制台输出语句检测到,首次进入此页面时,是进入到下图所示中放行的:
  在这里插入图片描述

 流程图所示:
在这里插入图片描述

4、GenerateRouters生成路由时,字符串如何通过过滤器转换为组件对象

以GenerateRouters方法中的const sidebarRoutes = filterAsyncRouter(sdata)为例,其filterAsyncRouter方法代码如下所示:

// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
    return asyncRouterMap.filter(route => {
        if (type && route.children) {
            route.children = filterChildren(route.children)
        }
        // 字符串变组件操作
        if (route.component) {
            // Layout ParentView 组件特殊处理
            if (route.component === 'Layout') {
                route.component = Layout
            } else if (route.component === 'ParentView') {
                route.component = ParentView
            } else if (route.component === 'InnerLink') {
                route.component = InnerLink
            } else {	//若route.component中的字符串与上面三个不匹配则进行下面的操作
                route.component = loadView(route.component)
            }
        }
        if (route.children != null && route.children && route.children.length) {
            route.children = filterAsyncRouter(route.children, route, type)
        } else {
            delete route['children']
            delete route['redirect']
        }
        return true
    })
}

上面代码中提到的loadView方法如下面所示:
 此函数可根据传过来的view内容,利用require对${view}进行替换后,形成一个组件

// 箭头函数的使用
export const loadView = (view) => {
    if (process.env.NODE_ENV === 'development') {
    	// 其中require是用于找组件的
        return (resolve) => require([`@/views/${view}`], resolve)
    } else {
        // 使用 import 实现生产环境的路由懒加载
        return () => import(`@/views/${view}`)
    }
}

// 将箭头函数转化为function函数
function loadView(view){
	return function(resolve) {require(['@/views/${view}'],resolve)}
}

5、关于登录流程中token的校验(利用过滤器)

流程如下(借用波波的图):

在这里插入图片描述

使用的技术栈

已知后台给前台返回数据时,使用的是Jwt给前台生成一个token值。
其中SysLoginService.java中使用的return tokenService.createToken(loginUser);,调用的下列函数方法:

    /**
     * 创建令牌
     *
     * @param loginUser 用户信息
     * @return 令牌
     */
    public String createToken(LoginUser loginUser)
    {
        String token = IdUtils.fastUUID();
        loginUser.setToken(token);
        setUserAgent(loginUser);
        refreshToken(loginUser);

        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.LOGIN_USER_KEY, token);
        return createToken(claims);
    }

而此处createToken的return中调用的createToken方法如下:

    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private String createToken(Map<String, Object> claims)
    {
        String token = Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, secret).compact();
        return token;
    }

因此可知最后使用Jwt来生成token。

token的校验

在登陆成功后,每一个请求发送到后台,都需要对token进行权限校验,若依框架对于token的校验是用过滤器实现的。(过滤器位于:ruoyi-framework/com/ruoyi/framework/security/filter包中的JwtAuthenticationTokenFilter,代码展示如下)

/**
 * token过滤器 验证token有效性
 * 
 * @author ruoyi
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
    @Autowired
    private TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException
    {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
        {
            tokenService.verifyToken(loginUser);
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        chain.doFilter(request, response);
    }
}

{
 从getLoginUser中追踪可发现使用了getToken方法,且对header使用了getHeader方法,(header使在TokenService.java中自定义的令牌标识),也就是对从前端传过来的request的头部信息中取到token值(这个值就是前置拦截器放入的)
在这里插入图片描述
 然后将token返回getLoginUser方法。
}
{
 随后在此方法中进行对token的解析操作
在这里插入图片描述
 这里获取的uuid正是前面的createToken方法使用String token = IdUtils.fastUUID();生成的uuid,并将uuid放到Redis中。
 然后 String userKey = getTokenKey(uuid);这里的getTokenKey是把uuid作为参数,拼接成想要的数据模式,然后使用userkey去Redis中拿值及用户的详细信息userLoginUser user = redisCache.getCacheObject(userKey);。(user值展示:)
在这里插入图片描述
}
通过上述操作,会得到LoginUser loginUser对象,随后便是对loginUser用户信息进行其他判断:
首先利用tokenService.verifyToken(loginUser);来验证token的有效期,然后 UsernamePasswordAuthenticationToken authenticationToken是对loginUser的token令牌时间进行一个刷新,最后执行chain.doFilter(request, response);,过滤器放行。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值