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

第27周JavaSpringboot电商进阶开发 1.企业级用户验证

课程笔记:注册邮箱验证

一、概述

从本小节开始,将学习如何进行注册邮箱验证。主要任务是给项目配置一个公共邮箱(可自己注册或由公司提供),用于向用户发送验证码,帮助用户完成注册流程。课程中以QQ邮箱为例,介绍在Spring Boot中发送邮件(包括正文、标题等)的方法。用户收到验证码后填写回来,与系统发送的进行比对,一致则验证成功,反之则失败。

二、注册邮箱验证的两种主流方式

  1. 验证码验证(国内常用,课程采用方式)

    • 流程:用户填写邮箱后点击发送验证码按钮,系统向该邮箱发送随机生成的验证码。用户收到后填写回来,系统进行比对校验。
    • 原理:验证码生成、校验及重复校验。若后续需进行短信验证,只需将发邮件改为发短信,其他校验原理一致。
  2. 唯一链接验证(国外常用)

    • 原理:给用户邮箱发送一个带有长链接的邮件,链接访问某接口并带参数(含个人信息)。每个邮箱的链接唯一,能访问该链接说明用户是邮箱主人。
    • 流程:用户点击唯一链接后,系统验证通过,确认是真实用户。

三、课程采用的验证码验证流程

  1. 用户在网页点击发送验证码按钮,系统检查该邮箱是否已注册。
    • 未注册:将用户信息放入缓存,向邮箱发送验证码。
    • 已注册:拒绝注册(不允许两个用户使用同一邮箱)。
  2. 用户在邮箱中收到验证码,复制到网站继续注册,提交验证码。
  3. 系统在缓存中校验验证码是否正确、是否过期。
    • 不正确或过期:拒绝注册。
    • 正确:用户注册成功。

课程笔记:邮件发送功能的开发与完善

一、邮箱配置(以 QQ 邮箱为例)

  1. 进入邮箱设置:点击邮箱界面的“设置”选项。
  2. 找到第三方服务:在设置页面的“常规”选项下,找到“第三方服务”相关设置。
  3. 开启服务并获取授权码
    • 如果服务未开启,点击“开启服务”。
    • 开启后,系统会生成一个授权码,该授权码专门用于程序验证,不同于普通登录密码。
    • 授权码相当于独立密码,用于提高账号安全性,需妥善保存,后续在程序配置中会用到。
  4. 其他邮箱配置类似:如 163 邮箱等,配置过程大致相同,找到类似“第三方服务”或“授权码”相关设置,进行相应配置。

二、给 User 类添加字段

  1. 进入 mybatis 配置类:找到对应的配置类,注释掉其他表,仅保留 User 表。
  2. 更新数据库表:在数据库中找到对应的 User 表,添加 email 字段,类型为 varchar(100),注释为“邮箱地址”。
  3. 重新生成旧类:运行 mybatis generator,生成新的 User 类。
  4. 备份自定义代码:提前复制保存 mapper 中自动生成代码后添加的自定义方法和 SQL 语句,避免重新生成后被覆盖。
  5. 恢复自定义代码:生成新类后,将备份的代码粘贴回来,并引入相关包。

三、引入邮件发送依赖

在项目中添加 spring-boot-starter-mail 依赖,指定版本为 2.3.4.RELEASE。

四、开发发送邮件接口

  1. 在 UserController 中添加接口

    • 方法头:发送邮件,路径为 sendEmail,参数为 emailaddress(邮件地址)。
    • 返回格式匹配。
  2. 实现方法

    • 校验邮件地址是否有效
      • 使用 utu 包中的 InternetAddress 类的 validate 方法。
      • 创建 Email 工具类,新增 isValidEmailAddress 方法,返回 boolean 值。
      • 在该方法中,使用 try-catch 包裹 InternetAddress(email).validate(),若无异常返回 true,否则返回 false。
      • 使用该方法检查传入的 emailaddress,若无效,返回错误响应(枚举值为 INVALID_EMAIL,中文为“非法的邮件地址”)。
    • 检查是否已注册:若邮件地址有效,进一步检查是否已注册。
    • 发送邮件:若以上校验均通过,执行邮件发送逻辑。

