本文使用springboot+mybatis-plus实现用户表的后端代码,包含加密功能。
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`password_hash` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`salt` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`grade` int NOT NULL,
`role` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC;
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.11</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>UserDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>UserDemo</name>
<description>UserDemo</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.6</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.6.3.Final</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<!-- 如果要使用 jjwt 的实现,还需要添加以下依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<!-- Hibernate Validator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
# ?????
spring.datasource.url=jdbc:mysql://localhost:3306/exam4?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# ???????
server.port=8081
# MyBatis-Plus ??
mybatis-plus.mapper-locations=classpath:/mapper/*.xml
mybatis-plus.type-aliases-package=com.example.demo.entity
mybatis-plus.global-config.db-config.id-type=auto
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.configuration.use-generated-keys=true
mybatis-plus.configuration.map-enum-as-ordinal=false
mybatis-plus.configuration.enum-handler=com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler
项目结构
import lombok.AllArgsConstructor;
import lombok.Data;
import com.baomidou.mybatisplus.annotation.*;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String avatar;
private String passwordHash;
private String salt;
private int grade;
private String role;
}
import lombok.Data;
import org.springframework.lang.Nullable;
@Data
public class Result<T> {
private int code;
private String message;
@Nullable
private T data;
public static <T> Result<T> success(T data) {
return success(data, "操作成功");
}
public static <T> Result<T> success(T data, String message) {
return new Result<>(200, message, data);
}
public static <T> Result<T> fail(String message) {
return new Result<>(500, message, null);
}
public Result(int code, String message, @Nullable T data) {
this.code = code;
this.message = message;
this.data = data;
}
}
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 安全相关工具类
* 提供各种安全相关的操作方法
*/
public class SecurityTool {
/**
* 对字符串进行MD5加密
* @param str 待加密的字符串
* @return 加密结果
*/
public static String MD5Encode(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes()); //对字符串加密
byte[] encodedBytes = md.digest();
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < encodedBytes.length; i++) {
String hex = Integer.toHexString(0xff & encodedBytes[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
//省略异常处理
}
return null;
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 允许来自本地的8080端口发起的跨域请求
registry.addMapping("/api/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true).maxAge(3600);
}
};
}
}
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.userdemo.Bean.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.userdemo.Bean.User;
public interface IUserService extends IService<User> {
/**
* 用户注册
* @param user 待注册用户信息
* @return 注册成功返回true,失败返回false
*/
boolean register(User user);
/**
* 用户登录
* @param username 用户名
* @param password 密码
* @return 登录成功返回对应用户的信息,失败返回null
*/
User login(String username, String password);
/**
* 根据用户名获取用户
* @param username 用户名
* @return 用户
*/
User getByUsername(String username);
}
import java.util.ArrayList;
import java.util.Random;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
/**
* 用户注册实现
* 先查用户名是否存在,不存在则加密密码进行保存
* @param user 待注册用户信息
* @return 注册成功返回true,失败返回false
*/
@Override
public boolean register(User user) {
String username = user.getUsername();
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", username); //查询用户名是否存在
User hasUser = getOne(wrapper);
if (hasUser != null) { //数据库中已经存在该用户名,注册失败
return false;
}
// 随机生成一个头像
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("https://pic3.zhimg.com/v2-d6ddf0128212235dddf76df7f4383f53.jpg");
arrayList.add("https://p0.ssl.img.360kuai.com/t01948ff2341a5d1ac3.jpg");
arrayList.add("https://pic3.zhimg.com/v2-65020b1231ba55d55ee3d6a29ff3df26_r.jpg");
arrayList.add("https://pic2.zhimg.com/v2-fc348d5e926116782149d2151dc09834.jpg");
arrayList.add("https://pic4.zhimg.com/v2-797973e16edcd0ccaab44cfbfa08d2d3_r.jpg");
Random random = new Random();
user.setAvatar(arrayList.get(random.nextInt(arrayList.size())));
String password = user.getPasswordHash();
String salt = RandomStringUtils.randomAlphabetic(6); //生成盐
String encryptedPwd = SecurityTool.MD5Encode(password + salt); //对密码+盐进行加密
user.setPasswordHash(encryptedPwd);
user.setSalt(salt);
boolean successFlag = save(user);
return successFlag;
}
@Override
public User getByUsername(String username) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", username);
return getOne(queryWrapper);
}
/**
* 用户登录实现
* 先使用用户名查询对应用户,然后进行密码比对
* @param username 用户名
* @param password 密码
* @return 如果成功则返回对应用户信息(不包含密码和盐),失败返回null
*/
@Override
public User login(String username, String password) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", username); //根据用户名找到对应的User对象
User user = getOne(wrapper);
if (user == null) { //未找到该用户
return null;
}
String salt = user.getSalt();
String encryptedPwd = SecurityTool.MD5Encode(password + salt); //对密码+盐进行加密
if (!encryptedPwd.equals(user.getPasswordHash())) { //密码错误
return null;
}
user.setPasswordHash(null); //不暴露密码hash与盐值Salt给前端
user.setSalt(null);
return user; //一切正常,返回相应用户信息
}
}
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.example.userdemo.Bean.Result;
import com.example.userdemo.Bean.User;
import com.example.userdemo.Service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
/**
* 获取全部用户信息列表
*/
@GetMapping("/all")
public Result<List<User>> listAllUsers() {
List<User> userList = userService.list();
return Result.success(userList);
}
/**
* 根据用户ID获取用户信息
*/
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable(value = "id") Long id) {
User user = userService.getById(id);
if (user == null) {
return Result.fail("该用户不存在");
}
return Result.success(user);
}
/**
* 更新用户信息
*/
@PostMapping("/save")
public Result<Boolean> UpdateUser(@RequestBody User user) {
// 判断新的username是否合法
if (StringUtils.isBlank(user.getUsername())) {
return Result.fail("更新失败:用户名不能为空");
}
// 校验用户名是否重复,如果已经存在就返回错误信息
User existingUser = userService.getByUsername(user.getUsername());
if (existingUser != null && !existingUser.getId().equals(user.getId())) {
return Result.fail("更新失败:该用户名已被占用");
}
// 获取旧用户并更新部分信息
User oldUser = userService.getById(user.getId());
if (oldUser == null) {
return Result.fail("更新失败:该用户不存在");
}
oldUser.setUsername(user.getUsername());
oldUser.setGrade(user.getGrade());
oldUser.setPasswordHash(user.getPasswordHash());
boolean flag = userService.save(oldUser);
if (flag) {
return Result.success(true, "保存成功");
} else {
return Result.fail("保存失败");
}
}
/**
* 根据用户ID删除用户信息
*/
@DeleteMapping("/delete/{id}")
public Result<Boolean> deleteUserById(@PathVariable(value = "id") Long id) {
boolean flag = userService.removeById(id);
if (flag) {
return Result.success(true, "删除成功");
} else {
return Result.fail("删除失败");
}
}
/**
* 用户注册
*/
@PostMapping("/register")
public Result<Boolean> register(@RequestBody User user) {
System.out.println(user);
boolean flag = userService.register(user);
if (flag) {
return Result.success(true, "注册成功");
} else {
return Result.fail("注册失败,用户名已被占用");
}
}
/**
* 用户登录
*/
@PostMapping("/login")
public Result<User> login(@RequestBody User user) {
String username = user.getUsername();
String password = user.getPasswordHash();
User user2 = userService.login(username, password);
if (user == null) {
return Result.fail("用户名或密码错误,登录失败");
} else {
return Result.success(user2, "登录成功");
}
}
}
好的,请按照以下步骤在 Postman 中测试接口:
http://localhost:8081/user/register
:{
"username": "test123",
"passwordHash": "test1234",
"grade": 1
}
若注册成功,会返回请求成功信息:
{
"code": 200,
"message": "注册成功",
"data": true
}
若注册失败(用户名已存在),会返回出错信息:
{
"code": 500,
"message": "注册失败,用户名已被占用",
"data": null
}
http://localhost:8080/user/login
:{
"username": "test123",
"password": "test1234"
}
若登录成功,会返回用户信息:
{
"code": 200,
"message": "登录成功",
"data": {
"id": 用户ID,
"username": "test123",
"avatar": null,
"passwordHash": null,
"salt": null,
"grade": 1,
"role": null
}
}
若登录失败(用户名或密码错误),会返回出错信息:
{
"code": 500,
"message": "用户名或密码错误,登录失败",
"data": null
}
http://localhost:8080/user/all
:若查询全部用户信息列表成功,会返回用户信息列表:
{
"code": 200,
"message": "操作成功",
"data": [
{
"id": 用户1ID,
"username": "test123",
"avatar": null,
"passwordHash": null,
"salt": null,
"grade": 0,
"role": null
},
{
"id": 用户2ID,
"username": "user",
"avatar": null,
"passwordHash": null,
"salt": null,
"grade": 1,
"role": null
}
]
}
http://localhost:8080/user/1
(用户ID为 1 的具体用户):若根据 ID 获取用户信息成功,会返回对应的用户信息:
{
"code": 200,
"message": "操作成功",
"data": {
"id": 用户1ID,
"username": "test123",
"avatar": null,
"passwordHash": null,
"salt": null,
"grade": 0,
"role": null
}
}
若未找到该用户,会返回出错信息:
{
"code": 500,
"message": "该用户不存在",
"data": null
}
http://localhost:8080/user/save
:{
"id": <当前用户ID>,
"username": "test321",
"passwordHash": "test4321",
"grade": 2
}
若新增或更新成功,会返回请求成功信息:
{
"code": 200,
"message": "保存成功",
"data": true
}
注意:若采用 POST 请求方式,请在请求头中添加 Content-Type 为 application/json。
希望这个例子可以帮助您更好地理解如何使用 Spring Boot 框架和 Postman 工具进行接口测试。