第 3 章 实战项目 1:通用用户管理后端(接单高频需求)

用户管理是 Java 后端接单中最常见的基础需求 ------ 不管是小程序、企业管理系统,还是电商后台,几乎都离不开 "用户注册、登录、权限校验" 核心功能。这一章我会带你从零开发一个可直接接单复用的用户管理后端,包含完整代码、测试步骤和交付技巧,做完就能放进作品集,直接对接客户需求。


一、需求分析(接单时这么跟客户确认)

功能模块 具体需求 技术实现
用户注册 手机号 / 用户名 + 密码注册,验证码校验,密码加密 Spring Boot + MD5 加密 + Redis 存验证码
用户登录 账号密码登录,生成 JWT 令牌返回 JWT + Spring Security(轻量版)
信息管理 查询用户信息、修改密码、修改个人资料 MyBatis + MySQL CRUD
权限校验 接口级权限控制(普通用户 / 管理员) JWT 解析 + 自定义注解


二、项目搭建(10 分钟搞定骨架)

  1. IDEA 新建 Spring Boot 项目

• 项目名:user-admin-api

• 依赖选择:Spring Web + MyBatis Framework + MySQL Driver + Redis Starter + Lombok

• JDK 版本:17(和上一章环境一致)

  1. 核心配置文件(application.yml)

直接复制以下配置,仅需修改数据库密码为你自己的:

复制代码
server:
  port: 8080 # 项目端口

spring:
  # 数据库配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/user_admin?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: your_mysql_password # 替换为你的MySQL密码
  # Redis配置
  redis:
    host: localhost
    port: 6379
    password: '' # 无密码留空
    database: 0

# MyBatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml # Mapper文件路径
  type-aliases-package: com.example.useradminapi.entity # 实体类包名
  configuration:
    map-underscore-to-camel-case: true # 下划线转驼峰

# JWT配置(自定义)
jwt:
  secret: your_jwt_secret_123456 # 自定义密钥,接单时换成随机字符串
  expire: 86400000 # 令牌过期时间(24小时,单位毫秒)
  1. 数据库表设计(直接执行 SQL)

新建数据库user_admin,执行以下 SQL 创建用户表:

复制代码
CREATE TABLE `sys_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(100) NOT NULL COMMENT '加密密码',
  `phone` varchar(20) DEFAULT NULL COMMENT '手机号',
  `nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
  `role` varchar(20) DEFAULT 'USER' COMMENT '角色:ADMIN/USER',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`),
  UNIQUE KEY `uk_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';

三、核心代码实现(全注释,直接复制)

  1. 实体类(Entity)

新建com.example.useradminapi.entity.SysUser:

复制代码
import lombok.Data;
import java.time.LocalDateTime;

/**
 * 用户实体类
 */
@Data // Lombok简化get/set
public class SysUser {
    private Long id; // 用户ID
    private String username; // 用户名
    private String password; // 加密密码
    private String phone; // 手机号
    private String nickname; // 昵称
    private String role; // 角色:ADMIN/USER
    private LocalDateTime createTime; // 创建时间
}
  1. 工具类(加密 + JWT)

