4、urbane-commerce 认证请求 DTO 设计规范

当然可以!以下是为你的 urbane-commerce 电商微服务系统 中的认证模块(auth-service)量身定制的 LoginRequestRegisterRequestRefreshTokenRequest 三类请求 DTO 的完整企业级实现示例,包含:

  • 字段设计依据与安全考量
  • Java 实体类完整代码(带中文注释)
  • 校验注解使用说明(JSR-303 / Hibernate Validator)
  • 前端调用示例(JSON 格式)
  • 最佳实践总结

📜《urbane-commerce 认证请求 DTO 设计规范》

版本:6.0 | 最后更新:2025年4月 | 技术栈:Spring Boot 3.x + Jakarta Validation + Lombok


✅ 一、总体设计原则

原则 说明
最小化输入 只收必要字段,拒绝"什么都传"
强校验 所有字段必须有明确格式、长度、非空约束
防御性编程 不信任前端,所有输入都做校验
一致性 所有请求 DTO 风格统一,命名规范
可扩展性 支持未来新增字段(如手机登录、微信扫码)

⚠️ 重要提醒

这些类是用户直接提交的入口 ,必须严格校验,否则极易被爆破、撞库、注入攻击!


✅ 二、LoginRequest.java ------ 用户登录请求

🔍 功能

用户通过 用户名 + 密码 登录系统,获取 JWT Token。

✅ 推荐字段

字段 类型 必填 说明
username String ✅ 是 用户名(邮箱或手机号也可,但建议统一为 username)
password String ✅ 是 明文密码(由服务端加密比对)

💡 为什么不支持 email/phone 直接登录?

→ 为统一身份体系,推荐在注册时绑定 username ,登录统一用 username。

→ 若需支持邮箱/手机号登录,可在 UserService 层做映射,Controller 层保持简单

java 复制代码
package io.urbane.auth.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;

/**
 * 用户登录请求数据传输对象(DTO)
 * 功能:
 *   - 前端提交登录凭证:用户名 + 密码
 *   - 用于 AuthService.login() 方法
 *
 * 安全要求:
 *   - 密码必须为明文(由服务端 BCrypt 加密比对)
 *   - 禁止返回密码哈希值
 *   - 输入必须校验长度和非空
 *
 * 校验规则:
 *   - username: 非空,长度 3~30 字符(支持字母、数字、下划线)
 *   - password: 非空,长度 8~128 字符(强制要求复杂度由业务层控制)
 */
@Data
public class LoginRequest {

    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 30, message = "用户名长度必须在 3 到 30 个字符之间")
    private String username;

    @NotBlank(message = "密码不能为空")
    @Size(min = 8, max = 128, message = "密码长度必须在 8 到 128 个字符之间")
    private String password;

    // ==================== 构造函数 ====================

    public LoginRequest() {}

    public LoginRequest(String username, String password) {
        this.username = username;
        this.password = password;
    }

    // ==================== 示例 JSON 请求 ====================
    // {
    //   "username": "zhangsan",
    //   "password": "MyPass123!"
    // }
}

前端调用示例(Vue/React)

js 复制代码
axios.post('/auth/login', {
  username: 'zhangsan',
  password: 'MyPass123!'
})

为什么不用 @Email

因为我们的系统统一使用 username 登录,邮箱仅作为注册字段。若要支持邮箱登录,应在 UserService 中做转换,而不是暴露给 Controller。


✅ 三、RegisterRequest.java ------ 用户注册请求

🔍 功能

新用户提交注册信息(用户名、邮箱、密码),完成账户创建。

✅ 推荐字段

字段 类型 必填 说明
username String ✅ 是 登录名,全局唯一
email String ✅ 是 邮箱地址,用于激活、找回密码
password String ✅ 是 密码,强度由服务端校验
nickname String ❌ 否 昵称(显示名),默认为 username

💡 为什么需要 nickname

→ 用户名可能为邮箱或手机号(如 zhangsan@example.com),不适合展示

→ 昵称是 UI 上显示的名字,如"小张"、"李四"

java 复制代码
package io.urbane.auth.dto;

import jakarta.validation.constraints.*;
import lombok.Data;

import java.util.regex.Pattern;

/**
 * 用户注册请求数据传输对象(DTO)
 * 功能:
 *   - 前端提交注册表单:用户名、邮箱、密码、昵称(可选)
 *   - 服务端校验唯一性、格式、强度后保存用户
 *
 * 安全与合规要求:
 *   - 邮箱必须合法格式(使用 @Email 注解)
 *   - 密码必须满足最小长度(8位)和复杂度(服务端增强校验)
 *   - 用户名不能包含特殊字符(防止注入、路径遍历)
 *   - 邮箱和用户名必须全局唯一
 *
 * 校验规则:
 *   - username: 非空,3~30字符,仅允许字母、数字、下划线
 *   - email: 必须为合法邮箱格式
 *   - password: 非空,8~128字符
 *   - nickname: 可选,最大50字符,避免 XSS(不存储 HTML)
 */
