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

一、需求分析(接单时这么跟客户确认)
| 功能模块 | 具体需求 | 技术实现 |
|---|---|---|
| 用户注册 | 手机号 / 用户名 + 密码注册,验证码校验,密码加密 | Spring Boot + MD5 加密 + Redis 存验证码 |
| 用户登录 | 账号密码登录,生成 JWT 令牌返回 | JWT + Spring Security(轻量版) |
| 信息管理 | 查询用户信息、修改密码、修改个人资料 | MyBatis + MySQL CRUD |
| 权限校验 | 接口级权限控制(普通用户 / 管理员) | JWT 解析 + 自定义注解 |
二、项目搭建(10 分钟搞定骨架)
- IDEA 新建 Spring Boot 项目
• 项目名:user-admin-api
• 依赖选择:Spring Web + MyBatis Framework + MySQL Driver + Redis Starter + Lombok
• JDK 版本:17(和上一章环境一致)
- 核心配置文件(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小时,单位毫秒)
- 数据库表设计(直接执行 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='用户表';
三、核心代码实现(全注释,直接复制)
- 实体类(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; // 创建时间
}
- 工具类(加密 + 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();
}
}
- 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);
}
- 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;
}
}
- 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 实操)
- 获取验证码
• 请求方式:GET
• 请求地址:http://localhost:8080/code/send/13800138000
• 返回结果:验证码已发送:123456
- 用户注册
• 请求方式:POST
• 请求地址:http://localhost:8080/user/register
username: testuser
password: 123456
phone: 13800138000
nickname: 测试用户
code: 123456
.返回结果:注册成功
- 用户登录
• 请求方式:POST
• 请求地址:http://localhost:8080/user/login
• 参数:
username: testuser
password: 123456
返回结果:JWT 令牌字符串(后续接口携带令牌访问)
五、接单交付技巧
1. 代码交付 :将项目打包成 ZIP,包含源代码、SQL 文件、接口文档(用 Swagger 生成);
2. 部署说明 :写一份简易部署文档,包含 "打包命令、服务器启动命令、接口列表";
3. 售后约定 :明确 7 天免费修复 Bug,新增功能按模块收费(比如新增 "用户禁用" 功能收 500 元);
4. 作品集包装:把这个项目的核心接口、测试截图放到 CSDN 博客 / 掘金,标注 "可定制开发",吸引客户。
本章小结
-
通用用户管理后端 是接单 "刚需款 ",可直接复用 80% 的代码到不同项目中;
-
核心逻辑要做好 "密码加密、验证码校验、JWT 权限",避免客户后期反馈安全问题;
-
交付时重点做好 "文档 + 售后约定",提升客户满意度,方便后续复购 / 转介绍。
下一章,我会带大家做第二个实战项目 ------ 小程序订单后端,聚焦接单中高频的 "订单创建、支付回调、查询" 功能,做完就能对接小程序类接单需求!