当前位置: 首页 > news >正文

1.easypan-登录注册

1.0 遇到的问题

1.0.1 JRebel 导致的 Bug

场景:最开始 yml 中数据库的信息配置错了,修改对后从新构建项目运行(使用了 Jrebel),没有重启,导致一直错误

原因:JRebel 热加载插件缓存了旧的数据库配置…,他不会热加载 yml 配置文件

1.0.2 APIFox 插件导致请求失败

场景:更改了请求配置,原本是@RequestMapping,请求失败,改为争取的@GetMapping,还是请求失败

原因:更改后没有刷新 APIFox 插件,导致还是原本的请求

1.1 图形验证码

1.1.1 当前方案

  1. 自写 创建验证码图像类

  2. 前端 api 增加 time 字段保证每次请求都不同,避免查询缓存,后端不用处理 time 字段

  3. 设置响应头禁止缓存该 HTTP 请求

    • 这三个响应头共同构成了防御性缓存策略,确保:

    • 用户不会看到缓存的旧验证码

    • 代理服务器不会返回历史数据

    • 浏览器每次都必须从服务器获取新验证码

    • Tomcat 默认是 no-cache 的,设置上更保险

  4. 后端将验证码文本存储到 Session 中

    • 根据传入的 type 判断是 0-登录注册,1-邮箱验证码发送
  5. 响应类型设为 image/jpeg(存储在响应头中)

  6. 将验证码存入该 Session 中

    • 通过type参数支持存储到不同 Session 属性(如普通验证码和邮箱验证码分开存储)

    • 这样客户端只能看到图片,无法直接获取或修改验证码内容

    • yml 中设置 session:timeout: PT60M 会话 60 分钟后过期

    • Session ID 通过 Set-Cookie 响应头自动返回

    • 默认 Cookie 名称为 JSESSIONID(Tomcat)或 SESSION(Spring Session)

    • 浏览器后续请求会自动携带该 Cookie

    • Session 由 Tomcat 自行管理,不显示禁用 session 的话 Tomcat 还会创建 Session 到内存中,通过 JESSIONID Cookie 维护会话

  7. 将二进制图形验证码流写入响应体中返回给前端

    • 之前写的 API 都是以 RESTAPI 的格式返回 JSON 数据,这里直接输出响应,Tomcat 将内存中的二进制流(封装后的响应报文)通过 Socket 发送

    验证码为什么采用这种方式?

    • 性能考虑:二进制流直接输出效率更高

    • 资源管理:避免图片数据在内存中多次转换

    • 协议要求:图片响应需要精确控制 Content-Type 等头信息

    • 框架限制:Spring 的消息转换器主要针对结构化数据设计

1.1.2 其他方案

  1. 使用第三方库:Kaptcha 库,简单易用,功能丰富,支持多种干扰效果

  2. 基于 SVG 实现,矢量图形不失真,文件体积小

  3. 行为验证码(如滑动拼图)

  4. Redis 存储方案-支持分布式

1.2 邮箱验证码

1.2.1 当前方案

  1. 前端传入 email 和图形验证码,以及 type:0-注册,1-找回密码

  2. 获取 session 中存储的 code 做比较,没问题之后发送验证码

  3. 先判断是否为注册,校验邮箱是否已经注册

  4. 生成验证码并发送给用户短信,使用 spring 自带的 mail 相关工具包封装邮件

  5. 封装信息:邮箱相关信息,这里短信内容默认配置存入 redis,封装了 RedisUtil 常用方法,Redis 的相关操作放入了 RedisComponent 中

  6. 发送邮件后先将旧的验证么标识为已用,再封装将新验证码信息存入数据库

1.2.2 可优化的点

  1. 增加 IP 限制,防刷

  2. 增加邮箱发送频率限制

  3. 异步发送邮件,避免同步逻辑阻塞

  4. 邮件发送失败重试机制

  5. 数据库存储改为 redis 存储验证码

  6. 分布式锁防止并发(多次重复请求)

  7. 运维方面,日志、限流控制

1.2.3 其他方案

  1. 第三方验证码服务

  2. 自建验证码服务

    • 基于 Spring Cloud 的微服务架构

    • 基于消息队列的异步处理

    • 基于分布式缓存的存储方案

  3. redis+token

1.2.4 MP 查询方案

引入 Mybatis-Plus 后,IService 内部会调用 BaseMapper 类,功能更强大,在服务层获取数据库信息的方案有

  • 查询本表的方案:

    • 继承 BaseMapper 方法

    • Lambda 表达式方式 (推荐),使用 IService 中的 lambda 方法

    • 链式调用方式

  • 查询其他表的方案:

    • 通过注入的 Mapper 查询 (当前代码方式)

    • 自定义 Mapper 方法 (需在 Mapper 中定义)

    • 通过 Service 调用

    • 使用 Db 静态工具类,减少循环依赖注入的风险(推荐)

1.3 AOP 实现参数校验

1.3.1 实现步骤

  1. 引入 AOP 依赖

  2. 定义自定义注解(可选)

  3. 创建切面类并注册为 Bean

    • 使用@Pointcut 定义切点

    • 编写@Before,@After,@Around 等通知方法

    • 获取方法和参数信息

    • 实现业务逻辑(如数据库字段内容添加、参数校验)

    • 统一异常处理与日志记录

  4. 在目标方法上使用注解

