登陆、注册的完整步骤

1.注册

        1.1DTO和VO

1.1.1首先第一步引入本节主题相关的依赖。

我构建的项目有很多哥模块,这是从各个模块抽取出来的,不能直接使用,目的是让读者知道需要哪些依赖。除此之外还有web,lombok等依赖。

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt-api</artifactId>
      <version>0.11.5</version>
</dependency>
<dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt-impl</artifactId>
      <version>0.11.5</version>
      <scope>runtime</scope>
</dependency>
      <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt-jackson</artifactId>
      <version>0.11.5</version>
      <scope>runtime</scope>
</dependency>
<dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
      <version>3.5.6</version>
</dependency>

1.1.2 实体类、RegisterDTO、RegisterVO

   POJO

package 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 ArrayList<Integer> roles;

    public User(String userName, String password, String trueName, String userPhone, Integer userStatus, ArrayList<Integer> roles){
        this.userName = userName;
        this.password = password;
        this.trueName = trueName;
        this.userPhone = userPhone;
        this.userStatus = userStatus;
        this.roles = roles;
    }
}

    DTO        前端表单提交封装进DTO

package 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;
}

   VO

package 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(Map<String, 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工具生成token
5.最后构建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.查询后端是否有该用户
        LambdaQueryWrapper<User> 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("验证码错误");
        }
        Map<String, 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生成token,mybatis-plus做数据操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值