1.注册1.1DTO和VO1.1.1首先第一步引入本节主题相关的依赖。我构建的项目有很多哥模块这是从各个模块抽取出来的不能直接使用目的是让读者知道需要哪些依赖。除此之外还有weblombok等依赖。dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-jackson/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version3.5.6/version /dependency1.1.2 实体类、RegisterDTO、RegisterVOPOJOpackage com.mc.domain.pojo; import com.baomidou.mybatisplus.annotation.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.time.LocalDateTime; import java.util.ArrayList; Data Builder AllArgsConstructor NoArgsConstructor TableName(user) public class User { TableId(type IdType.AUTO) private Integer id; private String userName; private String password; private String trueName; private String userPhone; private Integer userStatus; TableField(fill FieldFill.INSERT) private LocalDateTime createdAt; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updatedAt; TableField(exist false) private ArrayListInteger roles; public User(String userName, String password, String trueName, String userPhone, Integer userStatus, ArrayListInteger roles){ this.userName userName; this.password password; this.trueName trueName; this.userPhone userPhone; this.userStatus userStatus; this.roles roles; } }DTO 前端表单提交封装进DTOpackage com.mc.domain.dto; import lombok.Builder; import lombok.Data; Data Builder public class UserRegisterDTO { private String username; private String password; private String trueName; private String userPhone; private Integer roleId; }VOpackage com.mc.domain.vo; import lombok.Builder; import lombok.Data; Data Builder public class RegisterVO { private String userName; private String trueName; private String userPhone; }1.2 Controller层package com.mc.Controller; import com.mc.Service.UserService; import com.mc.domain.dto.UserLoginDTO; import com.mc.domain.dto.UserRegisterDTO; import com.mc.domain.vo.LoginVO; import com.mc.domain.vo.RegisterVO; import com.mc.result.Result; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; Tag(name 用户管理) RestController RequestMapping(/user) public class UserController { Resource private UserService userService; PostMapping(/login) public Result login(RequestBody UserLoginDTO userLoginDTO){ LoginVO loginVO userService.login(userLoginDTO); return Result.success(loginVO); } PostMapping(/register) public Result register(RequestBody UserRegisterDTO userRegisterDTO){ RegisterVO registerVO userService.register(userRegisterDTO); return Result.success(registerVO); } }1.3 ServiceImpl层利用md5对密码进行加密存储进入数据库拆解DTO构建POJO存入数据库再返回VO回前端对更新时间和创建时间进行自动填充利用mybatis-plus的TableField(fill FieldFill.****)完成。Service public class UserServiceImpl implements UserService { Resource private UserMapper userMapper; // MD5加密方法 /** * param password * return */ private String md5Encrypt(String password) { try { MessageDigest md MessageDigest.getInstance(MD5); byte[] bytes md.digest(password.getBytes()); StringBuilder sb new StringBuilder(); for (byte b : bytes) { sb.append(String.format(%02x, b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(MD5加密失败, e); } } /** * 注册 * param registerDTO * return */ Override public RegisterVO register(UserRegisterDTO registerDTO) { if(registerDTO.getPassword() null || registerDTO.getPassword().isEmpty()){ throw new BusinessException(DATA_NOT_EXIST); } String encodedPassword md5Encrypt(registerDTO.getPassword()); User user User.builder() .userName(registerDTO.getUsername()) .password(encodedPassword) .trueName(registerDTO.getTrueName()) .userPhone(registerDTO.getUserPhone()) .userStatus(1) .build(); userMapper.insert(user); return RegisterVO.builder() .userName(user.getUserName()) .trueName(user.getTrueName()) .userPhone(user.getUserPhone()) .build(); }2.登陆2.1 验证码逻辑2.1.1先通过获取验证码发送生成验证码的请求到后端。package com.mc.Controller; import com.mc.Service.CodeService; import com.mc.domain.dto.CodeDTO; import com.mc.domain.vo.CodeVO; import com.mc.result.Result; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; Tag(name 验证码接口) RestController RequestMapping(/code) public class CodeController { Resource private CodeService codeService; /** * 生成验证码 * param codeDTO 验证码DTO * return 验证码 */ PostMapping(/generate) public Result generate(RequestBody CodeDTO codeDTO) { // 生成验证码逻辑 CodeVO code codeService.generate(codeDTO); // 成功则返回前端成功消息 return Result.success(code); } }2.1.2 同样封装了Codedto和Codevo这样封装VO是为了将redis的验证码和前端用户填入的验证码进行验证所有code需要传会前端并且返回生成验证码成功的提示。package com.mc.domain.vo; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.context.annotation.Bean; Data Builder AllArgsConstructor NoArgsConstructor public class CodeVO { String code; boolean success; }为什么这样封装dto因为之后进入UserServiceImpl中需要通过电话为key去redis查询验证码。package com.mc.domain.dto; import lombok.Data; Data public class CodeDTO { private String phone; }将生成的验证码传入redis并将code:dto.getPhone()作为key。package com.mc.Service.ServiceImpl; import com.mc.Service.CodeService; import com.mc.domain.dto.CodeDTO; import com.mc.domain.vo.CodeVO; import jakarta.annotation.Resource; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import java.util.Random; import java.util.concurrent.TimeUnit; Service public class CodeServiceImpl implements CodeService{ Resource(name stringRedisTemplate) private StringRedisTemplate redisTemplate; Override public CodeVO generate(CodeDTO codeDTO) { String code String.format(%06d, new Random().nextInt(10000000)); String key code: codeDTO.getPhone(); redisTemplate.opsForValue().set(key, code,1, TimeUnit.MINUTES); return new CodeVO(code, true); } }这样redis就可以获得生成的验证码并且前端有一个生成验证码成功的提示2.2 Jwt令牌jwt工具类package com.mc.utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import org.springframework.stereotype.Component; import java.security.Key; import java.util.Date; import java.util.Map; /** * JWT工具类 */ Component public class JwtUtil { // 与测试类中一致的密钥 private static final Key SECRET_KEY Keys.secretKeyFor(SignatureAlgorithm.HS256); // 默认过期时间1小时(毫秒) private static final long DEFAULT_EXPIRATION 60 * 60 * 1000; /** * 生成JWT令牌 * param claims 自定义数据 * return 生成的JWT令牌 */ public static String generateToken(MapString, Object claims) { return Jwts.builder() .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .addClaims(claims) .setExpiration(new Date(System.currentTimeMillis() DEFAULT_EXPIRATION)) .compact(); } /** * 解析JWT令牌 * param jwt JWT令牌字符串 * return 解析后的Claims对象 */ public static Claims parseToken(String jwt) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(jwt) .getBody(); } }2.3 ServiceImpl层逻辑逻辑1.先去数据库查是否有该用户并得到用户实体2.再去查密码是否正确需先对DTO的密码进行md5加密再比较3.去redis查验证码是否正确4.jwt工具生成token5.最后构建VO返回前端Service public class UserServiceImpl implements UserService { Resource private UserMapper userMapper; Resource private CodeService codeService; Resource(name stringRedisTemplate) private StringRedisTemplate redisTemplate; Resource private JwtUtil jwtUtil; /** * 登录 * param userLoginDTO * return */ Override public LoginVO login(UserLoginDTO userLoginDTO) { //1.查询后端是否有该用户 LambdaQueryWrapperUser queryWrapper new LambdaQueryWrapper(); //仅仅只是一个包装sql的语句不会执行查询 queryWrapper.eq(User::getUserName, userLoginDTO.getUserName()); User user userMapper.selectOne(queryWrapper); //2.验证密码 LoginVO loginVO null; // 用户不存在 if (user null) { throw new BusinessException(USER_NOT_EXIST); } // 3. 校验密码 String inputEncodedPassword md5Encrypt(userLoginDTO.getPassword()); if (!inputEncodedPassword.equals(user.getPassword())) { throw new BusinessException(USERNAME_PASSWORD_ERROR); } String phone userLoginDTO.getPhone(); String redisCode redisTemplate.opsForValue().get(code: phone); // 验证码不存在/已过期 if (redisCode null) { throw new RuntimeException(验证码已过期请重新获取); } // 验证码不正确 if (!redisCode.equals(userLoginDTO.getCaptchaCode())) { throw new RuntimeException(验证码错误); } MapString, Object claims new HashMap(); claims.put(username, user.getUserName()); claims.put(id, user.getId()); String jwtToken jwtUtil.generateToken(claims); return LoginVO.builder() .token(jwtToken) .id(user.getId()) .userName(user.getUserName()) .trueName(user.getTrueName()) .userPhone(user.getUserPhone()) .roleId(user.getRoles() null || user.getRoles().isEmpty() ? null : user.getRoles().get(0)) .build(); } }3.总结整体使用了redis技术存储验证码使用jwt生成tokenmybatis-plus做数据操作。