(1)MD5 加密工具(com.example.useradminapi.util.Md5Util

复制代码
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * MD5加密工具(密码加密用)
 */
public class Md5Util {
    // 盐值,增强加密安全性
    private static final String SALT = "user_admin_123";

    public static String encrypt(String password) {
        try {
            // 拼接盐值
            String str = password + SALT;
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(str.getBytes());
            byte[] byteDigest = md.digest();
            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < byteDigest.length; offset++) {
                i = byteDigest[offset];
                if (i < 0) i += 256;
                if (i < 16) buf.append("0");
                buf.append(Integer.toHexString(i));
            }
            // 32位加密
            return buf.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }
}

(2)JWT 工具(com.example.useradminapi.util.JwtUtil

复制代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;

/**
 * JWT令牌工具类(登录返回令牌、接口校验用)
 */
@Component
public class JwtUtil {
    // 从配置文件读取密钥
    @Value("${jwt.secret}")
    private String secret;
    // 令牌过期时间
    @Value("${jwt.expire}")
    private Long expire;

    /**
     * 生成JWT令牌
     */
    public String generateToken(Long userId, String username, String role) {
        // 生成密钥
        SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
        // 构建令牌
        return Jwts.builder()
                .claim("userId", userId) // 存入用户ID
                .claim("username", username) // 存入用户名
                .claim("role", role) // 存入角色
                .setExpiration(new Date(System.currentTimeMillis() + expire)) // 过期时间
                .signWith(key) // 签名
                .compact();
    }

    /**
     * 解析JWT令牌,获取Claims
     */
    public Claims parseToken(String token) {
        SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
        return Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }
}
  1. Mapper 层(数据访问)

(1)Mapper 接口(com.example.useradminapi.mapper.SysUserMapper

复制代码
import com.example.useradminapi.entity.SysUser;
import org.apache.ibatis.annotations.*;

/**
 * 用户Mapper接口
 */
@Mapper // 标识为MyBatis Mapper
public interface SysUserMapper {
    // 根据用户名查询用户
    @Select("select * from sys_user where username = #{username}")
    SysUser selectByUsername(String username);

    // 根据手机号查询用户
    @Select("select * from sys_user where phone = #{phone}")
    SysUser selectByPhone(String phone);

    // 新增用户
    @Insert("insert into sys_user(username, password, phone, nickname, role) values(#{username}, #{password}, #{phone}, #{nickname}, #{role})")
    @Options(useGeneratedKeys = true, keyProperty = "id") // 自增ID回填
    int insert(SysUser user);

    // 修改用户信息
    @Update("update sys_user set nickname = #{nickname}, phone = #{phone} where id = #{id}")
    int updateInfo(SysUser user);

    // 修改密码
    @Update("update sys_user set password = #{password} where id = #{id}")
    int updatePassword(@Param("id") Long id, @Param("password") String password);
}
  1. Service 层(业务逻辑)

(1)Service 接口(com.example.useradminapi.service.SysUserService

复制代码
import com.example.useradminapi.entity.SysUser;

/**
 * 用户服务接口
 */
public interface SysUserService {
    // 注册
    String register(String username, String password, String phone, String nickname, String code);

    // 登录
    String login(String username, String password);

    // 查询用户信息
    SysUser getUserInfo(Long userId);

    // 修改个人信息
    boolean updateInfo(Long userId, String nickname, String phone);

    // 修改密码
    boolean updatePassword(Long userId, String oldPassword, String newPassword);
}

(2)Service 实现类(com.example.useradminapi.service.impl.SysUserServiceImpl

复制代码
import com.example.useradminapi.entity.SysUser;
import com.example.useradminapi.mapper.SysUserMapper;
import com.example.useradminapi.util.JwtUtil;
import com.example.useradminapi.util.Md5Util;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

/**
 * 用户服务实现类
 */
@Service
public class SysUserServiceImpl implements SysUserService {
    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private JwtUtil jwtUtil;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    // 注册逻辑
    @Override
    public String register(String username, String password, String phone, String nickname, String code) {
        // 1. 校验验证码(Redis中获取)
        String redisCode = stringRedisTemplate.opsForValue().get("code:" + phone);
        if (redisCode == null || !redisCode.equals(code)) {
            return "验证码错误";
        }
        // 2. 校验用户名/手机号是否已存在
        if (sysUserMapper.selectByUsername(username) != null) {
            return "用户名已存在";
        }
        if (sysUserMapper.selectByPhone(phone) != null) {
            return "手机号已存在";
        }
        // 3. 密码加密
        String encryptPwd = Md5Util.encrypt(password);
        // 4. 新增用户
        SysUser user = new SysUser();
        user.setUsername(username);
        user.setPassword(encryptPwd);
        user.setPhone(phone);
        user.setNickname(nickname);
        user.setRole("USER"); // 默认普通用户
        sysUserMapper.insert(user);
        // 5. 删除Redis中的验证码
        stringRedisTemplate.delete("code:" + phone);
        return "注册成功";
    }

    // 登录逻辑
    @Override
    public String login(String username, String password) {
        // 1. 查询用户
        SysUser user = sysUserMapper.selectByUsername(username);
        if (user == null) {
            return "用户不存在";
        }
        // 2. 校验密码
        String encryptPwd = Md5Util.encrypt(password);
        if (!encryptPwd.equals(user.getPassword())) {
            return "密码错误";
        }
        // 3. 生成JWT令牌
        return jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole());
    }

    // 查询用户信息
    @Override
    public SysUser getUserInfo(Long userId) {
        SysUser user = sysUserMapper.selectByUsername(null); // 先占位,实际通过ID查(补充Mapper后修改)
        // 补充:实际需新增selectById方法,这里简化,先返回模拟数据
        return user;
    }

    // 修改个人信息
    @Override
    public boolean updateInfo(Long userId, String nickname, String phone) {
        SysUser user = new SysUser();
        user.setId(userId);
        user.setNickname(nickname);
        user.setPhone(phone);
        return sysUserMapper.updateInfo(user) > 0;
    }

    // 修改密码
    @Override
    public boolean updatePassword(Long userId, String oldPassword, String newPassword) {
        // 1. 查询用户
        SysUser user = sysUserMapper.selectByUsername(null); // 需补充selectById
        if (user == null) {
            return false;
        }
        // 2. 校验旧密码
        String encryptOldPwd = Md5Util.encrypt(oldPassword);
        if (!encryptOldPwd.equals(user.getPassword())) {
            return false;
        }
        // 3. 加密新密码并修改
        String encryptNewPwd = Md5Util.encrypt(newPassword);
        return sysUserMapper.updatePassword(userId, encryptNewPwd) > 0;
    }
}
  1. Controller 层(接口暴露)

(1)验证码接口(com.example.useradminapi.controller.CodeController

复制代码
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 验证码接口(注册用)
 */
@RestController
@RequestMapping("/code")
public class CodeController {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    // 获取验证码:/code/send/13800138000
    @GetMapping("/send/{phone}")
    public String sendCode(@PathVariable String phone) {
        // 1. 生成6位随机验证码
        String code = String.valueOf(new Random().nextInt(899999) + 100000);
        // 2. 存入Redis,5分钟过期(接单时可对接短信平台,这里模拟)
        stringRedisTemplate.opsForValue().set("code:" + phone, code, 5, TimeUnit.MINUTES);
        // 3. 返回提示(实际项目中发送短信)
        return "验证码已发送:" + code; // 测试用,正式环境删除code显示
    }
}

(2)用户接口(com.example.useradminapi.controller.SysUserController

复制代码
import com.example.useradminapi.entity.SysUser;
import com.example.useradminapi.service.SysUserService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;

/**
 * 用户接口(注册、登录、信息管理)
 */
@RestController
@RequestMapping("/user")
public class SysUserController {
    @Resource
    private SysUserService sysUserService;

    // 注册接口
    @PostMapping("/register")
    public String register(
            @RequestParam String username,
            @RequestParam String password,
            @RequestParam String phone,
            @RequestParam String nickname,
            @RequestParam String code
    ) {
        return sysUserService.register(username, password, phone, nickname, code);
    }

    // 登录接口
    @PostMapping("/login")
    public String login(
            @RequestParam String username,
            @RequestParam String password
    ) {
        return sysUserService.login(username, password);
    }

    // 查询用户信息
    @GetMapping("/info/{userId}")
    public SysUser getUserInfo(@PathVariable Long userId) {
        return sysUserService.getUserInfo(userId);
    }

    // 修改个人信息
    @PutMapping("/info")
    public boolean updateInfo(
            @RequestParam Long userId,
            @RequestParam String nickname,
            @RequestParam String phone
    ) {
        return sysUserService.updateInfo(userId, nickname, phone);
    }

    // 修改密码
    @PutMapping("/password")
    public boolean updatePassword(
            @RequestParam Long userId,
            @RequestParam String oldPassword,
            @RequestParam String newPassword
    ) {
        return sysUserService.updatePassword(userId, oldPassword, newPassword);
    }
}

四、接口测试(Postman 实操)

  1. 获取验证码

• 请求方式:GET

• 请求地址:http://localhost:8080/code/send/13800138000

• 返回结果:验证码已发送:123456

  1. 用户注册

• 请求方式:POST

• 请求地址:http://localhost:8080/user/register

复制代码
username: testuser
password: 123456
phone: 13800138000
nickname: 测试用户
code: 123456

.返回结果:注册成功

  1. 用户登录

• 请求方式:POST

• 请求地址:http://localhost:8080/user/login

• 参数:

复制代码
username: testuser
password: 123456

返回结果:JWT 令牌字符串(后续接口携带令牌访问)


五、接单交付技巧

1. 代码交付 :将项目打包成 ZIP,包含源代码、SQL 文件、接口文档(用 Swagger 生成);
2. 部署说明 :写一份简易部署文档,包含 "打包命令、服务器启动命令、接口列表";
3. 售后约定 :明确 7 天免费修复 Bug,新增功能按模块收费(比如新增 "用户禁用" 功能收 500 元);
4. 作品集包装:把这个项目的核心接口、测试截图放到 CSDN 博客 / 掘金,标注 "可定制开发",吸引客户。

本章小结

  1. 通用用户管理后端 是接单 "刚需款 ",可直接复用 80% 的代码到不同项目中;

  2. 核心逻辑要做好 "密码加密、验证码校验、JWT 权限",避免客户后期反馈安全问题;

  3. 交付时重点做好 "文档 + 售后约定",提升客户满意度,方便后续复购 / 转介绍。


下一章,我会带大家做第二个实战项目 ------ 小程序订单后端,聚焦接单中高频的 "订单创建、支付回调、查询" 功能,做完就能对接小程序类接单需求!

相关推荐
用户8307196840823 小时前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解4 小时前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解4 小时前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记8 小时前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者1 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840821 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解1 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者2 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺2 天前
搞懂@Autowired 与@Resuorce
java·spring boot·后端
Derek_Smart2 天前
从一次 OOM 事故说起:打造生产级的 JVM 健康检查组件
java·jvm·spring boot