1.3.2 对比其他项目

苍穹外卖、黑马点评中是使用 MVC 的 Interceptor 统一拦截请求然后校验的

这里 AOP 自己实现

  • 更细粒度的控制(方法级别)

  • 可以结合自定义注解灵活控制

  • 不局限于 Web 层,可应用于 Service 层

对比建议:

  • 对于纯 Web 请求的登录校验,推荐使用拦截器方案,因为:

    1. 更符合 MVC 架构思想

    2. 性能更好(AOP 会有额外代理开销)

    3. 更易获取 Web 相关对象

  • 考虑 AOP 的场景:

    1. 需要方法级别的细粒度控制

    2. 校验逻辑需要复用至非 Web 组件

    3. 已有基于注解的权限体系

1.4 登录注册&CRUD

1.4.1 登录逻辑

  1. 账号密码登录,先验证图形码

  2. 再验证账号密码,“记住我”1 前端存储的是 md5 加密后的密码

  3. 更新登录时间

  4. 返回给前端基础的用户信息

1.4.2 注册逻辑

  1. 校验图形验证码

  2. 验证邮箱、昵称是否存在

  3. 验证邮箱验证码

  4. 初始化用户云盘空间信息

  5. 保存用户信息到数据库

1.4.3 头像获取

  1. 直接是存储二进制文件到本地

  2. 先根据用户 id 拼接图像名称查找

  3. 没有则使用默认头像

1.4.4 其他

密码更新、重置

退出登录

头像更新 - 直接创建新的头像文件

1.4.5 存在问题

  1. MD5 安全性不足-改用 BCrypt

  2. Session 存储方式存在会话劫持风险

  3. 缺少登录失败次数限制

  4. 缺少密码强度校验

1.4.6 其他方案

  1. JWT + Redis 方案

  2. OAuth2.0 方案:支持第三方登录

  3. Spring Security:安全性高

1.5 登录后默认行为

1.5.1 获取用户默认信息

因为这里采用的是 session 认证,要根据 session 去内存中获取用户信息,所以单独封装了 ABaseController 里面封装了获取用户信息的方法,方便多个接口调用获取。

而苍穹外卖是 JWT+ThreadLocal 方案则是登录后返回给前端 JWT,以后请求先拦截器拦截请求获取 JWT 然后解析出用户信息(包含唯一标识)然后存入 ThreadLocal 中,方便后续直接使用。

而黑马点评是在登录后生产随机 token 返回给前端,并将用户信息作为 value,token 作为小 key 存入 redis 的 hash 中,以后请求携带 token,拦截然后去 redis 中做校验,将用户信息存入 ThreadLocal 中

这里获取用户默认信息以及从 redis 中获取用户的空间使用信息,如果 redis 中没有则先查数据库再缓存并返回

1.5.2 获取文件列表

  1. 先获取具体的文件类型:前端传来的与数据库存储的有个映射

  2. 然后根据 session 分页查询用户文件信息

http://www.xdnf.cn/news/1128241.html

相关文章:

  • git起步
  • Jfinal+SQLite java工具类复制mysql表数据到 *.sqlite
  • 同济医院R语言训练营第三期开讲!上交大张维拓老师主讲
  • 2025最新国产用例管理工具评测:Gitee Test、禅道、蓝凌测试、TestOps 哪家更懂研发协同?
  • 希尔排序:突破传统排序的边界
  • 22.计算指定范围内数字的幂次和
  • StampedLock分析
  • 基于cornerstone3D的dicom影像浏览器 第二章,初始化页面结构
  • 亚矩阵云手机:破解 Yandex 广告平台多账号风控难题的利器
  • 跨平台游戏引擎 Axmol-2.7.1 发布
  • APP端定位实现(uniapp Vue3)(腾讯地图)
  • Ext系列文件系统知识点
  • Linux进程信号--1、信号产生
  • 时间复杂度和空间复杂度是衡量一个算法好坏的标准
  • A*算法详解
  • 9、线程理论1
  • eVTOL分布式电推进(DEP)适航审定探究
  • redisson tryLock
  • Spring MVC2
  • 尚庭公寓-----day1----@MapperScan爆红问题
  • 三十二、【核心功能改造】数据驱动:重构仪表盘与关键指标可视化
  • 【转】Rust: PhantomData,#may_dangle和Drop Check 真真假假
  • 【字节跳动】数据挖掘面试题0019:带货直播间推荐:现在有一个带货的直播间,怎么把它精准地推送给有需要的用户
  • 【C++】神奇的AVL树
  • WebView JSBridge 无响应问题排查实录 全流程定位桥接调用失效
  • 无人机故障响应模块运行与技术难点
  • Ubuntu24 辅助系统-屏幕键盘的back按键在网页文本框删除不正常的问题解决方法
  • RTL编程中常用的几种语言对比
  • 【C#地图显示教程:实现鼠标绘制图形操作】
  • 厂区车辆导航系统:基于 GPS+AI 动态路径规划的技术实现与实践