核心需求复述
你需要基于SpringCloud微服务架构,为企业电商系统设计一套覆盖C端游客/会员全场景的登录注册模块方案,要求方案完整、思路落地性强,能适配电商业务中登录注册的各类实际场景(如游客转正、多方式登录、登录态管理等)。
一、整体架构设计(SpringCloud环境)
1. 微服务拆分(核心服务边界)
基于“高内聚、低耦合”原则,将登录注册模块拆解为以下微服务(以SpringCloud Alibaba为例):
| 服务名称 | 核心职责 |
|---|---|
| user-center | 核心服务:用户注册、登录、身份校验、游客转正、用户基础信息管理 |
| auth-server | 认证授权:JWT Token生成/验证、刷新Token、多端登录态管理 |
| notify-service | 通知服务:短信/邮箱验证码发送、登录提醒、注册成功通知 |
| oauth-service | 第三方登录:对接微信/支付宝/QQ等第三方账号授权、账号绑定 |
| gateway | 网关服务:接口统一入口、限流、鉴权、路径转发(拦截未登录请求) |
2. 核心技术栈选型
| 技术/组件 | 用途 |
|---|---|
| SpringCloud Alibaba | 微服务基座(Nacos:注册/配置中心;Sentinel:限流;Seata:分布式事务) |
| Spring Security + JWT | 认证授权(Token生成/验证、权限控制) |
| Redis | 缓存验证码、登录Token、游客临时数据、限流计数 |
| MySQL + MyBatis-Plus | 用户数据存储、CRUD(支持分库分表) |
| BCrypt | 密码不可逆加密 |
| OpenFeign | 微服务间接口调用(如user-center调用notify-service发验证码) |
二、核心业务场景与实现思路
1. 基础数据模型设计(核心表)
-- 用户核心表(区分游客/会员)
CREATE TABLE user_info (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id VARCHAR(64) NOT NULL COMMENT '用户唯一标识(雪花算法)',
mobile VARCHAR(11) UNIQUE COMMENT '手机号(会员必填)',
email VARCHAR(64) UNIQUE COMMENT '邮箱',
password VARCHAR(128) COMMENT '密码(BCrypt加密)',
nickname VARCHAR(64) COMMENT '昵称(游客可随机生成)',
avatar VARCHAR(255) COMMENT '头像',
user_type TINYINT NOT NULL DEFAULT 0 COMMENT '0-游客 1-会员',
status TINYINT NOT NULL DEFAULT 1 COMMENT '1-启用 0-禁用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_mobile (mobile),
INDEX idx_user_id (user_id)
) COMMENT 'C端用户信息表';
-- 验证码表(防重复/过期)
CREATE TABLE verify_code (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
target VARCHAR(64) NOT NULL COMMENT '手机号/邮箱',
code VARCHAR(6) NOT NULL COMMENT '6位验证码',
type TINYINT NOT NULL COMMENT '1-注册 2-登录 3-找回密码',
expire_time DATETIME NOT NULL COMMENT '过期时间(5分钟)',
status TINYINT NOT NULL DEFAULT 0 COMMENT '0-未使用 1-已使用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_target_type (target, type)
) COMMENT '验证码记录表';
-- 登录日志表(安全审计)
CREATE TABLE login_log (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id VARCHAR(64) NOT NULL,
login_type TINYINT NOT NULL COMMENT '1-手机号验证码 2-密码 3-第三方',
device VARCHAR(32) COMMENT 'PC/APP/H5',
ip VARCHAR(32) COMMENT '登录IP',
login_time DATETIME DEFAULT CURRENT_TIMESTAMP,
logout_time DATETIME COMMENT '登出时间',
INDEX idx_user_id (user_id)
) COMMENT '用户登录日志表';
-- 第三方绑定表
CREATE TABLE oauth_bind (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id VARCHAR(64) NOT NULL,
oauth_type TINYINT NOT NULL COMMENT '1-微信 2-支付宝 3-QQ',
oauth_openid VARCHAR(64) NOT NULL COMMENT '第三方唯一标识',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_oauth_type_openid (oauth_type, oauth_openid),
INDEX idx_user_id (user_id)
) COMMENT '第三方账号绑定表';
2. 核心场景实现
场景1:游客身份处理(未登录)
电商场景中游客需支持“浏览商品、加购、收藏”等基础操作,核心思路:
- 游客无身份验证,后端生成临时user_id(雪花算法)+ 临时Token(JWT,有效期2小时);
- 游客的购物车、收藏等数据通过临时user_id关联存储(购物车服务/收藏服务);
- 下单前强制引导登录/注册,触发“游客转正”流程。
场景2:会员注册(手机号为主流)
核心流程:获取验证码 → 校验验证码 → 注册入库,具体实现:
// user-center 核心代码示例(注册接口)
@RestController
@RequestMapping("/api/user")
@Slf4j
public class UserRegisterController {
@Autowired
private VerifyCodeService verifyCodeService;
@Autowired
private UserInfoService userInfoService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 1. 发送注册验证码
@PostMapping("/send-register-code")
public ResultVO sendRegisterCode(@RequestParam String mobile) {
// 步骤1:参数校验(手机号格式)
if (!RegexUtils.isMobile(mobile)) {
return ResultVO.error(10001, "手机号格式错误");
}
// 步骤2:限流(同一手机号1分钟内最多1次)
String limitKey = "send_code_limit:" + mobile;
if (redisTemplate.hasKey(limitKey)) {
return ResultVO.error(10002, "验证码发送过于频繁,请稍后再试");
}
// 步骤3:生成6位验证码
String code = RandomUtils.generate6DigitCode();
// 步骤4:缓存验证码(Redis,5分钟过期)
String codeKey = "verify_code:register:" + mobile;
redisTemplate.opsForValue().set(codeKey, code, 5, TimeUnit.MINUTES);
// 步骤5:记录验证码到数据库
verifyCodeService.saveVerifyCode(mobile, code, 1); // 1=注册类型
// 步骤6:调用notify-service发送短信(OpenFeign)
notifyService.sendSms(mobile, "【XX电商】您的注册验证码是:" + code + ",5分钟内有效");
// 步骤7:设置限流缓存(1分钟)
redisTemplate.opsForValue().set(limitKey, "1", 1, TimeUnit.MINUTES);
return ResultVO.success("验证码发送成功");
}
// 2. 提交注册
@PostMapping("/register")
public ResultVO register(@RequestBody RegisterDTO registerDTO) {
// 步骤1:参数校验
String mobile = registerDTO.getMobile();
String code = registerDTO.getCode();
String password = registerDTO.getPassword();
if (StringUtils.isAnyBlank(mobile, code, password)) {
return ResultVO.error(10003, "参数不能为空");
}
// 步骤2:校验验证码
String codeKey = "verify_code:register:" + mobile;
String cacheCode = redisTemplate.opsForValue().get(codeKey);
if (cacheCode == null || !cacheCode.equals(code)) {
return ResultVO.error(10004, "验证码错误或已过期");
}
// 步骤3:校验手机号是否已注册
if (userInfoService.existsByMobile(mobile)) {
return ResultVO.error(10005, "手机号已注册");
}
// 步骤4:密码加密(BCrypt)
String encryptPwd = BCrypt.hashpw(password, BCrypt.gensalt());
// 步骤5:插入用户表(会员)
UserInfo userInfo = new UserInfo();
userInfo.setUserId(SnowFlakeUtil.generateId()); // 雪花算法生成唯一ID
userInfo.setMobile(mobile);
userInfo.setPassword(encryptPwd);
userInfo.setNickname("用户" + mobile.substring(7)); // 默认昵称
userInfo.setUserType(1); // 1=会员
userInfoService.save(userInfo);
// 步骤6:清理验证码缓存+标记验证码为已使用
redisTemplate.delete(codeKey);
verifyCodeService.markCodeUsed(mobile, code, 1);
return ResultVO.success("注册成功", userInfo.getUserId());
}
}
场景3:多方式登录(适配电商核心场景)
| 登录方式 | 核心流程 |
|---|---|
| 手机号+验证码 | 校验验证码 → 查找用户(不存在则自动注册为会员)→ 生成Token → 记录登录日志 |
| 账号密码 | 查找用户 → BCrypt校验密码 → 生成Token → 记录登录日志 |
| 第三方登录(微信) | 前端获取微信code → 后端换openid → 查绑定关系(已绑定:生成Token;未绑定:创建会员+绑定) |
| 游客临时登录 | 生成临时user_id → 生成临时Token → 返回给前端(无身份校验) |
核心登录代码示例(手机号+验证码) :
@PostMapping("/login-by-code")
public ResultVO loginByCode(@RequestBody LoginByCodeDTO loginDTO) {
String mobile = loginDTO.getMobile();
String code = loginDTO.getCode();
// 1. 校验验证码(同注册逻辑)
String codeKey = "verify_code:login:" + mobile;
String cacheCode = redisTemplate.opsForValue().get(codeKey);
if (cacheCode == null || !cacheCode.equals(code)) {
return ResultVO.error(10006, "验证码错误或已过期");
}
// 2. 查找用户(不存在则自动注册)
UserInfo userInfo = userInfoService.getByMobile(mobile);
if (userInfo == null) {
// 自动注册为会员(简化流程)
userInfo = new UserInfo();
userInfo.setUserId(SnowFlakeUtil.generateId());
userInfo.setMobile(mobile);
userInfo.setNickname("用户" + mobile.substring(7));
userInfo.setUserType(1);
userInfoService.save(userInfo);
}
// 3. 生成JWT Token(access_token+refresh_token)
AuthTokenDTO tokenDTO = authService.generateToken(userInfo.getUserId(), userInfo.getUserType());
// 4. 记录登录日志(异步处理,避免阻塞)
loginLogService.asyncSaveLoginLog(userInfo.getUserId(), 1, loginDTO.getDevice(), loginDTO.getIp()); // 1=手机号验证码登录
// 5. 清理验证码缓存
redisTemplate.delete(codeKey);
return ResultVO.success("登录成功", tokenDTO);
}
场景4:登录态管理(电商核心诉求)
- Token刷新:access_token(2小时)过期前,前端调用
/api/user/refresh-token接口,传入refresh_token(7天有效期),后端校验后生成新的access_token; - 多端登录:Redis中存储
user_id:devices(key)→ 设备+Token列表(value),支持“查看登录设备、强制下线某设备(删除对应Token)”; - 自动登录:前端勾选“记住我”时,refresh_token有效期延长至30天,下次打开APP自动刷新access_token。
场景5:游客转正(电商核心场景)
游客下单前需转正为会员,核心是合并临时数据,流程:
- 游客触发登录/注册后,前端传入“游客临时user_id”和“会员Token”;
- 后端校验会员身份 + 游客临时数据有效性;
- 调用购物车服务/收藏服务,将游客的临时购物车、收藏数据合并到会员账号;
- 更新用户表中游客的user_type为“会员”,失效游客临时Token;
- 返回合并成功,引导用户继续下单。
3. 安全设计(电商必备)
- 密码安全:BCrypt不可逆加密,禁止明文存储;
- 验证码安全:限流、Redis缓存防重复使用、5分钟过期;
- Token安全:JWT签名(防止篡改)、Redis存储Token(便于注销/强制下线)、HTTPS传输;
- 接口安全:网关层做XSS/CSRF防护、重要接口加签名校验;
- 数据安全:用户敏感信息脱敏(如手机号显示138****1234)、数据库字段加密(手机号/邮箱)。
4. 性能优化
- 缓存:验证码、Token、用户基础信息存入Redis,减少DB查询;
- 限流:Sentinel对登录/注册/发送验证码接口限流(如QPS=1000),防止恶意攻击;
- 异步:登录日志、短信发送等非核心逻辑用
@Async异步处理; - 分库分表:用户量达千万级时,对user_info表按user_id分表(如按尾号分10张表)。
三、总结
核心关键点回顾
- 架构层面:基于SpringCloud拆分用户中心、认证、通知等微服务,职责单一,便于扩展;
- 业务层面:覆盖游客/会员全生命周期(临时登录→注册→正式登录→转正),适配电商多方式登录场景;
- 安全&性能:通过Redis缓存、限流、密码加密、异步处理,兼顾安全性和高并发能力;
- 落地性:数据模型、核心代码可直接复用,适配企业级电商系统的实际业务需求。
这套方案既满足了C端用户登录注册的基础需求,又适配了电商场景的特殊诉求(游客转正、多端登录、安全审计),同时符合SpringCloud微服务的最佳实践,可直接落地到实际项目中。