点评项目-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;}}

测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/445349.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Qt】详细Qt基础 (包括自定义控件)

目录 QT 概述创建项目项目文件&#xff08;. pro&#xff09;main.cppmainwindow.uimainwindow.hmainwindow.cpp 窗口类QWidget 窗口显示QDialog 窗口 QPushButton创建显示 对象树基本概念功能 坐标体系控件Item WidgetsQListWidgetQTreeWidgetQTableWidget 自定义控件 QT 概述…

一键安装与配置Stable Diffusion,轻松实现AI绘画

随着技术的迭代&#xff0c;目前 Stable Diffusion 已经能够生成非常艺术化的图片了&#xff0c;完全有赶超人类的架势&#xff0c;已经有不少工作被这类服务替代&#xff0c;比如制作一个 logo 图片&#xff0c;画一张虚拟老婆照片&#xff0c;画质堪比相机。 最新 Stable Di…

当今SNARKs全景

1. 引言 前序博客有&#xff1a; ZKP历史总览SNARK原理示例SNARK性能及安全——Prover篇SNARK性能及安全——Verifier篇Transparent 且 Post-quantum zkSNARKsSNARK DesignRollup项目的SNARK景观 SNARKs因&#xff1a; proof size证明时长验证时长密码学信任假设是否需要tr…

前端开发设计模式——装饰器模式

目录 一、装饰器模式的定义和特点 1.定义 2.特点 二、装饰器模式的实现方式 1.在原生JS中实现&#xff08;以类的形式为例&#xff09; 2、在Vue中实现&#xff08;以指令和混入为例&#xff09; 2.1、指令方式实现装饰功能 2.2、混入方式实现装饰功能 三、装饰器模式的…

基于方块编码的图像压缩matlab仿真,带GUI界面

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 编码单元的表示 4.2编码单元的编码 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 下图是随着方块大小的变化&#xff0c;图像的压缩率以及对应的图像质量指标PSN…

PDF处理技巧:Windows电脑如何选择合适的 PDF 编辑器

您可以阅读本文以了解用于在 PC 上编辑 PDF 的顶级免费软件&#xff0c;而无需花费任何费用即可轻松进行快速编辑、拆分、合并、注释、转换和共享您的 PDF。 PDF 或可移植文档文件是由 Adobe 创建的一种多功能文件格式。它可以帮助您轻松可靠地交换文档&#xff0c;无论相关方…

TCN-Transformer时间序列预测(多输入单预测)——基于Pytorch框架

1 数据集介绍 我们使用的数据集包含以下几个重要的属性&#xff1a; date&#xff08;日期&#xff09; open&#xff08;开盘价&#xff09; high&#xff08;最高价&#xff09; low&#xff08;最低价&#xff09; close&#xff08;收盘价&#xff09; pre_close&…

IDE启动失败

报错&#xff1a;Cannot connect to already running IDE instance. Exception: Process 24,264 is still running 翻译&#xff1a;无法连接到已运行的IDE实例。异常:进程24,264仍在运行 打开任务管理器&#xff0c;找到PID为24264的CPU线程&#xff0c;强行结束即可。 【Ct…

EXCEL_光标百分比

Public Sub InitCells()Dim iSheet As LongFor iSheet Sheets.Count To 1 Step -1Sheets(iSheet).ActivateActiveWindow.Zoom 85ActiveWindow.ScrollRow 1ActiveWindow.ScrollColumn 1Sheets(iSheet).Range("A1").ActivateNext iSheetEnd Sub对日项目中的文档满天…

CSS 布局——清除浮动 (二)

目录 1. 清除浮动 2. 清除浮动本质 3. 清除浮动 4. 清除浮动方法 4.1 额外标签法 4.1.1 总结 4.2 父级添加 overflow 4.3 after 伪元素法 4.4 双伪元素清除浮动 5 总结 1. 清除浮动 这是上面的源代码&#xff1a; <!DOCTYPE html> <html lang"en"&…

【FPGA开发】Modelsim如何给信号分组

