Redis实战(黑马点评)——涉及session、redis存储验证码,双拦截器处理请求

项目整体介绍 

数据库表介绍

基于session的短信验证码登录与注册

controller层

     // 获取验证码@PostMapping("code")public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {return userService.sendCode(phone, session);}// 获取验证码之后登录页面@PostMapping("/login")public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){// TODO 实现登录功能return userService.login(loginForm, session);}

service层 

@Override
public Result sendCode(String phone, HttpSession session) {// 1. 校验手机号格式是否正确if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail("手机号格式不正确"); // 如果手机号格式不正确,返回失败结果}// 2. 生成6位随机数字验证码String code = RandomUtil.randomNumbers(6);// 3. 将验证码存储到HttpSession中session.setAttribute("code", code);// 4. 模拟发送验证码(实际开发中可以替换为短信发送逻辑)log.debug("发送验证码成功,验证码为:" + code);// 5. 返回成功结果return Result.ok();
}@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {// 1. 校验手机号格式是否正确(防止用户在发送验证码后修改手机号)String phone = loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail("手机号格式不正确"); // 如果手机号格式不正确,返回失败结果}// 2. 从HttpSession中获取存储的验证码Object Cachecode = session.getAttribute("code");// 3. 校验用户输入的验证码是否正确if (!loginForm.getCode().equals(Cachecode.toString()) || Cachecode == null) {return Result.fail("验证码错误"); // 如果验证码不匹配或为空,返回失败结果}// 4. 判断数据库中是否存在该手机号对应的用户User user = lambdaQuery().eq(User::getPhone, phone).one(); // 查询用户// 5. 如果用户不存在,则创建新用户并保存到数据库if (user == null) {user = new User();user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10)) // 设置随机昵称.setPhone(phone); // 设置手机号save(user); // 保存新用户到数据库}// 6. 将用户信息存储到HttpSession中session.setAttribute("user", user);// 7. 返回登录成功结果return Result.ok();
}

基于session登录的拦截器相关配置

创建拦截器

@Slf4j // 使用Lombok自动生成日志对象
@Component // 标识这是一个Spring组件
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 获取当前请求的session对象HttpSession session = request.getSession();// 2. 从session中获取用户信息Object user = session.getAttribute("user");// 3. 判断用户是否存在if (user == null) {// 4. 如果用户不存在,拦截请求,返回401状态码(未授权)response.setStatus(401);return false; // 返回false表示请求被拦截,不再继续执行后续的处理器}// 5. 如果用户存在,将用户信息保存到ThreadLocal中,以便在其他地方使用BaseContext.setCurrent((User) user);  // 6. 放行请求,继续执行后续的处理器return true;}// 视图渲染完毕后运行@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 记录日志信息log.info("afterCompletion ....");        // 通常移除线程池中的用户信息,防止内存泄漏BaseContext.removeCurrent();}
}

注册拦截器拦截对象

@Configuration // 标识这是一个Spring配置类
public class MvcConfig implements WebMvcConfigurer {@Autowired // 自动注入LoginInterceptor的实例private LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 向Spring MVC注册拦截器registry.addInterceptor(loginInterceptor) // 添加拦截器.addPathPatterns("/**") // 拦截所有请求路径.excludePathPatterns( // 排除不需要拦截的路径"/shop/**", // 排除/shop/下的请求"/voucher/**", // 排除/voucher/下的请求"/shop-type/**", // 排除/shop-type/下的请求"/upload/**", // 排除/upload/下的请求"/blog/hot", // 排除/blog/hot请求"/user/code", // 排除/user/code请求"/user/login" // 排除/user/login请求);}
}

基于redis实现token登录与拦截器刷新token的过期时间

拦截器内获取请求头token同时检验与刷新