@Data
public class RegisterRequest {

    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 30, message = "用户名长度必须在 3 到 30 个字符之间")
    @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
    private String username;

    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    @Size(max = 100, message = "邮箱长度不能超过 100 个字符")
    private String email;

    @NotBlank(message = "密码不能为空")
    @Size(min = 8, max = 128, message = "密码长度必须在 8 到 128 个字符之间")
    private String password;

    @Size(max = 50, message = "昵称长度不能超过 50 个字符")
    private String nickname; // 可选,默认等于 username

    // ==================== 构造函数 ====================

    public RegisterRequest() {}

    public RegisterRequest(String username, String email, String password) {
        this(username, email, password, null);
    }

    public RegisterRequest(String username, String email, String password, String nickname) {
        this.username = username;
        this.email = email;
        this.password = password;
        this.nickname = nickname != null ? nickname : username; // 默认昵称为用户名
    }

    // ==================== 示例 JSON 请求 ====================
    // {
    //   "username": "zhangsan",
    //   "email": "zhangsan@example.com",
    //   "password": "MyPass123!",
    //   "nickname": "小张"
    // }
}

服务端增强校验(在 Service 层)

java 复制代码
if (!PasswordStrengthValidator.isStrong(password)) {
    throw new IllegalArgumentException("密码强度不足:需包含大小写字母、数字、特殊符号");
}

前端提示

"密码至少8位,需包含大小写字母、数字和特殊符号"


✅ 四、RefreshTokenRequest.java ------ 刷新 Token 请求

🔍 功能

用户使用旧的、即将过期的 JWT Token ,换取一个新的有效 Token,实现"无感续期"。

✅ 推荐字段

字段 类型 必填 说明
token String ✅ 是 当前有效的 JWT Token(Header 中的 Authorization 值)

⚠️ 注意:这个请求不是从 Header 读取 ,而是从 Body 传递,原因如下:

  • 防止浏览器自动携带旧 Token(跨域问题)
  • 避免重复发送两次 Token(网关已校验一次)
  • 更清晰的语义:这是一个"主动刷新"操作,而非"认证请求"
java 复制代码
package io.urbane.auth.dto;

import jakarta.validation.constraints.NotBlank;
import lombok.Data;

/**
 * 刷新 Token 请求数据传输对象(DTO)
 * 功能:
 *   - 用户携带旧 Token 请求生成新 Token
 *   - 用于实现"无感续期"体验(如 App 在后台自动刷新)
 *
 * 使用场景:
 *   - 用户打开 App,Token 即将过期(剩余 < 5min)
 *   - 前端调用 /auth/refresh-token,传入当前 token
 *   - 服务端验证旧 token 有效 → 生成新 token → 返回
 *   - 前端替换本地 token,无需重新登录
 *
 * 安全要求:
 *   - 旧 token 必须未过期、未被吊销
 *   - 每次刷新后,旧 token 必须加入黑名单
 *   - 限制刷新频率(如每小时最多刷新 5 次)
 *
 * 校验规则:
 *   - token: 非空,长度 > 100(JWT 通常 > 300 字符)
 */
@Data
public class RefreshTokenRequest {

    @NotBlank(message = "刷新令牌不能为空")
    @Size(min = 100, message = "令牌格式无效,长度不足")
    private String token;

    // ==================== 构造函数 ====================

    public RefreshTokenRequest() {}

    public RefreshTokenRequest(String token) {
        this.token = token;
    }

    // ==================== 示例 JSON 请求 ====================
    // {
    //   "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    // }
}

前端调用示例(JavaScript)

js 复制代码
const oldToken = localStorage.getItem('token');
const res = await axios.post('/auth/refresh-token', { token: oldToken });
localStorage.setItem('token', res.data.token); // 替换为新 token

为什么不在 Header 中传?

因为 Authorization: Bearer xxx 已经被网关拦截并校验过了。

如果再传一次,会引发双重校验逻辑混乱
Body 传参更清晰、可控、符合 RESTful 设计。


✅ 五、校验注解说明(Hibernate Validator)

注解 作用 适用场景
@NotBlank 非空且去除空格后不为空 username, password, token
@Size(min=8, max=128) 字符串长度范围 密码、昵称、邮箱
@Email 验证邮箱格式 email 字段
@Pattern(regexp="...") 正则匹配 username 只允许字母数字下划线
@NotNull 非空(包括空字符串) 一般不用于 String,优先用 @NotBlank

