用户管理系统 04 实现后端登录功能 | Java新手实战 | 最小架构 | 期末实训 | Java+SpringBoot+Vue3

用户管理系统 04 实现后端登录功能 Java新手实战 最小架构 期末实训 Java+SpringBoot+Vue3

B站视频教程:https://space.bilibili.com/3493114532596161/lists/6609623?type=season

关注B站"90后小陈老师 ",私信回复"Java"获取资料!

手把手教你实现安全的用户登录功能,掌握企业级开发核心技能

本文是用户管理系统实战系列的第四篇,重点讲解如何实现用户登录功能。作为系统最核心的功能之一,登录模块涉及参数校验、数据查询、安全处理等多个重要知识点。

🎯 本章学习目标

  • ✅ 掌握用户登录的业务流程设计
  • ✅ 学会参数校验和异常处理
  • ✅ 理解Session会话管理机制
  • ✅ 掌握用户数据脱敏处理技巧

🏗️ 登录功能架构设计

登录流程全景图

复制代码
前端请求
    ↓
Controller层(参数基础校验)
    ↓
Service层(业务逻辑处理)
    ├── 参数格式校验
    ├── 数据库查询验证
    ├── 密码匹配判断
    ├── 用户状态检查
    └── 数据脱敏处理
    ↓
Session存储登录状态
    ↓
返回脱敏后的用户信息

🔧 代码实现详解

1. Controller层实现 - 请求入口

位置src/main/java/com/jackson/usermanager/controller/UserController.java

java 复制代码
package com.jackson.usermanager.controller;

import com.jackson.usermanager.common.Result;
import com.jackson.usermanager.model.domain.User;
import com.jackson.usermanager.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;

@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {
    
    private final UserService userService;
    
    /**
     * 用户登录接口
     * @param phone 手机号
     * @param pwd 密码
     * @param session HTTP会话
     * @return 登录结果
     */
    @PostMapping("/login")
    public Result<User> login(@RequestParam String phone, 
                            @RequestParam String pwd,
                            HttpSession session) {
        
        // 1. 基础参数校验
        if (!StringUtils.hasText(phone) || !StringUtils.hasText(pwd)) {
            return Result.error("手机号和密码不能为空");
        }
        
        // 2. 调用Service层处理登录逻辑
        Result<User> result = userService.login(phone, pwd);
        
        // 3. 登录成功,将用户信息存入Session
        if (result.getCode() == 200) {
            User user = result.getData();
            session.setAttribute("loginUser", user);
            session.setMaxInactiveInterval(30 * 60); // 30分钟超时
        }
        
        return result;
    }
}

2. Service层实现 - 核心业务逻辑

位置src/main/java/com/jackson/usermanager/service/UserService.java(接口)

java 复制代码
package com.jackson.usermanager.service;

import com.jackson.usermanager.common.Result;
import com.jackson.usermanager.model.domain.User;

public interface UserService {
    
    /**
     * 用户登录
     * @param phone 手机号
     * @param pwd 密码
     * @return 登录结果
     */
    Result<User> login(String phone, String pwd);
}

位置src/main/java/com/jackson/usermanager/service/impl/UserServiceImpl.java(实现类)

