【基于 Spring Boot 3 + Spring Security + MyBatis-Plus 构建的用户登录功能】

用户登录功能设计文档(代码片段版)

1. 项目概述

本文档对youlai-boot项目的用户登录功能进行全面分析,梳理完整的实现流程,包括多种登录方式、安全验证机制、令牌管理等核心功能。

2. 相关代码文件及代码片段

2.1 认证控制器层

AuthController.java - 提供多种登录接口的核心实现:

java 复制代码
@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
@Slf4j
public class AuthController {

    @Operation(summary = "账号密码登录")
    @PostMapping("/login")
    @Log(value = "登录", module = LogModuleEnum.LOGIN)
    public Result<AuthenticationToken> login(
            @Parameter(description = "用户名", example = "admin") @RequestParam String username,
            @Parameter(description = "密码", example = "123456") @RequestParam String password
    ) {
        AuthenticationToken authenticationToken = authService.login(username, password);
        return Result.success(authenticationToken);
    }

    @Operation(summary = "短信验证码登录")
    @PostMapping("/login/sms")
    public Result<AuthenticationToken> loginBySms(
            @Parameter(description = "手机号", example = "18812345678") @RequestParam String mobile,
            @Parameter(description = "验证码", example = "1234") @RequestParam String code
    ) {
        AuthenticationToken loginResult = authService.loginBySms(mobile, code);
        return Result.success(loginResult);
    }

    @Operation(summary = "微信小程序登录(Code)")
    @PostMapping("/wx/miniapp/code-login")
    public Result<AuthenticationToken> loginByWxMiniAppCode(@RequestBody @Valid WxMiniAppCodeLoginDTO loginDTO) {
        AuthenticationToken token = authService.loginByWxMiniAppCode(loginDTO);
        return Result.success(token);
    }
}

2.2 认证服务层

AuthServiceImpl.java - 账号密码登录的核心实现:

java 复制代码
@Override
public AuthenticationToken login(String username, String password) {
    // 1. 创建用于密码认证的令牌(未认证)
    UsernamePasswordAuthenticationToken authenticationToken =
            new UsernamePasswordAuthenticationToken(username.trim(), password);

    // 2. 执行认证(认证中)
    Authentication authentication = authenticationManager.authenticate(authenticationToken);

    // 3. 认证成功后生成 JWT 令牌,并存入 Security 上下文,供登录日志 AOP 使用(已认证)
    AuthenticationToken authenticationTokenResponse =
            tokenManager.generateToken(authentication);
    SecurityContextHolder.getContext().setAuthentication(authentication);
    return authenticationTokenResponse;
}

2.3 安全认证配置

SecurityConfig.java - 安全过滤器链配置:

java 复制代码
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(requestMatcherRegistry -> {
                        // 配置无需登录即可访问的公开接口
                        String[] ignoreUrls = securityProperties.getIgnoreUrls();
                        if (ArrayUtil.isNotEmpty(ignoreUrls)) {
                            requestMatcherRegistry.requestMatchers(ignoreUrls).permitAll();
                        }
                        // 其他所有请求需登录后访问
                        requestMatcherRegistry.anyRequest().authenticated();
                    }
            )
            // 限流过滤器
            .addFilterBefore(new RateLimiterFilter(redisTemplate, configService), UsernamePasswordAuthenticationFilter.class)
            // 验证码校验过滤器
            .addFilterBefore(new CaptchaValidationFilter(redisTemplate, codeGenerator), UsernamePasswordAuthenticationFilter.class)
            // 验证和解析过滤器
            .addFilterBefore(new TokenAuthenticationFilter(tokenManager), UsernamePasswordAuthenticationFilter.class)
            .build();
}

2.4 令牌管理

JwtTokenManager.java - JWT令牌生成核心逻辑:

java 复制代码
@Override
public AuthenticationToken generateToken(Authentication authentication) {
    int accessTokenTimeToLive = securityProperties.getSession().getAccessTokenTimeToLive();
    int refreshTokenTimeToLive = securityProperties.getSession().getRefreshTokenTimeToLive();

    String accessToken = generateToken(authentication, accessTokenTimeToLive);
    String refreshToken = generateToken(authentication, refreshTokenTimeToLive, true);

    return AuthenticationToken.builder()
            .accessToken(accessToken)
            .refreshToken(refreshToken)
            .tokenType("Bearer")
            .expiresIn(accessTokenTimeToLive)
            .build();
}

2.5 认证过滤器

TokenAuthenticationFilter.java - 令牌认证校验核心逻辑:

