当前位置: 首页 > 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;会发现无法获取电影…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...

【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?

FTP&#xff08;File Transfer Protocol&#xff09;本身是一个基于 TCP 的协议&#xff0c;理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况&#xff0c;主要原因包括&#xff1a; ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...