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

监控脚本展示

需求&#xff1a; 监控SVQC&#xff0c;SVCD&#xff0c;FHTC&#xff0c;FHQC&#xff0c;FHCD文件的生成 监控服务器&#xff1a;10.10.3.56 监控路径&#xff1a;/data/app/datafile/ftp/qdttec/10000002/download/yyyyMMdd/* 监控时间&#xff1a;每天7点开始&#xff0c;2…...

【重拾C语言】五、模块化程序设计——函数(定义、调用、参数传递、结果返回、函数原型;典例:打印字符图形、验证哥德巴赫猜想)

目录 前言 五、模块化程序设计——函数 5.1 计算三角形的重心 5.2 函数 5.2.1 函数定义 5.2.2 函数调用 a. 函数调用的形式和过程 b. 参数传递 值传递 指针传递 c. 函数结果返回 5.2.3 函数原型&#xff08;先调用后定义&#xff09; 5.3 程序设计实例 5.3.1 打印…...

Unity实现设计模式——迭代器模式

Unity实现设计模式——迭代器模式 迭代器模式是一种行为型设计模式&#xff0c;它提供了一种统一的方式来访问集合对象中的元素&#xff0c;而不是暴露集合内部的表示方式。简单地说&#xff0c;就是将遍历集合的责任封装到一个单独的对象中&#xff0c;我们可以按照特定的方式…...

【数据结构与算法】之“堆”介绍

目录 堆的基本存储 一、概念及其介绍 二、适用说明 三、结构图示 堆的 shift up 堆的 shift down 基础堆排序 一、概念及其介绍 二、适用说明 三、过程图示 优化堆排序 索引堆及其优化 一、概念及其介绍 二、适用说明 三、结构图示 堆的基本存储 一、概念及其介…...

ncnn Fatal signal 11 (SIGSEGV) 使用GPU加速崩溃

如果你的报错堆栈中包含以下信息,其中的关键信息是 anon:dalvik-classes2.dex extracted in memory Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x3c in tid 8619 (eplabv3plusncnn), pid 8619 () 2023-10-07 15:48:31.395 9793-9793 DEBUG …...

计算机考研 | 2018年 | 计算机组成原理真题

文章目录 【计算机组成原理2018年真题44题-15分】【第一步&#xff1a;信息提取】【第二步&#xff1a;具体解答】 【计算机组成原理2018年真题45题-8分】【第一步&#xff1a;信息提取】【第二步&#xff1a;具体解答】 【计算机组成原理2018年真题44题-15分】 某计算机采用页…...

用Configuration注解的方式写一个java过滤器的详细实例?

在Java中&#xff0c;可以使用Configuration注解和Spring框架来创建和配置过滤器。下面是一个详细的示例&#xff1a; 首先&#xff0c;创建一个实现javax.servlet.Filter接口的过滤器类&#xff0c;例如MyFilter&#xff1a; import javax.servlet.*; import java.io.IOExce…...

基于Springboot实现旧物置换网站平台演示【项目源码+论文说明】分享

基于Springboot实现旧物置换网站平台演示 摘要 随着时代在一步一步在进步&#xff0c;旧物也成人们的烦恼&#xff0c;许多平台网站都在推广自已的产品像天猫、咸鱼、京东。所以开发出一套关于旧物置换网站成为必需。旧物置换网站主要是借助计算机&#xff0c;通过对用户进行管…...

想要精通算法和SQL的成长之路 - 存在重复元素

想要精通算法和SQL的成长之路 - 存在重复元素 前言一. 存在重复元素II二. 存在重复元素III2.1 基于红黑树增删改查 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 存在重复元素II 原题链接 思路&#xff1a; 我们用HashSet存储元素&#xff0c;做到去重的效果。同时存储…...

使用华为eNSP组网试验⑸-访问控制

今天练习使用华为sNSP模拟网络设备上的访问控制&#xff0c;这样的操作我经常在华为的S7706、S5720、S5735或者H3C的S5500、S5130、S7706上进行&#xff0c;在网络设备上根据情况应用访问控制的策略是一个网管必须熟练的操作&#xff0c;只是在真机上操作一般比较谨慎&#xff…...

网站建设策划书/电商网站推广方案

1、以excel2010版本为例&#xff0c;如下图所示&#xff0c;要把该图表复制到PPT&#xff1b; 2、点击excel的图表点复制&#xff0c;然后在PPT里面点击鼠标右键&#xff0c;粘贴选项选择保留源格式和嵌入工作簿&#xff1b; 3、点击保留源格式和嵌入工作簿后就会得到如下图所…...

固原网站建设/域名查询系统

一、SDK的安装 安装过程中不要插相机&#xff0c;且一定注意SDK和realsense_ros之间的版本对应关系 我这里使用的版本是2.42.0&#xff0c;对应成功的包&#xff0c;可以到该链接下直接下载&#xff0c;https://download.csdn.net/download/YOULANSHENGMENG/87672407 然后再N…...

wordpress页面加载时间/链接地址

使用VC编译C或者C程序&#xff0c;都需要相关的C runtime库才能运行。如果你是VC6&#xff0c;相应的库就叫MSVCR&#xff0c;如果是VC2005&#xff0c;那就是MSVCR08&#xff0c;VC2008就是MSVCR09。我这 里假设你安装的是VC2005&#xff0c;请进入如下目录&#xff1a;{VS In…...

外贸企业 访问国外网站/百度一下就知道了官网楯

Full GC 问题之前在一些文章里面已经讲过它的来龙去脉&#xff0c;主要的解决方案目前 主要有两方面需要注意&#xff0c;一方面需要查看 GC 日志确认是哪种 Full GC&#xff0c;根据 Full GC 类型对 JVM 参数进行调优&#xff0c;另一方面需要确认是否开启了 BucketCache 的 o…...

dw表格怎么做网站搜索/seo专业学校

重新回顾文章之后发现了一些错误&#xff0c;出于严谨&#xff0c;我重新对文章进行整理&#xff0c;并使用更好的方式去验证这个结论&#xff0c;大家可以转移到这篇文章&#xff1a;陈若楓&#xff1a;探究: 为什么CSS要在head标签中引入以下为原答案&#xff1a;css为什么要…...

海外公司注册在哪里比较好/我是seo关键词

print([x*11 for x in range(10)]) 转载于:https://www.cnblogs.com/sea-stream/p/11192554.html...