java 复制代码
package com.jackson.usermanager.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jackson.usermanager.common.Result;
import com.jackson.usermanager.mapper.UserMapper;
import com.jackson.usermanager.model.domain.User;
import com.jackson.usermanager.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.regex.Pattern;

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
    
    private final UserMapper userMapper;
    
    // 手机号正则表达式
    private static final String PHONE_REGEX = "^1[3-9]\\d{9}$";
    // 密码最小长度
    private static final int MIN_PASSWORD_LENGTH = 8;
    
    @Override
    public Result<User> login(String phone, String pwd) {
        
        // 1. 参数格式校验
        Result<String> validateResult = validateLoginParams(phone, pwd);
        if (validateResult.getCode() != 200) {
            return Result.error(validateResult.getMessage());
        }
        
        // 2. 查询用户信息(先根据手机号查询)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", phone);
        User user = userMapper.selectOne(queryWrapper);
        
        // 3. 验证用户是否存在
        if (user == null) {
            return Result.error("用户不存在");
        }
        
        // 4. 验证密码
        if (!validatePassword(pwd, user.getPwd())) {
            return Result.error("密码错误");
        }
        
        // 5. 检查用户状态
        if (user.getStatus() != null && user.getStatus() == 1) {
            return Result.error("账户已被禁用");
        }
        
        // 6. 数据脱敏处理
        User safeUser = desensitizeUserInfo(user);
        
        return Result.success("登录成功", safeUser);
    }
    
    /**
     * 登录参数校验
     */
    private Result<String> validateLoginParams(String phone, String password) {
        // 手机号格式校验
        if (!patternMatches(PHONE_REGEX, phone)) {
            return Result.error("手机号格式不正确");
        }
        
        // 密码长度校验
        if (password.length() < MIN_PASSWORD_LENGTH) {
            return Result.error("密码长度不能少于8位");
        }
        
        return Result.success("参数校验通过");
    }
    
    /**
     * 正则校验工具方法
     */
    private boolean patternMatches(String regex, String input) {
        if (!StringUtils.hasText(input)) {
            return false;
        }
        Pattern pattern = Pattern.compile(regex);
        return pattern.matcher(input).matches();
    }
    
    /**
     * 密码验证(后续会加入加密逻辑)
     */
    private boolean validatePassword(String inputPassword, String storedPassword) {
        // 暂时使用明文比较,后续会加入加密逻辑
        return inputPassword.equals(storedPassword);
    }
    
    /**
     * 用户信息脱敏
     */
    private User desensitizeUserInfo(User user) {
        User safeUser = new User();
        safeUser.setId(user.getId());
        safeUser.setNickName(user.getNickName());
        safeUser.setPhone(desensitizePhone(user.getPhone()));
        safeUser.setRole(user.getRole());
        // 敏感信息不返回:密码、状态等
        return safeUser;
    }
    
    /**
     * 手机号脱敏:138****1234
     */
    private String desensitizePhone(String phone) {
        if (StringUtils.hasText(phone) && phone.length() == 11) {
            return phone.substring(0, 3) + "****" + phone.substring(7);
        }
        return phone;
    }
}

3. Mapper层增强 - 添加查询方法

位置src/main/java/com/jackson/usermanager/mapper/UserMapper.java

java 复制代码
package com.jackson.usermanager.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jackson.usermanager.model.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserMapper extends BaseMapper<User> {
    
    /**
     * 根据手机号查询用户信息
     */
    @Select("SELECT * FROM user WHERE phone = #{phone} AND status = 0")
    User selectByPhone(@Param("phone") String phone);
}

4. 统一返回结果类

位置src/main/java/com/jackson/usermanager/common/Result.java

java 复制代码
package com.jackson.usermanager.common;

import lombok.Data;
import java.io.Serializable;

@Data
public class Result<T> implements Serializable {
    private Integer code;    // 状态码:200成功,500失败
    private String message;  // 返回信息
    private T data;         // 返回数据
    
    // 成功静态方法
    public static <T> Result<T> success(String message, T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMessage(message);
        result.setData(data);
        return result;
    }
    
    public static <T> Result<T> success(T data) {
        return success("操作成功", data);
    }
    
    public static <T> Result<T> success(String message) {
        return success(message, null);
    }
    
    // 错误静态方法
    public static <T> Result<T> error(String message) {
        Result<T> result = new Result<>();
        result.setCode(500);
        result.setMessage(message);
        return result;
    }
}

5. 常量类定义

位置src/main/java/com/jackson/usermanager/constant/LoginConstants.java

java 复制代码
package com.jackson.usermanager.constant;

public class LoginConstants {
    public static final int MIN_PASSWORD_LENGTH = 8;
    public static final int SESSION_TIMEOUT = 30 * 60;
    public static final String PHONE_REGEX = "^1[3-9]\\d{9}$";
}

6. 全局异常处理器

位置src/main/java/com/jackson/usermanager/handler/GlobalExceptionHandler.java

java 复制代码
package com.jackson.usermanager.handler;

import com.jackson.usermanager.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    
    @ExceptionHandler(Exception.class)
    public Result<String> handleException(Exception e) {
        log.error("系统异常:", e);
        return Result.error("系统异常:" + e.getMessage());
    }
}

⚙️ 配置文件更新

application.yml 完整配置

yaml 复制代码
server:
  port: 8080
  servlet:
    context-path: /api
  session:
    timeout: 1800  # Session超时时间30分钟(单位:秒)