五、检查邮件地址是否已注册

  1. 在 User Service 中新增方法

    • 方法名:public boolean checkEmailRegistered(String emailaddress)
    • 使用 UserMapper 的 selectOneByEmailaddress 方法,根据传入的邮件地址查询用户。
    • 若返回的 User 对象不为空,说明邮件地址已被注册,返回 false;否则返回 true。
  2. 在 UserMapper 中补充 SQL 语句

    • 方法名:selectOneByEmailaddress(String emailaddress)
    • SQL 语句:SELECT * FROM imcomo_user WHERE emailaddress = #{emailaddress} LIMIT 1

六、邮件发送逻辑实现

  1. 在 UserController 中调用检查方法

    • 在通过邮件地址合法性验证后,调用 userService.checkEmailRegistered(emailaddress)
    • 若返回值为 false(邮件地址已被注册),返回错误响应(枚举值为 EMAIL_ALREADY_BEEN_REGISTERED,中文为“email 地址已被注册”)。
  2. 创建 EmailService 接口及实现类

    • 接口方法:void sendSimpleMessage(String to, String subject, String text)
    • 实现类:EmailServiceImpl,使用 JavaMailSender 发送邮件。
  3. 配置邮件相关属性

    • 在配置文件中设置邮件主机、端口号、用户名、授权码、编码及验证相关属性。
  4. 完善邮件发送方法

    • sendSimpleMessage 方法中,创建 SimpleMailMessage 对象,设置发件人(从常量中获取)、收件人、主题和正文。
    • 使用 JavaMailSender 的 send 方法发送邮件。
  5. 在 UserController 中调用邮件发送服务

    • 引入 EmailService。
    • 调用 emailService.sendSimpleMessage,传入邮件地址、主题(从常量中获取)和验证码相关正文。

七、生成随机验证码

  1. 在 Email 工具类中新增方法

    • 方法名:public static String generateVerificationCode()
    • 创建包含数字、大写字母、小写字母的字符列表。
    • 使用 Collections.shuffle 打乱列表顺序。
    • 取列表前六位字符作为验证码。
    • 返回生成的验证码字符串。
  2. 测试验证码生成方法:编写测试方法,打印生成的验证码,验证其随机性和正确性。

八、限制重复发送邮件

  1. 引入 Redis 依赖:添加 redisson 依赖,用于操作 Redis。

  2. 在 EmailService 中新增方法

    • 方法名:public boolean saveEmailToRedis(String emailaddress, String verificationCode)
    • 获取 Redis 客户端,连接到本地 Redis。
    • 使用 getBucket 方法传入邮箱地址作为 key,获取对应的 bucket。
    • 检查 bucket 中是否存在值:
      • 不存在:使用 set 方法存入验证码,设置过期时间为 60 秒,单位为秒,返回 true。
      • 存在:返回 false,表示 60 秒内已发送过邮件。
  3. 在 UserController 中调用并处理

    • 调用 emailService.saveEmailToRedis,传入邮箱地址和验证码。
    • 根据返回值判断:
      • 返回 true:发送邮件,返回成功响应。
      • 返回 false:返回错误响应,提示邮件已发送,请稍后再试。

九、完善邮件发送内容

将生成的验证码添加到邮件正文内容中。

十、测试验证

  1. 测试非法邮件地址:发送格式错误的邮件地址,验证是否被拦截。
  2. 测试已注册邮箱:将邮箱地址设置为已注册的用户,验证是否被拦截。
  3. 测试重复发送:短时间内反复发送邮件,验证是否被拦截。

十一、总结

通过校验邮件地址合法性、检查是否已注册、限制重复发送以及使用 Redis 缓存验证码,完善了邮件发送接口的功能,提高了系统的安全性和稳定性。

课程笔记:注册接口升级与邮箱验证总结

一、注册接口升级

  1. 调整入参

    • 增加 emailaddress(邮箱地址)和 verificationcode(验证码)两个参数。
  2. 增加校验

    • 非空校验:对邮箱和验证码进行非空校验,新建异常类 26email不能为空27验证码不能为空
    • 邮箱是否已注册校验:调用 checkEmailRegister 方法,判断邮箱是否已被注册。
    • 邮箱和验证码匹配校验:在 emailService 中新增 checkEmailAndCode 方法,通过 Redis 获取存储的验证码并与用户传入的验证码进行比对。
  3. 数据存储

    • 注册成功时,将邮箱地址存入数据库用户表中。

二、校验流程

  1. 非空校验:确保用户名、密码、邮箱和验证码均不为空。
  2. 邮箱注册状态校验:防止重复注册。
  3. 验证码匹配校验:确保用户提供的验证码与 Redis 中存储的验证码一致。

