当前位置: 首页 > news >正文

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、登录拦截器、全局异常处理)

登录流程&#xff1a; 1、前端调用登录接口&#xff0c;往接口里传入账号&#xff0c;密码 2、根据账号判断是否有这个用户&#xff0c;如果有则继续判断密码是否正确 3、验证成功后&#xff0c;则是根据账号&#xff0c;登录时间生成token&#xff08;用JWT&#xff09; 4、将…...

AI工程化—— 如何让AI在企业多快好省的落地?

文章目录 前言内容简介读者对象专家推荐目录赠书活动 前言 作为计算机科学的一个重要领域&#xff0c;机器学习也是目前人工智能领域非常活跃的分支之一。机器学习通过分析海量数据、总结规律&#xff0c;帮助人们解决众多实际问题。随着机器学习技术的发展&#xff0c;越来越多…...

mysqld_multi测试

mysqld_multi测试 mysql版本&#xff1a;5.7.25-log 在OS上分别安装了两套mysql&#xff0c; data目录为/mysql/mysql3306、 /mysql/mysql3307 。 端口分别为3306 、3307 配置文件为&#xff1a; /mysql/mysql3306/my.cnf /mysql/mysql3307/my.cnf 参考文档&#xff1a; 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深度学习&#xff1a;除基本命令操作外的实用操作 软件安装systemctl软连接日期、时区IP地址、主机名网络传输下载和网络请求端口 进程管理主机状态系统资源监控磁盘信息监控网络状态监控 环境变量上传、下载压缩、解压root用户、用户、用户组管理查看、修改权限控制 软件…...

app对接广告变现平台:影响app广告单价的4大因素

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

【数字化转型】10大数字化转型能力成熟度模型01(IOMM)

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

2023腾讯云轻量应用服务器和普通服务器有什么区别?

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

SSL证书是什么?1分钟get

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

3D打印机升级killpper

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

源码编译dotnetcore的runtime

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

11个在线免费调整图像大小而不会降低质量工具

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

聊聊机器的情感和意识

这是鼎叔的第七十七篇原创文章。行业大牛和刚毕业的小白&#xff0c;都可以进来聊聊。 欢迎关注本公众号《敏捷测试转型》&#xff0c;星标收藏&#xff0c;大量原创思考文章陆续推出。 鼎叔的个人专著《无测试组织-测试团队的敏捷转型》无测试组织&#xff1a;测试团队的敏捷…...

职责链模式,非常容易被忽视的设计模式之一(设计模式与开发实践 P13)

文章目录 现实实例反例优化异步职责链 职责链模式在 C# 中是常见的&#xff0c;他的定义是&#xff1a;使多个对象都有机会处理请求&#xff0c;从而避免发送者和请求者之间的耦合关系&#xff0c;将对象连成一条链并传递该请求&#xff0c;直到有一个对象处理它为止 现实实例…...

架构师选择题--计算机网络

架构师选择题--计算机网络 22年考题21年考题 22年考题 d http:80 https:httpssl &#xff1a;443 b b pop3是邮件接收协议&#xff1a;110 SMTP是邮件发送协议&#xff1a;25 http:80 A 网络隔离&#xff1a;防火墙&#xff08;逻辑&#xff09;&#xff0c;网闸&#xff08;物…...

【图论】Linova and Kingdom—CF1336A

Linova and Kingdom—CF1336A 参考文章 思路 1 1 1 号节点为根节点。很容易想到&#xff0c;工业城市在树的下边&#xff0c;旅游城市在树的上边。具体来说&#xff0c;如果节点 u u u 是工业城市&#xff0c;那么它的子树的所有节点一定都是工业城市&#xff1b;如果节点 u…...

【红日靶场】vulnstack3-完整渗透过程

系列文章目录 【红日靶场】vulnstack1-完整渗透过程 【红日靶场】vulnstack2-完整渗透过程 【红日靶场】vulnstack3-完整渗透过程 文章目录 系列文章目录基本信息环境配置开始渗透信息收集暴力破解漏洞利用绕过内网信息收集尝试上线msf上线msf横向移动msf 传达会话给cs横向到域…...

物联网通信技术课程作业资料(TPUNB技术)

参考内容 TPUNB无线通信技术 - 技象科技 (techphant.cn) 技象科技CTO郑凛&#xff1a;用最好的物联网服务最多的人 | 了不起的创变者_技术_通信_团队 (sohu.com) LPWAN技术融合使用大势之下&#xff0c;TPUNB奔跑的一年-IOTE物联网展 (baidu.com) 院士认可国际首创&#xf…...

