基于uniapp原生组件uni-ui 做一个登录注册与个人中心(后端篇)
简介
本文使用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;
项目编写
- 创建项目
- pom.xml
<?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>
- application.properties
# ????? 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
代码编写
项目结构
Bean
User
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; }
Result
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; } }
Tool
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; } }
Config
CorsConfig
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); } }; } }
Mapper
UserMapper
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> { }
Service
IUserService
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); }
Impl
UserServiceImpl
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; //一切正常,返回相应用户信息 } }
Controller
UserController
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测试
好的,请按照以下步骤在 Postman 中测试接口:
- 以 POST 请求方式发送以下数据至 http://localhost:8081/user/register:
{ "username": "test123", "passwordHash": "test1234", "grade": 1 }
若注册成功,会返回请求成功信息:
{ "code": 200, "message": "注册成功", "data": true }
若注册失败(用户名已存在),会返回出错信息:
{ "code": 500, "message": "注册失败,用户名已被占用", "data": null }
- 以 POST 请求方式发送以下数据至 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 }
- 以 GET 请求方式发送以下数据至 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 } ] }
- 以 GET 请求方式发送以下数据至 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 }
- 以 POST 请求方式发送以下数据至 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 工具进行接口测试。