@Override  
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  // 从请求头中获取 "authorization" 的值  String token = request.getHeader("authorization");  // 检查 token 是否为空或者空串  if(StrUtil.isBlank(token)){   // 如果 token 为空,设置响应状态为 401(未授权)  response.setStatus(401);  return false; // 返回 false,表示请求未被处理  }  // 从 Redis 中获取与 token 关联的用户信息  Map<Object, Object> userMap = redisTemplate.opsForHash().entries(LOGIN_USER_KEY + token);  // 检查用户信息是否为空  if(userMap.isEmpty()){  // 如果用户信息为空,设置响应状态为 401(未授权)  response.setStatus(401);  return false; // 返回 false,表示请求未被处理  }  // 将用户信息填充到 UserDTO 对象中  UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);  // 将当前用户信息设置到上下文中  BaseContext.setCurrent(userDTO);  // 刷新 token 的过期时间String key = LOGIN_USER_KEY + token;  redisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.MINUTES);  // 返回 true,表示请求可以继续处理  return true;  
}

service层业务逻辑 

@Autowired  
private RedisTemplate redisTemplate;  // 发送验证码的方法  
@Override  
public Result sendCode(String phone, HttpSession session) {  // 1、校验手机号格式  if(RegexUtils.isPhoneInvalid(phone)){  return Result.fail("手机号格式不正确"); // 返回失败结果,提示手机号格式不正确  }  // 生成一个随机的6位验证码  String code = RandomUtil.randomNumbers(6);  // 将验证码存储到 Redis 中,设置过期时间  redisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);  // 模拟发送验证码(此处仅为日志记录,实际应用中应调用短信发送服务)  log.debug("发送验证码成功,验证码为:" + code);  return Result.ok(); // 返回成功结果  
}  // 登录的方法  
@Override  
public Result login(LoginFormDTO loginForm, HttpSession session) {  // 校验手机号,可能在收到验证码后修改了手机号  String phone = loginForm.getPhone();  if(RegexUtils.isPhoneInvalid(phone)){  return Result.fail("手机号格式不正确"); // 返回失败结果,提示手机号格式不正确  }  // 从 Redis 中获取与手机号相关的验证码  String code = (String) redisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);  // 校验输入的验证码是否与 Redis 中的验证码匹配,且验证码不为空  if(!loginForm.getCode().equals(code) || code == null){  return Result.fail("验证码错误"); // 返回失败结果,提示验证码错误  }  // 判断数据库中是否存在此电话号码的用户,如果没有就插入数据库  User user = lambdaQuery().eq(User::getPhone, phone).one();  if(user == null) {  // 如果用户不存在,创建新用户  user = new User();  user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10)) // 设置用户昵称  .setPhone(phone); // 设置用户手机号  save(user); // 保存新用户到数据库  }  // 生成一个新的 token  String token = UUID.randomUUID().toString();  // 将用户信息复制到 UserDTO 对象中  UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);  // 将 UserDTO 转换为 Map 以便存储到 Redis  Map<String, Object> userMap = BeanUtil.beanToMap(userDTO);  // 生成 Redis 中的 token 键  String tokenkey = LOGIN_USER_KEY + token;  // 将用户信息存储到 Redis 中  redisTemplate.opsForHash().putAll(tokenkey, userMap);  // 设置 token 的过期时间  redisTemplate.expire(tokenkey, LOGIN_USER_TTL, TimeUnit.MINUTES);  // 返回成功结果,携带生成的 token  return Result.ok(token);  
}

双拦截器实现登录与未登录功能差别 

第一层拦截器 

@Autowiredprivate RedisTemplate redisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("authorization");if(StrUtil.isBlank(token)){ // 检查是否为空或者空串return true;}Map<Object, Object> userMap = redisTemplate.opsForHash().entries(LOGIN_USER_KEY + token);if(userMap.isEmpty()){return true;}UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);BaseContext.setCurrent(userDTO);String key = LOGIN_USER_KEY + token;redisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.MINUTES);return true;}//视图渲染完毕后运行@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("afterCompletion ....RefreshTokenInterceptor");BaseContext.removeCurrent(); // 通常移除线程池}

 第二层拦截器

    @Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if(BaseContext.getCurrent() == null){response.setStatus(401);return false;}return true;}

注册双拦截器 

.order();方法用于指定拦截器的优先级,里面的值越小,那么优先级越高