[开源]研发管理项目,支持从需求到代码发布全过程全生命周期管理

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

一文生成猫眼电影热榜词云

1.爬取猫眼电影热榜数据 此次爬取的是电影票房的热榜电影名称&#xff0c;具体网站网址为猫眼电影热榜&#xff0c;经过实验观察后发现&#xff0c;此处的数据是通过ajax异步加载的&#xff0c;如果不相信可以使用request对当前网站网址发送请求&#xff0c;会发现无法获取电影…...

如何从视频中智能提取PPT幻灯片:终极免费工具使用指南

如何从视频中智能提取PPT幻灯片&#xff1a;终极免费工具使用指南 【免费下载链接】extract-video-ppt extract the ppt in the video 项目地址: https://gitcode.com/gh_mirrors/ex/extract-video-ppt 在当今数字化教学和远程办公的时代&#xff0c;视频中常常包含重要…...

GCC开发者转LLVM必看:模块化设计带来的5个关键工作流变革

GCC开发者转LLVM必看&#xff1a;模块化设计带来的5个关键工作流变革 当GCC开发者第一次接触LLVM时&#xff0c;往往会惊讶于其完全不同的设计哲学。就像从单块巨石建筑转向预制模块化结构&#xff0c;LLVM的三段式架构不仅改变了代码的组织方式&#xff0c;更从根本上重塑了编…...

深入解析BUCK、BOOST与Charge Pump电路的设计与应用

1. 开关电源基础&#xff1a;为什么需要BUCK、BOOST和Charge Pump&#xff1f; 刚入行那会儿&#xff0c;我总觉得电源设计就是个"变压器加整流桥"的事&#xff0c;直到某次项目里把12V电池直接怼到3.3V的MCU上——随着一缕青烟升起&#xff0c;我才明白电压转换这门…...

终极指南:facenet-pytorch API参考手册与完整函数方法详解

终极指南&#xff1a;facenet-pytorch API参考手册与完整函数方法详解 【免费下载链接】facenet-pytorch Pretrained Pytorch face detection (MTCNN) and facial recognition (InceptionResnet) models 项目地址: https://gitcode.com/gh_mirrors/fa/facenet-pytorch f…...

B站视频下载终极指南:DownKyi高效工具完整使用教程

B站视频下载终极指南&#xff1a;DownKyi高效工具完整使用教程 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频提取、去水印等&#xff…...

OpenClaw隐私保护实践:GLM-4.7-Flash本地处理敏感数据

OpenClaw隐私保护实践&#xff1a;GLM-4.7-Flash本地处理敏感数据 1. 为什么选择本地化方案处理敏感数据 去年我在处理一批客户合同时遇到了一个棘手问题——合同中包含大量身份证号、银行账号等敏感信息&#xff0c;而团队当时使用的云端AI解析服务需要上传完整文件。虽然服…...

遥感智能体模块全景解析:从任务拆解到工作流编排

1. 遥感智能体的核心架构设计 第一次接触遥感智能体&#xff08;RS-Agent&#xff09;这个概念时&#xff0c;很多人会感到困惑&#xff1a;它和传统遥感处理软件有什么区别&#xff1f;简单来说&#xff0c;RS-Agent更像是一个"会思考的助手"。我参与过几个遥感智能…...

PlayCover深度技术解析:如何在M系列Mac上实现iOS游戏原生运行体验

PlayCover深度技术解析&#xff1a;如何在M系列Mac上实现iOS游戏原生运行体验 【免费下载链接】PlayCover Community fork of PlayCover 项目地址: https://gitcode.com/gh_mirrors/pl/PlayCover PlayCover作为一款创新的开源工具&#xff0c;让Apple Silicon Mac用户能…...

效率飙升:用快马生成可复用的wsl环境配置脚本,告别重复劳动

最近在团队协作和更换设备时&#xff0c;经常需要重复配置WSL开发环境&#xff0c;每次都要手动执行一堆命令&#xff0c;不仅耗时还容易遗漏步骤。经过多次实践&#xff0c;我总结出一套用脚本自动化配置的方法&#xff0c;现在通过InsCode(快马)平台就能快速生成可复用的环境…...

为什么很多人学 Django 会懵?因为没搞懂 MVC 和 MTV 的真正区别

很多刚接触 Django 的开发者&#xff0c;甚至包括不少测试工程师&#xff0c;在学习 Django 时都会遇到一个困惑&#xff1a;为什么 Django 不叫 MVC&#xff0c;而是 MTV&#xff1f;更奇怪的是&#xff1a;很多教程还会说&#xff1a;“Django 的 MTV 其实就是 MVC。”这句话…...