Springboot实现登录功能(token、redis、登录拦截器、全局异常处理)
登录流程:
1、前端调用登录接口,往接口里传入账号,密码
2、根据账号判断是否有这个用户,如果有则继续判断密码是否正确
3、验证成功后,则是根据账号,登录时间生成token(用JWT)
4、将token存入Redis当中,用于token过期策略
5、将token和用户信息返回给前端
6、此后调用后端任何接口都会先判断发来请求里token是否存在、有效(拦截器实现)
7、然后继续接下来的正常调用
具体思路:
1、再登录接口中实现账号、密码的验证和token创建
2、实现一个拦截器,拦截除登录接口外的其他所有接口
3、再拦截器中从请求头中取出token对其进行正确性的验证
4、然后从redis<account,token>里取出属于这个账号的token,如果取的出来则说明这个用户登录状态没有过期,然后和取出来的token进行对比,如果不一样则说明同一账号再其他地方进行了登录,则被挤出登录状态。
5、再这整个判断过程中所产生的异常(没有登录状态,不存在这个用户....)都是由全局异常处理器进行捕获然后返回给前端。
pom文件要引的依赖:
<!--版本--> <properties><java.version>1.8</java.version><lombok.version>1.18.12</lombok.version><hutool.version>5.8.8</hutool.version><mybatis-plus.version>3.5.2</mybatis-plus.version><JWT.version>6.0</JWT.version></properties><!--JWT--><dependency><groupId>com.nimbusds</groupId><artifactId>nimbus-jose-jwt</artifactId><version>${JWT.version}</version></dependency><dependency><groupId>com.qcby</groupId><artifactId>qcby-common</artifactId><version>1.0-SNAPSHOT</version></dependency><!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>${hutool.version}</version></dependency>
对于token我使用了JWT,有一个token的工具类用于token的创建和 验证。(这个类里使用的redisUtil类大家可以从网上随便找一个redis工具类,绑定上自己的reids)
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import com.qcby.framework.common.exception.ServiceException;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.text.ParseException;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;@Component
public class TokenUtil {@ResourceRedisUtil redisUtil;/*** 创建秘钥*/private static final byte[] SECRET = "qngChengBoYa-realtimeWuIngWangJiaQiZhangYv".getBytes();/*** 生成token* @param account* @return {@link String}*/public String buildToken(String account) {try {/*** 1.创建一个32-byte的密匙*/MACSigner macSigner = new MACSigner(SECRET);/*** 2. 建立payload 载体*/JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject("login").claim("ACCOUNT",account).issueTime(new Date()).build();/*** 3. 建立签名*/SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet);signedJWT.sign(macSigner);/*** 4. 生成token*/String token = signedJWT.serialize();redisUtil.setEx(account,token,10,TimeUnit.MINUTES);return token;} catch (KeyLengthException e) {e.printStackTrace();} catch (JOSEException e) {e.printStackTrace();}return null;}/*** 校验token* @param token* @return*/public boolean verifyToken(String token) {try {SignedJWT jwt = SignedJWT.parse(token);JWSVerifier verifier = new MACVerifier(SECRET);/*** 校验是否有效*/if (!jwt.verify(verifier)) {return false;}/*** 获取载体中的数据*/String account = (String) jwt.getJWTClaimsSet().getClaim("ACCOUNT");//是否有if (Objects.isNull(account)){return false;}/*** 判断redis里是否有account为key的值,如果有* 判断token是否和redis里存的是是否一样,* 如果不一样说明已经有其他账号登录了,则回到登录页面* 如果一样,则给token续期*/if (redisUtil.hasKey(account)){String s = redisUtil.get(account);if (s.equals(token)){redisUtil.expire(account,10,TimeUnit.MINUTES);return true;}throw new ServiceException("422","有其他设备登录");}} catch (ParseException e) {e.printStackTrace();} catch (JOSEException e) {e.printStackTrace();}return false;}}
登录拦截器的实现,要先创建一个类并实现HandlerInterceptor这个接口,然后再创建一个拦截器的配置类令其实现WebmvcConfigurer这个接口,重新addInterceptors方法,再这个方法中将之前实现的登录拦截器给注册进去,并配置这个拦截器的拦截路径,拦截优先级等等。
/*** 请求拦截器* @author MI* @date 2023/10/03*/
@Component
public class LoginInterceptor implements HandlerInterceptor {private static Logger log = Logger.getLogger(LoginInterceptor.class);/**** 在请求处理之前进行调用(Controller方法调用之前)@param request@param response@param handler@return boolean@throws Exception*/@ResourceTokenUtil tokenUtil;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {/*** 从请求头中取出token,并判断其是否存在和合法*/String token = request.getHeader("token");if (token != null && tokenUtil.verifyToken(token)) {return true;}else {throw new ServiceException("100","还未登录");}}/**** 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)@param request@param response@param handler@param modelAndView@throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}/**** 整个请求结束之后被调用,也就是在DispatchServlet渲染了对应的视图之后执行(主要用于进行资源清理工作)@param request@param response@param handler@param ex@throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
import javax.annotation.Resource;
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {@ResourceLoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {/*** 登录拦截器* */registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/login").order(1);}
}
全局异常处理器,它能捕获到全部的异常(前提是要把异常抛出),所有异常都会在controller层反应出来,因为执行方法的原头在controller。我之前就是用try-catch处理,然后一直一直捕获不到,然后网上说正常的实现的全局异常器只能捕获到controller层的异常,所以拦截器里的异常捕获不到,这句话对也不对。拦截器里异常确实捕获不到,但只要咱把它抛出去就能再controller层显现了。
具体实现就是我们要加@ControllerAdvice注解, @ExceptionHandler根据这个注解具体绑定处理哪个异常。
/*** 全局异常处理器* @author MI* @date 2023/10/02*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler{/*** 自定义异常拦截器* @param req* @param e* @return {@link Result}*/@ResponseBody@ExceptionHandler(value =ServiceException.class)public Result exceptionHandler(HttpServletRequest req, ServiceException e){log.info("发送{}异常",e.getMessage());return Result.getBusinessException(e.getLocalizedMessage(),e.getCode());}@ResponseBody@ExceptionHandler(value =Exception.class)public Result exceptionHandler(HttpServletRequest req, Exception e){log.info("发送{}异常",e.getMessage());return Result.getBusinessException(e.getLocalizedMessage());}
}
Server层实现:
@Service
@Slf4j
public class LoginServiceImpl implements ILoginService {@ResourceUserMapper userMapper;@ResourceTokenUtil tokenUtil;@ResourceUserRoleMapper userRoleMapper;/*** 登录实现* @param loginDto* @return {@link LoginVo}*/@Overridepublic LoginVo login(LoginDto loginDto) {UserPo userPo = userMapper.selectOne(new LambdaQueryWrapper<UserPo>().eq(UserPo::getAccount, loginDto.getAccount()));if (userPo!=null){if (userPo.getPassword().equals(loginDto.getPassword())){/*** 构建token*/String token = tokenUtil.buildToken(userPo.getAccount());LoginVo loginVo = new LoginVo();loginVo.setToken(token);loginVo.setRoleId(userRoleMapper.selectOne(new LambdaQueryWrapper<UserRolePo>().eq(UserRolePo::getUserId,userPo.getUserId())).getRoleId());loginVo.setAccount(userPo.getAccount());return loginVo;}else{throw new ServiceException("422","密码错误");}}else {throw new ServiceException("422", "用户不存在");}}
}
后面的Controller、Mapper、实体层大家要根据自己的需求字段来进行具体实现,我就不贴出来了。
具体用法,像这个异常咱直接抛出来就行,全局异常处理类都能捕获,就不会继续往下执行了,它会把报错信息返回给前端:
如图所示:
相关文章:

Springboot实现登录功能(token、redis、登录拦截器、全局异常处理)
登录流程: 1、前端调用登录接口,往接口里传入账号,密码 2、根据账号判断是否有这个用户,如果有则继续判断密码是否正确 3、验证成功后,则是根据账号,登录时间生成token(用JWT) 4、将…...

AI工程化—— 如何让AI在企业多快好省的落地?
文章目录 前言内容简介读者对象专家推荐目录赠书活动 前言 作为计算机科学的一个重要领域,机器学习也是目前人工智能领域非常活跃的分支之一。机器学习通过分析海量数据、总结规律,帮助人们解决众多实际问题。随着机器学习技术的发展,越来越多…...
mysqld_multi测试
mysqld_multi测试 mysql版本:5.7.25-log 在OS上分别安装了两套mysql, data目录为/mysql/mysql3306、 /mysql/mysql3307 。 端口分别为3306 、3307 配置文件为: /mysql/mysql3306/my.cnf /mysql/mysql3307/my.cnf 参考文档: htt…...
MDC方式实现简单链路追踪
MDC 方式实现日志链路追踪 拦截器 package com.cdn.log.interceptor;import com.cdn.log.consts.CLogConst; import com.cdn.log.utils.IdUtil; import org.slf4j.MDC; import org.springframework.util.StringUtils; import org.springframework.web.servlet.ModelAndView; im…...

Linux深度学习:除基本命令操作外的实用操作
Linux深度学习:除基本命令操作外的实用操作 软件安装systemctl软连接日期、时区IP地址、主机名网络传输下载和网络请求端口 进程管理主机状态系统资源监控磁盘信息监控网络状态监控 环境变量上传、下载压缩、解压root用户、用户、用户组管理查看、修改权限控制 软件…...

app对接广告变现平台:影响app广告单价的4大因素
在移动应用开发者和媒体公司竞相寻求提高广告变现效率的今天,理解影响APP广告单价的关键因素至关重要。广告单价是广告收入的核心组成部分,它受多种因素的影响,直接关系到媒体的盈利能力。主要因素大概有以下几点:#APP广告变现# …...

【数字化转型】10大数字化转型能力成熟度模型01(IOMM)
一、前言 数字化转型是数据化能力建设的目标和价值,作为一个新兴的课题,目前为止并未出现一个统一的数字化转型成熟度模型。不同的企业和机构,根据自身的发展和认知,推出了自己的企业级或者准行业级标准。这些标准具有很强的参考意…...

2023腾讯云轻量应用服务器和普通服务器有什么区别?
腾讯云轻量服务器和云服务器有什么区别?为什么轻量应用服务器价格便宜?是因为轻量服务器CPU内存性能比云服务器CVM性能差吗?轻量应用服务器适合中小企业或个人开发者搭建企业官网、博客论坛、微信小程序或开发测试环境,云服务器CV…...

SSL证书是什么?1分钟get
在当今互联网世界中,保护数据的完整性和隐私性至关重要,由此,在网络数据安全保护领域,作为保护网络传输数据安全的SSL证书越来越频繁出现。那么你知道SSL证书是什么?SSL证书有哪些类型?SSL证书有什么用吗&a…...

3D打印机升级killpper
本来是想整台新机的,但是想想老机器4max也不能就此放弃,看了看视频,改装升级似乎也没有那么难。然后就是换了喷头、皮带、轴承、挤出机、打印平台、加热板等等。做了干燥箱,改装挤出机结构来适配,风扇口也一并搞掉&…...

源码编译dotnetcore的runtime
为了dotnetcore运行时的安可目标,特意在国庆假期研究了怎么编译dotnetcore的runtime。由于我们用的是.net6,最新的是8,所以从github下载的.net6的分支代码进行的编译。查遍了国内外资料,估计微软服务太体贴了,竟然没什…...

11个在线免费调整图像大小而不会降低质量工具
图片对于增强您的网站、博客和其他在线平台的视觉效果非常重要,而这些图片的正确尺寸在这里起着重要作用。如果您有多种尺寸的图像并且想要调整为一个尺寸,可以使用多种在线图像调整工具。使用在线工具,没有软件下载或安装的麻烦,…...

聊聊机器的情感和意识
这是鼎叔的第七十七篇原创文章。行业大牛和刚毕业的小白,都可以进来聊聊。 欢迎关注本公众号《敏捷测试转型》,星标收藏,大量原创思考文章陆续推出。 鼎叔的个人专著《无测试组织-测试团队的敏捷转型》无测试组织:测试团队的敏捷…...
职责链模式,非常容易被忽视的设计模式之一(设计模式与开发实践 P13)
文章目录 现实实例反例优化异步职责链 职责链模式在 C# 中是常见的,他的定义是:使多个对象都有机会处理请求,从而避免发送者和请求者之间的耦合关系,将对象连成一条链并传递该请求,直到有一个对象处理它为止 现实实例…...

架构师选择题--计算机网络
架构师选择题--计算机网络 22年考题21年考题 22年考题 d http:80 https:httpssl :443 b b pop3是邮件接收协议:110 SMTP是邮件发送协议:25 http:80 A 网络隔离:防火墙(逻辑),网闸(物…...
【图论】Linova and Kingdom—CF1336A
Linova and Kingdom—CF1336A 参考文章 思路 1 1 1 号节点为根节点。很容易想到,工业城市在树的下边,旅游城市在树的上边。具体来说,如果节点 u u u 是工业城市,那么它的子树的所有节点一定都是工业城市;如果节点 u…...

【红日靶场】vulnstack3-完整渗透过程
系列文章目录 【红日靶场】vulnstack1-完整渗透过程 【红日靶场】vulnstack2-完整渗透过程 【红日靶场】vulnstack3-完整渗透过程 文章目录 系列文章目录基本信息环境配置开始渗透信息收集暴力破解漏洞利用绕过内网信息收集尝试上线msf上线msf横向移动msf 传达会话给cs横向到域…...
物联网通信技术课程作业资料(TPUNB技术)
参考内容 TPUNB无线通信技术 - 技象科技 (techphant.cn) 技象科技CTO郑凛:用最好的物联网服务最多的人 | 了不起的创变者_技术_通信_团队 (sohu.com) LPWAN技术融合使用大势之下,TPUNB奔跑的一年-IOTE物联网展 (baidu.com) 院士认可国际首创…...

[开源]研发管理项目,支持从需求到代码发布全过程全生命周期管理
一、开源项目简介 neatlogic-rdm支持从需求到代码发布全过程覆盖。具备需求管理、缺陷追踪、测试计划、测试用例、报表仪表板等功能,支持关联外部代码库如GitLab、GitHub等。个性化的属性配置和状态流转控制,能帮助用户管理不同类型项目。 二、开源协议…...

一文生成猫眼电影热榜词云
1.爬取猫眼电影热榜数据 此次爬取的是电影票房的热榜电影名称,具体网站网址为猫眼电影热榜,经过实验观察后发现,此处的数据是通过ajax异步加载的,如果不相信可以使用request对当前网站网址发送请求,会发现无法获取电影…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...

深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...