三、验证码存储选择

  1. 为什么选择 Redis 而不是数据库
    • 临时性:验证码仅一次有效,注册成功后即无作用,无需长期存储。
    • 过期机制:Redis 提供方便的过期时间设置,自动处理验证码过期,而数据库难以实现自动过期。

四、总结

通过升级注册接口,增加邮箱和验证码参数及相关校验,确保了用户注册流程的安全性和完整性。使用 Redis 存储验证码,利用其过期机制,有效防止了恶意重复注册和验证码滥用。整个流程包括发送验证码、校验邮箱是否已注册、验证码匹配校验以及最终的用户注册,各步骤紧密衔接,确保了用户注册信息的准确性和系统安全性。

课程笔记:登录状态的保存和验证

一、学习背景

在企业级权限认证中,登录是第一步,不仅需要验证用户身份,还要保存登录状态,以便用户后续操作时系统能够识别其身份。

二、HTTP 无状态特性

HTTP 协议是无状态的,意味着每个请求都是独立的,请求之间不携带状态信息。即使前一个请求通过验证,下一个请求仍需重新验证。这要求我们采取措施保存凭证,以解决无状态带来的问题。

三、凭证保存机制

1. 第一次登录验证

用户第一次登录时,需要提供用户名和密码等信息进行严格的身份验证。

2. Session 的创建

验证通过后,服务器为用户创建一个 Session,Session 是保存在服务器端的数据结构,用于跟踪用户状态。

3. Cookie 的作用

服务器返回给客户端一个 Cookie,其中包含 Session ID。客户端(如浏览器)在后续请求中携带此 Cookie,服务器通过 Session ID 找到对应的 Session,从而识别用户身份。

四、Session 和 Cookie 的工作流程

  1. 用户发送登录请求:包含用户名和密码。
  2. 服务器验证并创建 Session:验证通过后,生成 Session 并返回包含 Session ID 的 Cookie。
  3. 客户端保存 Cookie:浏览器保存 Cookie,在后续请求中自动携带。
  4. 服务器识别用户:通过 Cookie 中的 Session ID 找到对应的 Session,获取用户状态和数据。

五、特殊情况处理

如果客户端禁用 Cookie,可以采用 URL 重写的方式,在每个请求的 URL 后附加必要的身份参数,以便服务器进行校验。

六、总结

本小节介绍了登录状态保存和验证的基本原理和流程,重点讲解了 HTTP 无状态特性下如何通过 Session 和 Cookie 解决用户身份识别问题。下一个小节将深入探讨 Session 的细节和应用。

课程笔记:深入理解Session

一、Session 的安全性

  1. 用户空间的独立性

    • 每个用户的Session空间是独立的,即使使用相同的key存储数据,也不会相互影响。
    • Tomcat会为每个用户分配独立的Session ID,每个Session ID对应自己的空间。
  2. Session ID 的生成规则

    • 目标:保证唯一性,防止重复。
    • 方法:通常结合随机数、当前时间(过滤大部分同时触发的情况)和JVM的ID值(区分不同服务器)。

二、Session 劫持与防护

  1. Session 劫持

    • 概念:攻击者窃取用户的Session ID,冒充用户进行操作。
    • 风险:可能导致用户数据泄露、被恶意操作等。
  2. 防护措施

    • HttpOnly 标记:服务器告知客户端,Session Cookie不允许通过前端代码读取,仅允许浏览器读取。
    • Secure 标记:如果项目支持HTTPS,标记Session Cookie仅在HTTPS协议下传输。

三、Session 的缺点

  1. 扩展性差

    • 分布式环境下,多台机器需要同步和复制Session,处理复杂。
  2. 服务端存储压力

    • 用户量大时,存储大量Session数据(无论在内存、Redis还是数据库中)都会带来挑战。

四、实际操作演示

  1. Postman 测试流程

    • 默认Cookie中包含Session ID,每个用户的Session ID唯一。
    • 删除Cookie后请求,服务端会创建新的Session并要求设置Cookie。
    • 设置Cookie后,后续请求携带该Session ID,服务端据此识别用户。
  2. 浏览器中的Session Cookie

    • 查看请求中的Cookie,包含JSession Cookie,代表用户唯一标识。
    • 注意保护Session ID,防止泄露。

五、总结

Session用于保存用户状态,其安全性至关重要。通过理解Session的工作原理、Session ID的生成规则以及劫持与防护措施,可以更好地保障用户数据安全。Session存在扩展性和存储压力的缺点,后续将学习JWT来克服这些问题。