java 复制代码
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

    String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);

    try {
        if (StrUtil.isNotBlank(authorizationHeader)
                && authorizationHeader.startsWith(SecurityConstants.BEARER_TOKEN_PREFIX)) {

            // 剥离Bearer前缀获取原始令牌
            String rawToken = authorizationHeader.substring(SecurityConstants.BEARER_TOKEN_PREFIX.length());

            // 执行令牌有效性检查(包含密码学验签和过期时间验证)
            boolean isValidToken = tokenManager.validateToken(rawToken);
            if (!isValidToken) {
                WebResponseHelper.writeError(response, ResultCode.ACCESS_TOKEN_INVALID);
                return;
            }

            // 将令牌解析为 Spring Security 上下文认证对象
            Authentication authentication = tokenManager.parseToken(rawToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
    } catch (Exception ex) {
        // 安全上下文清除保障(防止上下文残留)
        SecurityContextHolder.clearContext();
        WebResponseHelper.writeError(response, ResultCode.ACCESS_TOKEN_INVALID);
        return;
    }

    // 继续后续过滤器链执行
    filterChain.doFilter(request, response);
}

2.6 认证提供者

SmsAuthenticationProvider.java - 短信验证码认证核心逻辑:

java 复制代码
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String mobile = (String) authentication.getPrincipal();
    String inputVerifyCode = (String) authentication.getCredentials();

    // 根据手机号获取用户信息
    UserAuthCredentials userAuthCredentials = userService.getAuthCredentialsByMobile(mobile);

    if (userAuthCredentials == null) {
        throw new UsernameNotFoundException("用户不存在");
    }

    // 检查用户状态是否有效
    if (ObjectUtil.notEqual(userAuthCredentials.getStatus(), 1)) {
        throw new DisabledException("用户已被禁用");
    }

    // 校验发送短信验证码的手机号是否与当前登录用户一致
    String cacheKey = StrUtil.format(RedisConstants.Captcha.SMS_LOGIN_CODE, mobile);
    String cachedVerifyCode = (String) redisTemplate.opsForValue().get(cacheKey);

    if (!StrUtil.equals(inputVerifyCode, cachedVerifyCode)) {
        throw new CaptchaValidationException("验证码错误");
    } else {
        // 验证成功后删除验证码
        redisTemplate.delete(cacheKey);
    }

    // 构建认证后的用户详情信息
    SysUserDetails userDetails = new SysUserDetails(userAuthCredentials);

    // 创建已认证的 SmsAuthenticationToken
    return SmsAuthenticationToken.authenticated(
            userDetails,
            userDetails.getAuthorities()
    );
}

2.7 认证模型

AuthenticationToken.java - 认证令牌响应对象:

java 复制代码
@Schema(description = "认证令牌响应对象")
@Data
@Builder
public class AuthenticationToken {

    @Schema(description = "令牌类型", example = "Bearer")
    private String tokenType;

    @Schema(description = "访问令牌")
    private String accessToken;

    @Schema(description = "刷新令牌")
    private String refreshToken;

    @Schema(description = "过期时间(单位:秒)")
    private Integer expiresIn;

}

3. 后端API调用信息

3.1 账号密码登录

  • 接口URL : /api/v1/auth/login

  • 请求方法: POST

  • 请求参数 :

    json 复制代码
    {
      "username": "string",    // 用户名
      "password": "string"     // 密码
    }
  • 响应格式 :

    json 复制代码
    {
      "code": "00000",
      "msg": "success",
      "data": {
        "tokenType": "Bearer",
        "accessToken": "string",
        "refreshToken": "string",
        "expiresIn": 7200
      }
    }

3.2 短信验证码登录

  • 接口URL : /api/v1/auth/login/sms

  • 请求方法: POST

  • 请求参数 :

    json 复制代码
    {
      "mobile": "string",      // 手机号
      "code": "string"         // 短信验证码
    }

3.3 微信小程序登录

  • 接口URL : /api/v1/auth/wx/miniapp/code-login

  • 请求方法: POST

  • 请求参数 :

    json 复制代码
    {
      "code": "string"          // 微信小程序登录码
    }

4. 用户登录调用流程

4.1 账号密码登录流程

前端 AuthController AuthService AuthenticationManager UserDetailsService TokenManager Redis UserMapper POST /api/v1/auth/login login(username, password) 创建UsernamePasswordAuthenticationToken authenticate(token) loadUserByUsername(username) 查询用户信息 返回用户数据 返回UserDetails 密码校验(PasswordEncoder) 返回认证对象 generateToken(authentication) 生成JWT令牌 存储令牌信息 返回AuthenticationToken 返回认证结果 返回令牌信息 前端 AuthController AuthService AuthenticationManager UserDetailsService TokenManager Redis UserMapper

4.2 短信验证码登录流程

