点评项目-4-隐藏敏感信息、使用 redis 优化登录业务
一、隐藏敏感信息
之前我们对 /user/me 路径,直接返回了登录的所有用户信息,其中的 passward 等敏感信息也会被返回到前端,这是很危险的,故我们需要选择性的返回用户信息,隐藏敏感用户信息
我们可以创建一个 UserDTO 类将 user 中可以返回的信息封装到其中后,将 UserDTO 返回
@Data
public class UserDTO {private Long id;private String nickName;private String icon;
}
然后我们将登录成功时,存入 user 的操作,改为存入 UserDTO ,这里使用的是 hutool 工具中 BeanUtil 来封装,将之前的 user 换成 UserDTO 后再存入 session
//保存用户登录信息
// session.setAttribute("user",user);session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
在拦截器进行登录校验时,我们会拿出这个 user ,需要将拦截器的对应代码也修改
//前置拦截@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//拿到 session 中的 userObject user = request.getSession().getAttribute("user");System.out.println(user+"进入前置拦截器");//若用户不存在,拦截if(user == null){response.setStatus(401);//响应 401 状态码,表示未授权return false;}//将用户保存在 ThreadLocal 中,调用 UserHolder 中的静态方法UserHolder.saveUser((UserDTO) user);//放行System.out.println("前置拦截放行");return HandlerInterceptor.super.preHandle(request, response, handler);}
public class UserHolder {//user 对应的的线程池private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();//往线程池中存入用户public static void saveUser(UserDTO user){tl.set(user);}//拿到当前线程的 user,一个线程只有一个 userpublic static UserDTO getUser(){return tl.get();}//移除当前线程的 userpublic static void removeUser(){tl.remove();}}
修改完毕后,我们再次使用 postman 进行测试,完成发验证码,登录,等了校验三个请求

可以看到,这次返回的用户信息只含有 id,昵称,头像。保护了敏感信息。
二、解决集群的 session 共享问题
使用 session 进行登录信息的存储,当出现多台 tomcat 时,存储的信息会出现无法共享的情况,我们可以通过 Redis 来存储信息来解决
对于验证码,我们可以使用手机号作为 key 验证码存入 value中;
对于登录信息,我们可以使用哈希结构存储不同的信息,使用随机 token 生成随机且唯一的 key,在响应时,将 token 返回给浏览器,在之后需要用到 token 的请求,需要在请求中发送 token
在完成业务之前,我们先配置一下 redis 并测试是否可以正常访问
yml 配置文件:
server:port: 8082spring:application:name: mydpdatasource:url: jdbc:mysql://localhost:3306/learnbaseusername: rootpassword: 1234redis:host: 127.0.0.1port: 6379lettuce:pool:max-active: 8 # 最大连接max-idle: 8 # 最大空闲连接min-idle: 0max-wait: 100 # 最大等待时间,单位毫秒jackson:default-property-inclusion: non_null # JSON处理时忽略非空字段
测试 redis 是否连接正常
@SpringBootTest
class MyDianpingApplicationTests {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Testvoid redisText() {stringRedisTemplate.opsForValue().set("dataRides","check");Object dataRides = stringRedisTemplate.opsForValue().get("dataRides");System.out.println(dataRides);}}
若 redis 中写入了键值对 dataRides , check 则可以认为连接时畅通的
接下来我们便可以通过 Redis 来优化登录业务了
发送验证码
首先在成员变量位置注入 StringRedisTemplate
@Resourceprivate StringRedisTemplate stringRedisTemplate;
Controller 层和 UserService 接口无需改动,只需修改 UserServiceImpl 的 sendCode 方法即可
@Overridepublic Result sendCode(String phone, HttpSession session) {//先用 hutool 工具校验手机号是否合法if (phone == null || !PhoneUtil.isPhone(phone)) {return Result.fail("请输入合法的手机号");//若不合法直接响应错误}//用 hutool 工具生成六位验证码String code = RandomUtil.randomNumbers(6);//将生成的验证码放入 session//TODO 优化:保存验证码到redis//TODO 后面两个参数时验证码的有效期,2分钟,设置后 redis 会在底层加上 set key value ex 120stringRedisTemplate.opsForValue().set("login:code:"+phone,code,2, TimeUnit.MINUTES);//给手机号发送验证码,这里模拟发送验证码的操作,而不是真正的发送System.out.println("手机收到了一条验证码短信:"+code);return Result.ok();}
登录逻辑
从 redis 中获取验证码并验证,验证通过后存入用户信息到 redis
生成 token 作为唯一的标识符,将用户信息封装为哈希表,将其挂在 token 下后存入 redis
最后返回 token ,在之后每次需要用到用户信息的请求中,我们都将 token 作为请求头发送请求
@Overridepublic Result login(LoginFormDTO loginFormDTO, HttpSession session) {if(loginFormDTO == null){return Result.fail("无效操作");}//校验手机号String phone = loginFormDTO.getPhone();if (phone == null || !PhoneUtil.isPhone(phone)) {return Result.fail("请输入合法的手机号");//若不合法直接响应错误}//拿到 session 域中的验证码//TODO 优化:从 redis 中获取验证码String code1 = stringRedisTemplate.opsForValue().get("login:code:"+phone);String code = loginFormDTO.getCode();if(!code.equals(code1)){return Result.fail("验证码输入错误,请重新输入");}//验证码正确,判断是否存在用户User user = userMapper.selectByPhone(phone);if(user == null){//若不存在就创建一个用户并存入 mysqluser = createUserByPhone(phone);userMapper.insert(user);}//保存用户登录信息//TODO 优化:生成 token ,使用哈希的方式保存用户信息//使用 hutool 的 UUID 来生成 tokenString token = UUID.randomUUID().toString();UserDTO userDTO = BeanUtil.copyProperties(user,UserDTO.class);//使用 stringRedisTemplate,使用 hash 的方式存储存信息时必须保证所有 key value 都是 String 类型Map<String,String> userMap = new HashMap<>();userMap.put("id",Long.toString(userDTO.getId()));userMap.put("nickName",userDTO.getNickName());userMap.put("icon",userDTO.getIcon());stringRedisTemplate.opsForHash().putAll("login:token:"+token,userMap);//设置 token 有效日期,此处设定初始有效日期,可以通过通用拦截器更新有效日期stringRedisTemplate.expire("login:token:"+token,30,TimeUnit.MINUTES);//TODO 优化:返回生成的 tokenreturn Result.ok(token);}
拦截器更新 token 有效期,登录验证
为了做到当用户完成任何操作后,token 的有效期更新,以保证用户的使用体验,我们可以设置一个全局拦截器,将 token 的更新操作写在全局拦截器中,并在全局拦截器中将用户信息存入线程池中
再使用第二级拦截器判断用户是否登录,登录的用户一定会存在于线程池,我们可以通过此来判断用户是否登录
一级拦截
public class RefreshTokenInterceptor implements HandlerInterceptor {//这个类没有被 Spring 管理,我们可以使用其配置类 MvcConfig 拿到后通过有参构造传递进来private StringRedisTemplate stringRedisTemplate;public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate){this.stringRedisTemplate = stringRedisTemplate;}//前置拦截@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取请求头中的 tokenString token = request.getHeader("authorization");if(StrUtil.isBlank(token)){//token 不存在,直接放行return true;}//基于 token 取用户信息Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries("login:token:"+token);if(userMap.isEmpty()){//没有信息,直接放行return true;}//有用户信息,将 userMap 转为 UserDTO 后保存到 ThreadLocal 中UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);//最后一个参数表示是否忽略转换过程中的错误UserHolder.saveUser(userDTO);//更新 token 的失效时间stringRedisTemplate.expire("login:token:"+token,30, TimeUnit.MINUTES);//放行return true;}//渲染后拦截@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//在渲染后将 user 从线程中移除UserHolder.removeUser();}
}
二级拦截
public class LoginInterceptor implements HandlerInterceptor {//前置拦截@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//判断线程池中是否有登录用户,若没有则拦截,返回 401 状态码if(UserHolder.getUser() == null){response.setStatus(401);return false;}return true;}}
测试



相关文章:
点评项目-4-隐藏敏感信息、使用 redis 优化登录业务
一、隐藏敏感信息 之前我们对 /user/me 路径,直接返回了登录的所有用户信息,其中的 passward 等敏感信息也会被返回到前端,这是很危险的,故我们需要选择性的返回用户信息,隐藏敏感用户信息 我们可以创建一个 UserDTO…...
Redis异步实现解析
目录 1. Redis 异步方式1.1 同步连接优点缺点示例:访问 Redis,并对 counter 实现自增1000次,统计用时 1.2 异步连接优点缺点1.2.1 Redis 驱动1.2.2 示例第1步:实现 Reactor第2步:实现 Redis 适配器第3步:实…...
matlab 相关
1、xcorr 本质上是两个函数做内积运算 相关算法有两种: 在Matlab上既可以 1.用自带的xcorr函数计算互相关,2.通过在频域上乘以共轭复频谱来计算互相关; 网友验证程序 clc;clear;close all; % s1,s2为样例数据 s1 [-0.00430297851562500;-…...
从组会尴尬到学术突破:Transformer助力跨域推荐解析
最近学习了Transformer模型,突然意识到我常阅读的一篇论文中也使用了Transformer。回想起上次开组会时,老师问我论文中的模型是什么,我当时没有答上来,现在才发现其实用的就是Transformer。这种学习过程让我深感,学得越…...
【Flutter、H5、Web?前端个人总结】分享从业经历经验、自我规范准则,纯干货
前言 hi,正式接触web前端已经经过了两年的时间,从大学的java后端转型到web前端,再到后续转战Flutter,逐渐对前端有了一些心得体会,其实在当下前端的呈现形式一直在变化,无论你是用原生、还是web还是混编的…...
mysql主从配置
一、准备工作 准备两个版本一致的数据库。 确认主库开启二进制日志,并配置server-id。 $ ##将 mysql的配置文件/home/mysql2/mysql/my.cnf 中关于二进制日志的配置 $ cd /home/mysql2/mysql/ $ vi my.cnf 修改如下 server-id 11 #log settings log_error erro…...
sklearn pipeline
示例代码 from sklearn.pipeline import Pipeline from sklearn.feature_extraction.text import CountVectorizer from sklearn.naive_bayes import MultinomialNB import numpy as np import scipy.linalg from sklearn.preprocessing import LabelEncoder, StandardScaler …...
springboot实现服务注册与发现
在Spring Boot应用中实现服务注册与发现通常使用Spring Cloud框架,其中Eureka和Consul是两个常用的服务注册与发现组件。以下是使用Eureka来实现服务注册与发现的基本步骤。 准备工作 添加依赖:在你的Spring Boot项目的pom.xml文件中添加Eureka相关的依…...
美格智能亮相2024中国移动全球合作伙伴大会,共赢AI+时代
2024年10月11日至13日,主题为“智焕新生 共创AI时代”的2024中国移动全球合作伙伴大会,在广州琶洲保利世贸博览馆召开,作为中国移动重要的战略合作伙伴,美格智能亮相4号馆E22展位,与上百家知名企业共同展示最新数智化创…...
【LeetCode】动态规划—309. 买卖股票的最佳时机含冷冻期(附完整Python/C++代码)
动态规划—309. 买卖股票的最佳时机含冷冻期 题目描述前言基本思路1. 问题定义2. 理解问题和递推关系状态定义:状态转移公式:初始条件: 3. 解决方法动态规划方法伪代码: 4. 进一步优化5. 小总结 Python代码Python代码解释总结 C代…...
IDE启动失败
报错:Cannot connect to already running IDE instance. Exception: Process 24,264 is still running 翻译:无法连接到已运行的IDE实例。异常:进程24,264仍在运行 打开任务管理器,找到PID为24264的CPU线程,强行结束即可。 【Ct…...
【Kubernetes】常见面试题汇总(六十)
目录 131. pod 一直处于 pending 状态? 132. helm 安装组件失败? 特别说明: 题目 1-68 属于【Kubernetes】的常规概念题,即 “ 汇总(一)~(二十二)” 。 题目 69-113 属于…...
maven dependency中scope的取值类型
在 Maven 中,<scope> 标签用于定义依赖项的范围,以指定依赖在不同阶段的可见性和生命周期。以下是 Maven 中常见的 <scope> 取值类型的详细介绍: 1. **compile**: - 默认的依赖范围,适用于编译、测试和…...
线性代数在大一计算机课程中的重要性
线性代数在大一计算机课程中的重要性 线性代数是一门研究向量空间、矩阵运算和线性变换的数学学科,在计算机科学中有着广泛的应用。大一的计算机课程中,线性代数的学习为学生们掌握许多计算机领域的关键概念打下了坚实的基础。本文将介绍线性代数的基本…...
笔记本电脑按住电源键强行关机,对电脑有伤害吗?
电脑卡住了,我们习惯性地按住电源键或者直接拔掉电源强制关机,但这种做法真的安全吗?会不会对电脑造成伤害呢? 其实,按住电源键关机和直接拔掉电源关机是不一样的。它们在硬件层面有着本质区别。 按住电源键关机 当…...
如何将 cryptopp库移植到UE5内
cryptopp是一个开源免费的算法库,这个库的用途非常多,我常常用这个库来做加解密的运算。这段时间在折腾UE5.4.4,学习的过程中,准备把cryptopp移植到游戏的工程内,但UE的编译环境和VS的编译环境完全不同,能在…...
SpringBoot 集成GPT实战,超简单详细
Spring AI 介绍 在当前的AI应用开发中,像OpenAI这样的GPT服务提供商主要通过HTTP接口提供服务,这导致大部分Java开发者缺乏一种标准化的方式来接入这些强大的语言模型。Spring AI Alibaba应运而生,它作为Spring团队提供的一个解决方案&…...
基于Langchain框架下Prompt工程调教大模型(LLM)[输入输出接口、提示词模板与例子选择器的协同应用
大家好,我是微学AI,今天给大家介绍一下基于Langchain框架下Prompt工程调教大模型(LLM)[输入输出接口、提示词模板与例子选择器的协同应用。本文深入探讨了Langchain框架下的Prompt工程在调教LLM(大语言模型)方面的应用,…...
Vue基于vue-office实现docx、xlsx、pdf文件的在线预览
文章目录 1、vue-office概述2、效果3、实现3.1 安装3.2 使用示例3.2.1 docx文档的预览3.2.2 excel文档预览3.2.3 pdf文档预览1、vue-office概述 vue-office是一个支持多种文件(docx、.xlsx、pdf)预览的vue组件库,支持vue2和vue3。 功能特色: 一站式:提供docx、.xlsx、pdf多…...
哪个软件可以在线编辑ppt? 一口气推荐5个做ppt的得力助手!
日常在制作ppt时,你是否经常遇到这些问题,ppt做到一半,电脑突然死机,来不及保存的ppt付之一炬,分分钟让人原地崩溃…… 好在许多团队也在持续跟进这个问题,给出了一个一劳永逸的最佳方案——PPT在线编辑&a…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