registry.addInterceptor(loginInterceptor).excludePathPatterns("/shop/**","/voucher/**","/shop-type/**","/upload/**","/blog/hot","/user/code","/user/login").order(1);// 拦截所有请求registry.addInterceptor(refreshTokenInterceptor).addPathPatterns("/**").order(0);

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

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

相关文章

MYSQL数据库 - 启动与连接

MYSQL数据库的启动&#xff1a; 一 在cmd控制界面以管理员身份运行 执行语句: net start mysql80 net stop mysql80 二 MYSQL数据库客户端建立连接&#xff1a; 1 该种方法是使用windows系统的cmd界面&#xff0c;需要配置相关路径path 2 使用MYSQL自带的

【Salesforce】审批流程,代理登录 tips

审批流程权限 审批流程权限问题解决方案代理登录代理登录后Logout 审批流程权限 前几天&#xff0c;使用审批流程&#xff0c;但是是两个sandbox&#xff0c;同样的配置&#xff0c;我有管理员权限。但是profile不是管理员&#xff0c;只是通过具备管理员权限的permission set…

RDMA 工作原理 | 支持 RDMA 的网络协议

注&#xff1a;本文为 “RDMA” 相关文章合辑。 英文引文机翻未校。 图片清晰度受引文所限。 Introduction to Remote Direct Memory Access (RDMA) Written by: Dotan Barak on March 31, 2014.on February 13, 2015. What is RDMA? 什么是 RDMA&#xff1f; Direct me…

hexo + Butterfly搭建博客

Hexo‌是一个基于Node.js的静态网站生成器&#xff0c;主要用于快速搭建博客和个人网站。它使用Markdown语法编写文章&#xff0c;能够迅速生成静态页面并部署到服务器上。 配置node 使用nvm安装node(v16.13.2)后配置镜像 安装并使用node&#xff1a; nvm install 16.13.2 n…

手撕B-树

一、概述 1.历史 B树&#xff08;B-Tree&#xff09;结构是一种高效存储和查询数据的方法&#xff0c;它的历史可以追溯到1970年代早期。B树的发明人Rudolf Bayer和Edward M. McCreight分别发表了一篇论文介绍了B树。这篇论文是1972年发表于《ACM Transactions on Database S…

【2025年数学建模美赛F题】(顶刊论文绘图)模型代码+论文

全球网络犯罪与网络安全政策的多维度分析及效能评估 摘要1 Introduction1.1 Problem Background1.2Restatement of the Problem1.3 Literature Review1.4 Our Work 2 Assumptions and Justifications数据完整性与可靠性假设&#xff1a;法律政策独立性假设&#xff1a;人口统计…

【FreeRTOS 教程 四】队列创建与发布项目到队列

目录 一、FreeRTOS队列&#xff1a; &#xff08;1&#xff09;队列介绍&#xff1a; &#xff08;2&#xff09;用户模型说明&#xff1a; &#xff08;3&#xff09;阻塞队列&#xff1a; 二、队列管理 API&#xff1a; &#xff08;1&#xff09;uxQueueMessagesWaiti…

如何在data.table中处理缺失值

&#x1f4ca;&#x1f4bb;【R语言进阶】轻松搞定缺失值&#xff0c;让数据清洗更高效&#xff01; &#x1f44b; 大家好呀&#xff01;今天我要和大家分享一个超实用的R语言技巧——如何在data.table中处理缺失值&#xff0c;并且提供了一个自定义函数calculate_missing_va…

基于OpenCV实现的答题卡自动判卷系统

一、图像预处理 🌄 二、查找答题卡轮廓 📏 三、透视变换 🔄 四、判卷与评分 🎯 五、主函数 六、完整代码+测试图像集 总结 🌟 在这篇博客中,我将分享如何使用Python结合OpenCV库开发一个答题卡自动判卷系统。这个系统能够自动从扫描的答题卡中提取信…

C++ list 容器用法

