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 缓存用户信息
连接池:数据库连接池管理
分库分表:用户数据分片存储
异步处理:日志记录异步化

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