前面已经发布过了一篇关于 Modelsim 的入门使用教程&#xff0c;针对的基本是只有一个源文件加一个仿真tb文件的情况&#xff0c;而实际的工程应用中&#xff0c;往往是顶层加多个底层的源文件结构&#xff0c;如果不对信号进行一定的分组&#xff0c;就会显得杂乱不堪&#xf…

第33次CCF计算机软件能力认证-第4题十滴水

题干&#xff1a; 十滴水是一个非常经典的小游戏。 小 C C C 正在玩一个一维版本的十滴水游戏。 我们通过一个例子描述游戏的基本规则。 游戏在一个 1 c 1c 1c 的网格上进行&#xff0c;格子用整数 x ( 1 ≤ x ≤ c ) x(1≤x≤c) x(1≤x≤c) 编号&#xff0c;编号从左往…

Python学习-函数

函数 文章目录 函数定义与调用参数传递内存分析返回值参数定义默认值参数个数可变的参数关键字参数 变量的作用域 匿名函数基本语法示例lambda与排序高阶函数map函数reduce函数filter函数 多关键字排序 定义与调用 函数可以嵌套用 先定义后调用 def calc(a,b):cabreturn cre…

一台电脑轻松接入CANFD总线_来可CNA板卡介绍

在工业控制领域&#xff0c;常常使用的总线技术有CAN(FD)、RS-232、RS-485、Modbus、Profibus、Profinet、EtherCAT等。RS-485以其长距离通信能力著称&#xff0c;Modbus广泛应用于PLC等设备&#xff0c;EtherCAT则以其低延迟和高实时性在自动化系统中备受青睐。 其中&#xff…

Java虚拟机(JVM)介绍

**Java虚拟机&#xff08;JVM&#xff09;**是Java平台的核心组件&#xff0c;它提供了一个运行时环境&#xff0c;使得Java程序可以在不同的操作系统和硬件平台上运行而无需修改。 JVM的架构 JVM主要由以下几个部分组成&#xff1a; 类加载器&#xff08;Class Loader&#xf…

对后端返回的日期属性进行格式化(扩展 Spring MVC 的消息转换器)

格式化之前 格式化之后&#xff1a; 解决方式 方式一 在属性中加上注解&#xff0c;对日期进行格式化 JsonFormat(pattern "yyyy-MM-dd HH:mm:ss")private LocalDateTime createTime;//JsonFormat(pattern &quo…

小白必看web专题!PHP-WebShell免杀(基础版)!!真的很简单!(全网最详细版本)

大家好&#xff0c;我是Dest1ny&#xff01; 最近一直在搞辅导啥的&#xff0c;所以没啥时间搞写&#xff5e; 也谢谢大家一直的点赞&#xff0c;今天特意把之前的web专题再发一个。 废话不多说&#xff0c;我们直接开始&#xff01; CLASS-1 WebShell免杀测试 渊龙Sec团队导…

「Ubuntu」根目录存储空间不足

Linux系统不同于 Windows系统&#xff0c;复杂的文件系统常常让人头疼&#xff0c;特别是动不动就存储空间不足&#xff0c;简单的清空回收站根本不管用&#xff0c;在此推荐一个绝对好用的方法&#xff0c;并且还可以多学习一条 Linux命令 1、du 使用方法 通过使用命令 du&am…

Ubuntu24.04 安装 NCAR Command Language(NCL)

目录 一般直接在Terminal中使用apt安装命令即可&#xff0c; 出现这样的问题&#xff0c; 如何解决这个问题呢&#xff1f; 一般直接在Terminal中使用apt安装命令即可&#xff0c; sudo apt install ncl-ncarg 但是&#xff0c;由于 Ubuntu 版本较新 Ubuntu 24.04&#xff…

【Next.js 入门教程系列】07-身份验证

原文链接 CSDN 的排版/样式可能有问题&#xff0c;去我的博客查看原文系列吧&#xff0c;觉得有用的话&#xff0c; 给我的库点个star&#xff0c;关注一下吧 上一篇【Next.js 入门教程系列】06-上传文件 身份验证 本篇包括以下内容: Setting up Next AuthConfiguring the G…