前端 AuthController AuthService AuthenticationManager SmsAuthenticationProvider UserService Redis TokenManager POST /api/v1/auth/login/sms loginBySms(mobile, code) 创建SmsAuthenticationToken authenticate(token) authenticate(token) getAuthCredentialsByMobile(mobile) 获取缓存验证码 返回验证码 验证码比对 删除已使用验证码 返回认证对象 返回认证结果 generateToken(authentication) 返回AuthenticationToken 返回认证结果 返回令牌信息 前端 AuthController AuthService AuthenticationManager SmsAuthenticationProvider UserService Redis TokenManager

5. 关键业务逻辑说明

5.1 多种登录方式支持

系统支持五种登录方式,每种方式都有对应的认证提供者:

  1. 账号密码登录 : 使用UsernamePasswordAuthenticationToken和默认的DaoAuthenticationProvider
  2. 短信验证码登录 : 使用SmsAuthenticationTokenSmsAuthenticationProvider
  3. 微信登录 : 使用WxMiniAppCodeAuthenticationTokenWxMiniAppCodeAuthenticationProvider
  4. 微信小程序Code登录: 同上,使用微信Code进行登录
  5. 微信小程序手机号登录 : 使用WxMiniAppPhoneAuthenticationTokenWxMiniAppPhoneAuthenticationProvider

5.2 JWT令牌管理

JWT令牌包含以下关键信息:

  • 用户ID、部门ID、数据权限范围
  • 用户角色和权限信息
  • 令牌类型(访问令牌/刷新令牌)
  • 过期时间和唯一标识(JTI)

5.3 安全验证机制

  1. 密码加密: 使用BCryptPasswordEncoder进行密码加密
  2. 令牌黑名单: 用户注销或修改密码时将令牌加入Redis黑名单
  3. 限流控制: 基于IP的QPS限流,防止暴力破解
  4. 验证码校验: 登录接口需要验证码验证

5.4 用户状态检查

登录时会检查用户状态:

  • 用户是否存在
  • 用户是否被禁用(status=1为正常)
  • 验证码是否正确(短信登录)

6. 安全验证机制分析

6.1 认证过滤器链

系统配置了多层安全过滤器:

  1. RateLimitingFilter: IP限流,防止暴力破解
  2. CaptchaValidationFilter: 验证码校验,防止机器人攻击
  3. TokenAuthenticationFilter: JWT令牌认证,保护API接口

6.2 令牌安全机制

  1. JWT签名验证: 使用HS256算法和密钥进行签名验证
  2. 过期时间控制: 访问令牌2小时过期,刷新令牌7天过期
  3. 黑名单机制: 支持令牌注销和密码变更后的令牌失效
  4. 令牌刷新: 支持使用刷新令牌获取新的访问令牌

6.3 数据安全

  1. 密码安全: 使用BCrypt加密算法,不可逆加密
  2. 敏感信息: 用户手机号等敏感信息加密存储
  3. SQL注入防护: 使用MyBatis参数化查询

6.4 接口安全

  1. 白名单机制: 配置无需认证的公开接口
  2. 权限控制: 基于角色的访问控制(RBAC)
  3. 请求限流: 基于IP的请求频率限制

7. 总结

youlai-boot项目的用户登录功能设计完善,支持多种登录方式,具有完善的安全验证机制。系统采用Spring Security框架,结合JWT令牌实现无状态认证,通过多层过滤器保障系统安全。整体设计考虑了扩展性、安全性和用户体验,为后续的权限管理和用户功能提供了坚实的基础。

相关推荐
皮卡龙1 天前
Java常用的JSON
java·开发语言·spring boot·json
源码获取_wx:Fegn08951 天前
基于springboot + vue健身房管理系统
java·开发语言·前端·vue.js·spring boot·后端·spring
Mr1ght1 天前
为什么 InheritableThreadLocal 在 Spring 线程池中“偶尔”能传递变量?——一次线程池上下文传播的误解
java·spring
摇滚侠1 天前
面试实战 问题三十三 Spring 事务常用注解
数据库·spring·面试
JIngJaneIL1 天前
基于Java+ vue智慧医药系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
2501_916766541 天前
【Spring框架】SpringJDBC
java·后端·spring
谷哥的小弟1 天前
Spring Framework源码解析——ApplicationContextInitializer
java·spring·源码
+VX:Fegn08951 天前
计算机毕业设计|基于springboot + vue图书管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
黄俊懿1 天前
【深入理解SpringCloud微服务】Seata(AT模式)源码解析——开启全局事务
java·数据库·spring·spring cloud·微服务·架构·架构师
Dwzun1 天前
基于SpringBoot+Vue的二手书籍交易平台系统【附源码+文档+部署视频+讲解)
java·vue.js·spring boot·后端·spring·计算机毕业设计