课程笔记:JWT 介绍与原理

一、JWT 的重要性

  1. 独特优势:相比 Session 和 Cookie,JWT 有自身的特点和优势。
  2. 项目应用:后续小节将把项目中原有的 Session 验证方式升级为 JWT。

二、JWT 的基本概念

  1. 定义:JWT(JSON Web Token)是一种流行的用于网站身份验证的认证方案。
  2. 官网:jwt.io ,提供调试工具用于编码和解码。

三、JWT 的组成

JWT 由三部分组成,每部分之间用英文半角句号(.)分隔:

  1. Header(头部)

    • 包含两个功能:签名算法和令牌类型。
    • 示例:{"alg": "HS256", "typ": "JWT"},其中 alg 是签名算法,typ 是令牌类型。
  2. Payload(消息体)

    • 是 JWT 中最重要的部分,用于放置业务相关数据。
    • 示例:{"sub": "1234567890", "name": "John Doe", "iat": 1516239022},其中 name 可改为用户名、身份、用户 ID 等。
  3. Signature(签名)

    • 用于验证消息是否被更改过,保证数据安全。
    • 生成方式:将 Header 和 Payload 编码后,用 Secret 进行签名。

四、JWT 的生成与验证

  1. 生成

    • Header 和 Payload 分别经过 Base64 URL 编码。
    • 使用 Secret 对编码后的 Header 和 Payload 进行签名,生成 Signature。
    • 三部分组合成完整的 JWT。
  2. 验证

    • 服务端解码 JWT,验证 Signature 的有效性。
    • 如果 Signature 无效,说明 JWT 被篡改。

五、Session 与 JWT 的对比

流程对比

  1. Session 流程

    • 客户端发起 HTTP 请求,服务端创建 Session,生成唯一的 Session ID。
    • 服务端要求客户端保存 Session ID 到 Cookie。
    • 后续请求客户端携带 Cookie,服务端通过 Session ID 识别用户。
  2. JWT 流程

    • 客户端发起 HTTP 请求,服务端校验用户名和密码。
    • 校验通过后,服务端将用户信息转换为 JWT 并发送给客户端。
    • 后续请求客户端携带 JWT,服务端解码 JWT 获取用户信息。

优缺点对比

  1. Session

    • 优点:简单方便,适合小规模网站。
    • 缺点
      • 扩展性差:用户量大时,分布式架构下存储和同步 Session 成本高。
      • 需要存储数据:服务端需为每个用户开辟空间存储 Session。
  2. JWT

    • 优点
      • 减少存储开销:服务端无需存储用户信息,直接将信息编码到 JWT 中。
      • 可扩展性强:分布式架构下,各服务器可独立校验 JWT。
      • 可用于交换信息:直接携带用户相关业务数据。
      • 防止篡改:签名机制保证 JWT 的完整性。
    • 缺点
      • 默认不加密:不适合保存敏感信息,如密码。
      • 无法临时废止:一旦发出,无法主动使其失效,需设置合理过期时间。
      • 有效期评估难:过长或过短的过期时间都会带来问题。
      • 网络开销高:相比 Session ID,JWT 字符串较长。

六、总结

JWT 凭借其减少存储开销和良好的扩展性,在互联网公司中应用越来越广泛。尽管存在一些缺点,但通过合理设置和使用,可以充分发挥其优势。后续小节将进入 JWT 的实际开发应用。

课程笔记:项目实战 - 用户校验从Session升级为JWT

一、项目实战目标

将用户校验从传统的Session Cookie升级为JWT。

二、主要修改内容

  1. 登录接口升级:不再保存Session,改为在登录时生成JWT Token并返回给用户。
  2. 过滤器修改:包括用户过滤器和管理员过滤器,以适应JWT校验。
  3. 用户获取方式升级:调整从请求中获取用户信息的方式。

三、实战步骤

1. 引入JWT依赖

pom.xml中添加JWT依赖:

<dependency>
    <groupId>com.off0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.14.0</version>
</dependency>

手动刷新项目,确保依赖正确下载。

2. 修改登录接口

(1) 引入必要的包和工具

确保项目中已引入JWT相关的包和工具。

(2) 来到UserController

找到login接口,准备进行改造。

(3) 保留原有登录逻辑

保留用户名和密码的判空逻辑,以及通过用户名和密码获取用户信息的逻辑。