推荐组合

java 复制代码
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 30)
@Pattern(regexp = "^[a-zA-Z0-9_]+$")
private String username;

校验触发方式

在 Controller 中使用 @Valid

java 复制代码
@PostMapping("/login")
public ResponseEntity<LoginResponse> login(@Valid @RequestBody LoginRequest request) { ... }

错误响应示例(由全局异常处理器捕获):

json 复制代码
{
  "code": 400,
  "message": "用户名长度必须在 3 到 30 个字符之间",
  "path": "/auth/login",
  "timestamp": "2025-04-05T10:30:00Z"
}

✅ 六、进阶建议:未来扩展兼容性

场景 扩展方案
支持手机号登录 新增 mobile 字段,在 LoginRequest 中使用 @JsonCreator@JsonSubTypes 实现多态
支持第三方登录(微信/Google) 新增 SocialLoginRequest,独立 DTO,不混用
支持验证码登录 新增 VerifyCodeLoginRequest,含 codephone
多租户登录 新增 tenantId 字段,可选

建议
不要在一个 DTO 中塞入所有可能性!

应该采用 "单一职责 + 多个 DTO" 设计,例如:

java 复制代码
LoginRequest           ← 用户名+密码登录
MobileLoginRequest     ← 手机号+验证码登录
WeChatLoginRequest     ← 微信扫码登录

→ 更清晰、易维护、易测试


✅ 七、最终总结:黄金法则

维度 推荐做法
字段数量 少而精,只收必要字段
字段类型 使用 String,避免 IntegerBoolean 等容易被篡改的类型
校验层级 Controller 层用 @Valid + 注解Service 层做业务规则校验
安全性 所有输入都视为不可信,禁止直接存入数据库
一致性 所有 Request DTO 命名统一为 [Action]Request
文档化 每个 DTO 都写清楚用途、示例、校验规则
前端对接 提供 JSON 示例,让前端开发人员一看就懂

✅ 附录:三个 DTO 总结对比表

名称 用途 必填字段 是否应校验密码强度 是否应返回 Token
LoginRequest 用户名密码登录 username, password ✅ 是(服务端) ❌ 否(由 LoginResponse 返回)
RegisterRequest 新用户注册 username, email, password ✅ 是(服务端) ❌ 否
RefreshTokenRequest 刷新旧 Token token ❌ 否(Token 已存在) ❌ 否(由 LoginResponse 返回)

记住口诀
"登录查账号,注册查邮箱,刷新查令牌。"


🚀 最终建议:立即行动清单

步骤 操作
✅ 1 创建 dto/LoginRequest.javadto/RegisterRequest.javadto/RefreshTokenRequest.java
✅ 2 复制上方带注释的完整代码
✅ 3 AuthController 中添加 @Valid 校验
✅ 4 编写单元测试,验证非法输入是否返回 400
✅ 5 更新 Swagger/OpenAPI 文档,确保字段描述准确
✅ 6 通知前端团队:这三种请求结构已标准化,请按此格式调用

📦 Bonus:我为你准备了完整 ZIP 模板包

如果你希望我为你提供:

  • 完整的三个 DTO 文件(含注释)
  • 对应的单元测试类(MockMvc 测试 400 错误)
  • Postman Collection(三个接口测试用例)
  • OpenAPI YAML 定义(Swagger 自动生成)
  • Lombok + Validation 依赖配置说明

👉 请回复:
"请给我完整的认证请求 DTO 模板包!"

我会立刻发送你一份开箱即用的企业级认证请求 DTO 完整工程包 ,包含所有文件、注释、测试和文档,你只需复制粘贴,即可让团队进入专业 API 开发时代 💪

相关推荐
一只学java的小汉堡3 小时前
微服务与面向服务编程(SOA)入门指南:从架构演进到 Spring Cloud 实践(初学者友好版)
spring boot·spring cloud·java-ee·maven
程序员烧烤3 小时前
【Java初学基础10】一文讲清反射
java·开发语言
长安——归故李3 小时前
【PLC程序学习】
java·c语言·javascript·c++·python·学习·php
大卫小东(Sheldon)3 小时前
如何用Java25编译Java17的项目
java
笨手笨脚の4 小时前
设计模式-建造者模式
java·设计模式·建造者模式·创建型设计模式
SimonKing4 小时前
SpringBoot多模板引擎整合难题?一篇搞定JSP、Freemarker与Thymeleaf!
java·后端·程序员
Lbwnb丶4 小时前
JUnit 4 + Spring Boot 测试依赖
数据库·spring boot·junit