企业级 Java 登录注册系统构建指南(附核心代码与配置)
本文介绍了Java登录注册系统的完整实现方案。系统采用三层架构设计,包含表现层、业务逻辑层和数据访问层。数据库设计采用用户表结构,包含密码加密、账户锁定等安全字段。核心实体类User实现了账户状态判断等业务方法。安全工具类SecurityUtil提供盐值生成、密码加密验证等功能,强调密码安全存储原则。数据访问层定义了用户查询、保存等基本操作接口。整体方案注重安全性,采用随机盐值、SHA-256加密
目录
Java 登录注册系统完整指南
1. 系统架构概览
三层架构模式:
表现层 (Controller) → 业务逻辑层 (Service) → 数据访问层 (DAO) → 数据库 (MySQL/Oracle)
Controller
| 组件 | 职责 | 主要类 |
|---|---|---|
| Controller | 处理HTTP请求,参数校验 | UserController |
Service
| 组件 | 职责 | 主要类 |
|---|---|---|
| Service | 业务逻辑处理,事务管理 | UserService |
DAO
| 组件 | 职责 | 主要类 |
|---|---|---|
| DAO | 数据库操作 | UserDAO |
Entity
| 组件 | 职责 | 主要类 |
|---|---|---|
| Entity | 数据模型 | User |
Security
| 组件 | 职责 | 主要类 |
|---|---|---|
| Security | 安全验证,加密 | SecurityUtil |
2.数据库设计
用户表设计 (users)
CREATE TABLE users (
id BIGINT PRIMARY KEY PRIMARY AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL, -- 加密后的密码
salt VARCHAR(50) NOT NULL, -- 密码盐值
status TINYINT DEFAULT 1, -- 0:禁用 1:启用
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
last_login_time TIMESTAMP NULL, -- 最后登录时间
login_attempts INT DEFAULT 0, -- 登录失败次数
locked_until TIMESTAMP NULL -- 账户锁定到期时间
);
设计要点:
密码使用 BCrypt 或 SHA-256 + 盐值加密存储
增加登录失败次数控制,防止暴力破解
记录最后登录时间,便于安全审计
支持账户锁定机制
3. 核心实体类
User 实体类
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户实体类
*/
@Data
@NoArgsConstructor
public class User implements Serializable {
// 序列化版本号
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String email;
private String password;
private String salt;
private Integer status;
private Date createTime;
private Date updateTime;
private Date lastLoginTime;
private Integer loginAttempts;
private Date lockedUntil;
// 业务方法
/**
* 判断账户是否被锁定
* @return 锁定状态(true:已锁定,false:未锁定)
*/
public boolean isAccountLocked() {
return lockedUntil != null && lockedUntil.after(new Date());
}
/**
* 判断账户是否启用
* @return 启用状态(true:启用,false:禁用)
*/
public boolean isEnabled() {
return status == 1;
}
@Override
public String toString() {
return "User{id=" + id + ", username='" + username + "'}";
}
}
4. 安全工具类
密码加密与验证
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Base64;
/**
* 安全工具类
* 提供密码加密、盐值生成、密码验证和Token生成等安全相关操作
*/
public class SecurityUtil {
/**
* 生成随机盐值(16字节)
* @return Base64编码的盐值字符串
*/
public static String generateSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}
/**
* 使用SHA-256算法加密密码
* @param password 原始密码
* @param salt 盐值
* @return 加密后的密码(Base64编码)
*/
public static String hashPassword(String password, String salt) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt.getBytes());
byte[] hashedPassword = md.digest(password.getBytes());
return Base64.getEncoder().encodeToString(hashedPassword);
} catch (Exception e) {
throw new RuntimeException("密码加密失败", e);
}
}
/**
* 验证密码是否匹配
* @param inputPassword 输入的原始密码
* @param storedPassword 存储的加密后密码
* @param salt 盐值
* @return 密码是否匹配(true:匹配,false:不匹配)
*/
public static boolean verifyPassword(String inputPassword, String storedPassword, String salt) {
String hashedInput = hashPassword(inputPassword, salt);
return hashedInput.equals(storedPassword);
}
/**
* 生成随机Token(32字节)
* 用于会话管理、身份验证等场景
* @return Base64编码的Token字符串
*/
public static String generateToken() {
SecureRandom random = new SecureRandom();
byte[] token = new byte[32];
random.nextBytes(token);
return Base64.getEncoder().encodeToString(token);
}
}
安全提示:
永远不要明文存储密码
每个密码使用独立的盐值
考虑使用更强的加密算法如 BCrypt
实施密码强度策略
5. 数据访问层 (DAO)
UserDAO 接口与实现
UserDAO.java
import java.util.Date;
/**
* 用户数据访问接口
* 定义用户相关的数据库操作方法
*/
public interface UserDAO {
/**
* 根据用户名查询用户
* @param username 用户名
* @return 用户实体,不存在则返回null
*/
User findByUsername(String username);
/**
* 根据邮箱查询用户
* @param email 邮箱地址
* @return 用户实体,不存在则返回null
*/
User findByEmail(String email);
/**
* 保存新用户
* @param user 用户实体
* @return 保存成功返回true,否则返回false
*/
boolean save(User user);
/**
* 更新用户最后登录时间
* @param userId 用户ID
* @return 更新成功返回true,否则返回false
*/
boolean updateLastLoginTime(Long userId);
/**
* 更新登录失败次数
* @param userId 用户ID
* @param attempts 失败次数
* @return 更新成功返回true,否则返回false
*/
boolean updateLoginAttempts(Long userId, int attempts);
/**
* 锁定用户账户
* @param userId 用户ID
* @param lockUntil 锁定截止时间
* @return 操作成功返回true,否则返回false
*/
boolean lockAccount(Long userId, Date lockUntil);
}
UserDAOImpl.java
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
/**
* 用户数据访问实现类
* 实现用户相关的数据库操作
*/
public class UserDAOImpl implements UserDAO {
private final Connection connection;
/**
* 构造方法,注入数据库连接
* @param connection 数据库连接对象
*/
public UserDAOImpl(Connection connection) {
this.connection = connection;
}
@Override
public User findByUsername(String username) {
String sql = "SELECT * FROM users WHERE username = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, username);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return mapResultSetToUser(rs);
}
} catch (SQLException e) {
throw new RuntimeException("根据用户名查询用户失败: " + username, e);
}
return null;
}
@Override
public User findByEmail(String email) {
String sql = "SELECT * FROM users WHERE email = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, email);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return mapResultSetToUser(rs);
}
} catch (SQLException e) {
throw new RuntimeException("根据邮箱查询用户失败: " + email, e);
}
return null;
}
@Override
public boolean save(User user) {
String sql = "INSERT INTO users (username, email, password, salt) VALUES (?, ?, ?, ?)";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, user.getUsername());
stmt.setString(2, user.getEmail());
stmt.setString(3, user.getPassword());
stmt.setString(4, user.getSalt());
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
throw new RuntimeException("保存用户失败: " + user.getUsername(), e);
}
}
@Override
public boolean updateLastLoginTime(Long userId) {
String sql = "UPDATE users SET last_login_time = CURRENT_TIMESTAMP WHERE id = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setLong(1, userId);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
throw new RuntimeException("更新用户最后登录时间失败: " + userId, e);
}
}
@Override
public boolean updateLoginAttempts(Long userId, int attempts) {
String sql = "UPDATE users SET login_attempts = ?, update_time = CURRENT_TIMESTAMP WHERE id = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setInt(1, attempts);
stmt.setLong(2, userId);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
throw new RuntimeException("更新用户登录失败次数失败: " + userId, e);
}
}
@Override
public boolean lockAccount(Long userId, Date lockUntil) {
String sql = "UPDATE users SET locked_until = ?, update_time = CURRENT_TIMESTAMP WHERE id = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setTimestamp(1, new Timestamp(lockUntil.getTime()));
stmt.setLong(2, userId);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
throw new RuntimeException("锁定用户账户失败: " + userId, e);
}
}
/**
* 结果集映射为User实体
* @param rs 数据库结果集
* @return User实体对象
* @throws SQLException SQL异常
*/
private User mapResultSetToUser(ResultSet rs) throws SQLException {
User user = new User();
user.setId(rs.getLong("id"));
user.setUsername(rs.getString("username"));
user.setEmail(rs.getString("email"));
user.setPassword(rs.getString("password"));
user.setSalt(rs.getString("salt"));
user.setStatus(rs.getInt("status"));
user.setCreateTime(rs.getTimestamp("create_time"));
user.setUpdateTime(rs.getTimestamp("update_time"));
user.setLastLoginTime(rs.getTimestamp("last_login_time"));
user.setLoginAttempts(rs.getInt("login_attempts"));
user.setLockedUntil(rs.getTimestamp("locked_until"));
return user;
}
}
6. 业务逻辑层 (Service)
UserService 核心业务逻辑
import java.util.Date;
/**
* 用户服务类
* 处理用户注册、登录等核心业务逻辑
*/
public class UserService {
private final UserDAO userDAO;
// 最大登录失败次数
private static final int MAX_LOGIN_ATTEMPTS = 5;
// 账户锁定时长(30分钟,单位:毫秒)
private static final long LOCK_DURATION = 30 * 60 * 1000;
/**
* 构造方法,注入用户数据访问对象
* @param userDAO 用户数据访问接口实现
*/
public UserService(UserDAO userDAO) {
this.userDAO = userDAO;
}
/**
* 用户注册
* @param username 用户名
* @param email 邮箱地址
* @param password 原始密码
* @return 注册是否成功
*/
public boolean register(String username, String email, String password) {
// 1. 参数合法性校验
if (!isValidUsername(username) || !isValidEmail(email) || !isValidPassword(password)) {
throw new IllegalArgumentException("参数格式不正确:用户名3-50位,邮箱格式正确,密码至少6位");
}
// 2. 检查用户名和邮箱是否已存在
if (userDAO.findByUsername(username) != null) {
throw new RuntimeException("用户名已存在");
}
if (userDAO.findByEmail(email) != null) {
throw new RuntimeException("邮箱已存在");
}
// 3. 密码加密处理
String salt = SecurityUtil.generateSalt();
String hashedPassword = SecurityUtil.hashPassword(password, salt);
// 4. 创建用户对象并保存
User user = new User();
user.setUsername(username);
user.setEmail(email);
user.setPassword(hashedPassword);
user.setSalt(salt);
return userDAO.save(user);
}
/**
* 用户登录
* @param username 用户名
* @param password 原始密码
* @return 登录成功的用户对象
*/
public User login(String username, String password) {
// 1. 根据用户名查询用户
User user = userDAO.findByUsername(username);
if (user == null) {
throw new RuntimeException("用户不存在");
}
// 2. 检查账户状态
if (!user.isEnabled()) {
throw new RuntimeException("账户已被禁用");
}
if (user.isAccountLocked()) {
throw new RuntimeException("账户已被锁定,请稍后再试");
}
// 3. 验证密码
if (!SecurityUtil.verifyPassword(password, user.getPassword(), user.getSalt())) {
handleLoginFailure(user);
}
// 4. 登录成功处理
handleLoginSuccess(user);
return user;
}
/**
* 处理登录失败逻辑
* @param user 当前登录用户
*/
private void handleLoginFailure(User user) {
int attempts = user.getLoginAttempts() + 1;
userDAO.updateLoginAttempts(user.getId(), attempts);
// 达到最大失败次数,锁定账户
if (attempts >= MAX_LOGIN_ATTEMPTS) {
Date lockUntil = new Date(System.currentTimeMillis() + LOCK_DURATION);
userDAO.lockAccount(user.getId(), lockUntil);
throw new RuntimeException("登录失败次数过多,账户已被锁定30分钟");
}
throw new RuntimeException("密码错误,还可尝试 " + (MAX_LOGIN_ATTEMPTS - attempts) + " 次");
}
/**
* 处理登录成功逻辑
* @param user 当前登录用户
*/
private void handleLoginSuccess(User user) {
// 重置登录失败次数
userDAO.updateLoginAttempts(user.getId(), 0);
// 更新最后登录时间
userDAO.updateLastLoginTime(user.getId());
}
/**
* 校验用户名格式
* @param username 用户名
* @return 格式是否有效
*/
private boolean isValidUsername(String username) {
return username != null && username.length() >= 3 && username.length() <= 50;
}
/**
* 校验邮箱格式
* @param email 邮箱地址
* @return 格式是否有效
*/
private boolean isValidEmail(String email) {
if (email == null) {
return false;
}
// 简单邮箱格式正则校验
return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
}
/**
* 校验密码格式
* @param password 密码
* @return 格式是否有效
*/
private boolean isValidPassword(String password) {
return password != null && password.length() >= 6;
}
}
7. 控制器层 (Controller)
UserController - Web 层处理
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
/**
* 用户控制器
* 处理用户注册、登录、登出等HTTP请求
*/
@RestController
@RequestMapping("/api/user")
public class UserController {
private final UserService userService;
/**
* 构造方法注入用户服务
* @param userService 用户服务对象
*/
public UserController(UserService userService) {
this.userService = userService;
}
/**
* 用户注册接口
* @param request 注册请求参数
* @param httpRequest HTTP请求对象
* @return 注册结果响应
*/
@PostMapping("/register")
public ResponseEntity<Map<String, Object>> register(
@RequestBody RegisterRequest request,
HttpServletRequest httpRequest) {
Map<String, Object> response = new HashMap<>();
try {
// 参数验证
if (request.getUsername() == null || request.getPassword() == null || request.getEmail() == null) {
response.put("success", false);
response.put("message", "必填参数不能为空");
return ResponseEntity.badRequest().body(response);
}
// 调用服务层执行注册
boolean success = userService.register(
request.getUsername(),
request.getEmail(),
request.getPassword()
);
response.put("success", success);
response.put("message", success ? "注册成功" : "注册失败");
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 用户登录接口
* @param request 登录请求参数
* @param httpRequest HTTP请求对象
* @param httpResponse HTTP响应对象
* @return 登录结果响应
*/
@PostMapping("/login")
public ResponseEntity<Map<String, Object>> login(
@RequestBody LoginRequest request,
HttpServletRequest httpRequest,
HttpServletResponse httpResponse) {
Map<String, Object> response = new HashMap<>();
try {
// 调用服务层执行登录
User user = userService.login(request.getUsername(), request.getPassword());
// 生成会话token
String token = SecurityUtil.generateToken();
// 设置认证Cookie
Cookie cookie = new Cookie("auth_token", token);
cookie.setHttpOnly(true);
cookie.setSecure(true); // 生产环境使用HTTPS时启用
cookie.setMaxAge(24 * 60 * 60); // 24小时有效期
cookie.setPath("/"); // 设置Cookie路径
httpResponse.addCookie(cookie);
// 将用户信息存储到session
HttpSession session = httpRequest.getSession();
session.setAttribute("user_id", user.getId());
session.setAttribute("username", user.getUsername());
// 构建响应数据
response.put("success", true);
response.put("message", "登录成功");
response.put("user", Map.of(
"id", user.getId(),
"username", user.getUsername(),
"email", user.getEmail()
));
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 用户登出接口
* @param request HTTP请求对象
* @param response HTTP响应对象
* @return 登出结果响应
*/
@PostMapping("/logout")
public ResponseEntity<Map<String, Object>> logout(
HttpServletRequest request,
HttpServletResponse response) {
// 清除session
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
// 清除认证Cookie
Cookie cookie = new Cookie("auth_token", null);
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("message", "退出成功");
return ResponseEntity.ok(result);
}
}
/**
* 注册请求DTO
* 接收注册接口的请求参数
*/
class RegisterRequest {
private String username;
private String email;
private String password;
// Getter和Setter方法
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
/**
* 登录请求DTO
* 接收登录接口的请求参数
*/
class LoginRequest {
private String username;
private String password;
// Getter和Setter方法
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
配置文件
# Spring Boot 主配置文件
spring:
# 应用基本信息
application:
name: bushuousers # 应用名称
# 环境配置 (dev/test/prod,当前激活开发环境)
profiles:
active: dev
# 数据源配置(MySQL)
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver # MySQL 8.x 驱动类
url: jdbc:mysql://localhost:3306/bushuo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root # 数据库用户名,默认 root(支持环境变量注入)
password: 123456 # 数据库密码,默认 123456(支持环境变量注入)
# Hikari 连接池配置(Spring Boot 默认连接池)
hikari:
minimum-idle: 5 # 最小空闲连接数
maximum-pool-size: 20 # 最大连接数
auto-commit: true # 自动提交事务
idle-timeout: 30000 # 空闲连接超时时间(30秒)
pool-name: UserAuthHikariCP # 连接池名称
max-lifetime: 1800000 # 连接最大生命周期(30分钟)
connection-timeout: 30000 # 连接获取超时时间(30秒)
# JPA/Hibernate 配置(ORM 框架)
jpa:
hibernate:
ddl-auto: update # 表结构自动更新(开发环境用,生产环境建议改为 validate)
show-sql: true # 控制台打印 SQL 语句
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect # MySQL 8.x 方言
format_sql: true # 格式化 SQL 语句(便于调试)
# Redis 配置(用于会话存储、缓存)
redis:
host: localhost # Redis 服务地址
port: 6379 # Redis 端口
# Redis 密码(默认空,支持环境变量注入)
database: 0 # Redis 数据库索引(默认 0)
timeout: 2000ms # 连接超时时间(2秒)
# Jedis 连接池配置
jedis:
pool:
max-active: 20 # 最大活跃连接数
max-idle: 10 # 最大空闲连接数
min-idle: 5 # 最小空闲连接数
max-wait: 2000ms # 连接获取最大等待时间(2秒)
# 会话配置(基于 Redis 存储会话)
session:
store-type: redis # 会话存储类型(redis/memory/jdbc)
timeout: 1800s # 会话超时时间(30分钟)
redis:
namespace: "user:session" # Redis 会话键前缀(避免键冲突)
# Web 配置
mvc:
pathmatch:
matching-strategy: ant_path_matcher # 路径匹配策略(兼容老版本 Ant 风格)
web:
resources:
static-locations: classpath:/static/ # 静态资源路径(CSS/JS/图片等)
cache-period: 3600 # 静态资源缓存时间(1小时)
# 邮件配置(用于注册验证、密码找回)
# mail:
# host: smtp.qq.com # 邮件服务器地址(QQ 邮箱示例)
# port: 587 # 邮件服务器端口(TLS 协议默认 587)
# username: ${MAIL_USERNAME:your-email@qq.com} # 发件人邮箱(支持环境变量注入)
# password: ${MAIL_PASSWORD:your-auth-code} # 邮箱授权码(非登录密码,支持环境变量注入)
# properties:
# mail:
# smtp:
# auth: true # 启用 SMTP 认证
# starttls:
# enable: true # 启用 STARTTLS 加密(安全传输)
# 服务器配置
server:
port: 8080 # 应用端口
servlet:
context-path: / # 应用上下文路径(默认 /,如需前缀可改为 /api 等)
session:
timeout: 30m # 服务器会话超时时间(与 Spring 会话超时保持一致)
cookie:
http-only: true # 启用 HttpOnly(防止 JS 读取 Cookie,提升安全性)
secure: false # 启用 Secure(仅 HTTPS 传输 Cookie,生产环境建议设为 true)
max-age: 1800 # Cookie 最大存活时间(30分钟,与会话超时保持一致)
# 错误页面配置
error:
whitelabel:
enabled: false # 禁用默认错误页面(自定义错误页面时需关闭)
include-message: always # 错误响应中包含错误信息
include-binding-errors: always # 错误响应中包含参数绑定错误
# 日志配置
logging:
level: # 日志级别(DEBUG < INFO < WARN < ERROR)
com.example.demo: DEBUG # 自定义包日志级别(调试用)
org.springframework.security: DEBUG # Spring Security 日志级别(调试权限问题用)
org.springframework.web: INFO # Spring Web 日志级别
org.hibernate.SQL: DEBUG # Hibernate SQL 日志级别(打印执行的 SQL)
org.hibernate.type.descriptor.sql.BasicBinder: TRACE # 打印 SQL 参数(调试参数绑定用)
pattern: # 日志格式
console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n" # 控制台日志格式(简洁版)
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" # 日志文件格式(含线程、日志器)
file: # 日志文件配置
name: logs/user-auth.log # 日志文件路径及名称(项目根目录下 logs 文件夹)
max-size: 10MB # 单个日志文件最大大小(超过后切割)
max-history: 30 # 日志文件保留历史天数(30天)
# 应用自定义配置(业务相关配置,可通过 @ConfigurationProperties 注入)
controller → service → mapper → 数据库
(前端请求) (业务处理) (数据访问)
model贯穿各层,用于数据传递;utils为各层提供通用支持。
可能与最终版本可能有差距,但是不急。gitee
请等待后续扩展功能
▼
常见的扩展功能:
邮箱验证:注册后发送验证邮件
找回密码:通过邮箱重置密码
记住我:使用持久化Cookie
多因素认证:短信验证码、Google Authenticator
第三方登录:微信、QQ、微博登录
用户权限:角色权限管理系统
设备管理:记录登录设备,异地登录提醒
性能优化:
缓存:使用 Redis 缓存用户信息
连接池:数据库连接池管理
分库分表:用户数据分片存储
异步处理:日志记录异步化
更多推荐



所有评论(0)