(4) 生成JWT Token

在原有登录逻辑的基础上,添加JWT Token的生成代码:

// 定义算法
Algorithm algorithm = Algorithm.HMAC256(Constant.JWT_KEY);

// 创建JWT
String token = JWT.create()
    .withClaim(Constant.USERNAME, user.getUsername())
    .withClaim(Constant.USER_ID, user.getId())
    .withClaim(Constant.USER_ROW, user.getRow())
    .withExpiresAt(new Date(System.currentTimeMillis() + Constant.JWT_EXPIRE_TIME))
    .sign(algorithm);

// 返回token
return APIResponse.success(token);

3. 定义常量

Constant类中添加以下常量:

public static final String JWT_KEY = "your_secret_key"; // JWT密钥
public static final String USERNAME = "username"; // 用户名常量
public static final String USER_ID = "user_id"; // 用户ID常量
public static final String USER_ROW = "user_row"; // 用户行常量
public static final long JWT_EXPIRE_TIME = 1000L * 60 * 60 * 24 * 1000; // JWT过期时间(1000天)

四、总结

通过上述步骤,完成了将用户校验从Session升级为JWT的主要工作。登录接口已改造为生成并返回JWT Token,后续请求中客户端需将Token放在请求头中,由服务器进行校验。接下来,还需对过滤器和用户获取方式进行相应升级,以全面支持JWT校验。

课程笔记:项目实战 - JWT 校验与过滤器升级

一、项目重启与环境准备

  1. 重启项目:清理缓存、删除target目录,重新生成项目文件。
  2. 解决包不存在问题
    • 刷新Maven依赖,手动触发下载。
    • 调整IDEA设置(打开override、importing选项,检测JDK版本,配置process resources)。
    • 清空IDEA缓存(File -> Invalidate Caches)。

二、获取JWT Token

  1. 测试新接口:使用用户名和密码调用login for jwt接口。
  2. 验证Token:将返回的Token复制到jwt.io网站,解析查看内容是否包含用户名、用户ID、用户角色和过期时间等信息。

三、过滤器升级

  1. 修改用户过滤器

    • 删除原从Session获取用户信息的代码。
    • 从请求头获取JWT Token(约定Header的Key为jwt token)。
    • 校验Token:
      • 使用Algorithm.HMAC256(Constant.JWT_KEY)生成Algorithm对象。
      • 通过JWT.require(algorithm).build()生成Verifier。
      • 使用Verifier.verify(token)解码Token,获取用户信息并设置到CurrentUser对象中。
    • 处理解码异常:
      • Token过期异常(TokenExpiredException)。
      • Token解码失败异常(JWTDecodeException)。
  2. 完善用户过滤器配置

    • 修改方法名为user filter config。
    • 增加对更新用户信息接口的拦截。

四、总结

完成了JWT Token的生成与校验,以及过滤器的相应升级。用户登录后,通过在请求头中携带JWT Token进行身份校验,取代了原有的Session方式。在实际操作中,要注意处理Maven依赖和IDEA缓存等问题,确保项目顺利运行。


http://www.kler.cn/a/581170.html

相关文章:

  • 《Python基础教程》附录A笔记:简明教程
  • 对Docker的一些基本认识
  • 用ABBYY PDF Transformer+对PDF的创建编辑转换和注释等操作
  • 埋点PV和UV的含义
  • PAT乙级(1101 B是A的多少倍)C语言解析
  • 五、非云原生监控mysql-Exporter
  • 【玩转23种Java设计模式】结构型模式篇:享元模式
  • QT小项目-简单的记事本
  • 1.5 双指针专题:有效三⻆形的个数(medium)
  • Flink之水印(watermark)的补充理解
  • Linux驱动开发-设备树
  • python高效试用17---两个字符串组成一个新的字符串和两个字符串组成元组作为key哪个更高效
  • PyCharm 接入 DeepSeek、OpenAI、Gemini、Mistral等大模型完整版教程(通用)!
  • Qt不同窗口类的控件信号和槽绑定
  • Excel 中如何实现数据透视表?
  • 复现无人机的项目,项目名称为Evidential Detection and Tracking Collaboration
  • NPM安装与配置全流程详解(2025最新版)
  • Python基础之threading多线程同时运行程序
  • 衣联网的商品列表页面结构是怎样的?
  • 前端项目中创建自动化部署脚本,用于 Jenkins 触发 npm run publish 来完成远程部署