spring:
  application:
    name: user-manager
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/user-manager?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456

# MyBatis配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

# 日志配置
logging:
  level:
    com.jackson.usermanager: debug

🧪 功能测试指南

1. 使用Postman测试接口

请求示例

复制代码
POST http://localhost:8080/api/user/login
Content-Type: application/x-www-form-urlencoded

phone=13800138000&pwd=12345678

预期响应

json 复制代码
{
    "code": 200,
    "message": "登录成功",
    "data": {
        "id": 1,
        "nickName": "测试用户",
        "phone": "138****8000",
        "role": 0
    }
}

2. 测试用例覆盖场景

测试场景 输入数据 预期结果
正常登录 正确手机号+密码 登录成功,返回脱敏信息
手机号为空 空手机号+密码 "手机号和密码不能为空"
密码过短 正确手机号+"123" "密码长度不能少于8位"
用户不存在 未注册手机号+密码 "用户不存在"
密码错误 正确手机号+错误密码 "密码错误"

🔒 安全注意事项

1. 数据脱敏的重要性

  • 保护隐私:防止用户敏感信息泄露
  • 安全规范:符合数据安全法律法规要求
  • 最佳实践:只返回前端必需的最小信息集

2. Session安全配置

java 复制代码
// 设置Session超时时间
session.setMaxInactiveInterval(30 * 60);

// 重要操作前验证登录状态
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null) {
    return Result.error("未登录或登录已过期");
}

🌟 优化亮点总结

1. 代码结构优化

  • 常量提取:将魔法数字提取为常量类
  • 统一返回:标准化接口响应格式
  • 异常处理:全局异常处理器统一处理

2. 业务逻辑优化

  • 分层校验:Controller/Service各司其职
  • 查询优化:先查用户再验证密码,明确错误类型
  • 数据脱敏:保护用户隐私信息

3. 安全性提升

  • Session管理:登录状态维护
  • 参数校验:防止非法输入
  • 错误信息:明确的错误提示

🎯 下一步学习预告

第五篇:用户注册功能实现

  • 密码加密存储(BCrypt)
  • 手机号唯一性校验
  • 注册流程优化
  • 异常情况处理

💫 本章总结

通过本章学习,我们完成了:

✅ 核心功能实现

  1. 完整的登录业务流程 - 从参数校验到结果返回
  2. 分层架构实践 - Controller/Service/Mapper各司其职
  3. 数据安全处理 - 参数校验+数据脱敏
  4. Session会话管理 - 登录状态维护

💡 新手成长要点

  1. 理解业务流程比记住代码更重要
  2. 分层设计思想是JavaWeb开发的核心
  3. 安全意识要从第一个功能开始培养

🚀 立即实践建议

按照文章步骤亲手实现登录功能,并使用Postman进行完整测试!


📌 下篇预告:下一篇我们将实现用户注册功能,重点讲解密码加密和唯一性校验,让你的系统更加安全完善!

💬 互动话题:你在实现登录功能时遇到过哪些问题?或者对哪个技术点比较困惑?欢迎在评论区交流讨论!

相关推荐
超级大只老咪13 小时前
数组相邻元素比较的循环条件(Java竞赛考点)
java
小浣熊熊熊熊熊熊熊丶13 小时前
《Effective Java》第25条:限制源文件为单个顶级类
java·开发语言·effective java
毕设源码-钟学长13 小时前
【开题答辩全过程】以 公交管理系统为例,包含答辩的问题和答案
java·eclipse
啃火龙果的兔子13 小时前
JDK 安装配置
java·开发语言
星哥说事13 小时前
应用程序监控:Java 与 Web 应用的实践
java·开发语言
派大鑫wink14 小时前
【JAVA学习日志】SpringBoot 参数配置:从基础到实战,解锁灵活配置新姿势
java·spring boot·后端
xUxIAOrUIII14 小时前
【Spring Boot】控制器Controller方法
java·spring boot·后端
Dolphin_Home14 小时前
从理论到实战:图结构在仓库关联业务中的落地(小白→中级,附完整代码)
java·spring boot·后端·spring cloud·database·广度优先·图搜索算法
醇氧14 小时前
org.jetbrains.annotations的@Nullable 学习
java·开发语言·学习·intellij-idea
Java&Develop14 小时前
Aes加密 GCM java
java·开发语言·python