C list 容器用法 C 标准库提供了丰富的功能&#xff0c;其中 <list> 是一个非常重要的容器类&#xff0c;用于存储元素集合&#xff0c;支持双向迭代器。<list> 是 C 标准模板库&#xff08;STL&#xff09;中的一个序列容器&#xff0c;它允许在容器的任意位置快速…

docker部署jenkins

环境&#xff1a; centos7.9 jenkins/jenkins:lts-jdk11 1、拉去jenkins镜像&#xff0c;请指明版本号 [rootlocalhost ~]# docker pull jenkins/jenkins:lts 开始拉取 拉取完成 [rootlocalhost ~]# docker pull jenkins/jenkins:lts lts: Pulling from jenkins/jenkins 0a9…

沃尔玛 礼品卡绑定 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向分析 部分代码参考 func doPost…

【2024年华为OD机试】 (A卷,100分)- 整理扑克牌(JavaScriptJava PythonC/C++)

一、问题描述 题目描述 给定一组数字,表示扑克牌的牌面数字,忽略扑克牌的花色,请按如下规则对这一组扑克牌进行整理: 步骤1:分组形成组合牌 炸弹:当牌面数字相同张数大于等于4时。葫芦:3张相同牌面数字 + 2张相同牌面数字,且3张牌与2张牌不相同。三张:3张相同牌面数…

Arduino大师练成手册 --控制 OLED

要在 Arduino 上使用 U8glib 库控制带有 7 个引脚的 SPI OLED 显示屏&#xff0c;你可以按照以下步骤进行&#xff1a; 7pin OLED硬件连接 GND&#xff1a;连接到 Arduino 的 GND 引脚。 VCC&#xff1a;连接到 Arduino 的 5V 引脚。 D0&#xff08;或 SCK/CLK&#xff09;…

单片机基础模块学习——按键

一、按键原理图 当把跳线帽J5放在右侧&#xff0c;属于独立按键模式&#xff08;BTN模式&#xff09;&#xff0c;放在左侧为矩阵键盘模式&#xff08;KBD模式&#xff09; 整体结构是一端接地&#xff0c;一端接控制引脚 之前提到的都是使用了GPIO-准双向口的输出功能&#x…

AWScurl笔记

摘要 AWScurl是一款专为与AWS服务交互设计的命令行工具&#xff0c;它模拟了curl的功能并添加了AWS签名版本4的支持。这一特性使得用户能够安全有效地执行带有AWS签名的请求&#xff0c;极大地提升了与AWS服务交互时的安全性和有效性。 GitHub - okigan/awscurl: curl-like acc…

初识MySQL

文章目录 1.数据库2.查看数据库3.创建数据库4.字符集编码和排序规则6.修改数据库7.删除数据库 1.数据库 MySQL是一款使用率高且免费的数据库&#xff08;使用率仅仅低于Oracle&#xff09; 关系数据库和 NoSQL 数据库管理系统知识库(DB-Engines Ranking -) (此图数据于2025-1…

flutter_学习记录_00_环境搭建

1.参考文档 Mac端Flutter的环境配置看这一篇就够了 flutter的中文官方文档 2. 本人环境搭建的背景 本人的电脑的是Mac的&#xff0c;iOS开发&#xff0c;所以iOS开发环境本身是可用的&#xff1b;外加Mac电脑本身就会配置Java的环境。所以&#xff0c;后面剩下的就是&#x…

[b01lers2020]Life on Mars1

打开题目页面如下 看了旁边的链接&#xff0c;也没有什么注入点&#xff0c;是正常的科普 利用burp suite抓包&#xff0c;发现传参 访问一下 http://5edaec92-dd87-4fec-b0e3-501ff24d3650.node5.buuoj.cn:81/query?searchtharsis_rise 接下来进行sql注入 方法一&#xf…

【PyTorch】3.张量类型转换

个人主页&#xff1a;Icomi 在深度学习蓬勃发展的当下&#xff0c;PyTorch 是不可或缺的工具。它作为强大的深度学习框架&#xff0c;为构建和训练神经网络提供了高效且灵活的平台。神经网络作为人工智能的核心技术&#xff0c;能够处理复杂的数据模式。通过 PyTorch&#xff0…