spring boot jwt 实现用户登录完整java

spring boot jwt 实现用户登录完整java

登录校验逻辑

用户登录的校验逻辑分为三个主要步骤,分别是校验验证码校验用户状态校验密码,具体逻辑如下

  • 前端发送usernamepasswordcaptchaKeycaptchaCode请求登录。
  • 判断captchaCode是否为空,若为空,则直接响应验证码为空;若不为空进行下一步判断。
  • 根据captchaKey从Redis中查询之前保存的code,若查询出来的code为空,则直接响应验证码已过期;若不为空进行下一步判断。
  • 比较captchaCodecode,若不相同,则直接响应验证码不正确;若相同则进行下一步判断。
  • 根据username查询数据库,若查询结果为空,则直接响应账号不存在;若不为空则进行下一步判断。
  • 查看用户状态,判断是否被禁用,若禁用,则直接响应账号被禁;若未被禁用,则进行下一步判断。
  • 比对password和数据库中查询的密码,若不一致,则直接响应账号或密码错误,若一致则进行入最后一步。
  • 创建JWT,并响应给浏览器。

请求数据结构

package com.orchids.springmybatisplus.model.entity;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

/**
 * @Author qwh
 * @Date 2024/6/2 22:31
 */
@Data
@Schema(description = "后台管理系统登录信息")
public class LoginVo {

    @Schema(description="用户名")
    private String username;

    @Schema(description="密码")
    private String password;

    @Schema(description="验证码key")
    private String captchaKey;

    @Schema(description="验证码code")
    private String captchaCode;
}

枚举类

package com.orchids.lovehouse.common.result;

import lombok.Getter;

/**
 * 统一返回结果状态信息类
 */
@Getter
public enum ResultCodeEnum {

    SUCCESS(200, "成功"),
    FAIL(201, "失败"),
    PARAM_ERROR(202, "参数不正确"),
    SERVICE_ERROR(203, "服务异常"),
    DATA_ERROR(204, "数据异常"),
    ILLEGAL_REQUEST(205, "非法请求"),
    REPEAT_SUBMIT(206, "重复提交"),
    DELETE_ERROR(207, "请先删除子集"),

    ADMIN_ACCOUNT_EXIST_ERROR(301, "账号已存在"),
    ADMIN_CAPTCHA_CODE_ERROR(302, "验证码错误"),
    ADMIN_CAPTCHA_CODE_EXPIRED(303, "验证码已过期"),
    ADMIN_CAPTCHA_CODE_NOT_FOUND(304, "未输入验证码"),
    ADMIN_ACCOUNT_NOT_EXIST(330,"用户不存在"),


    ADMIN_LOGIN_AUTH(305, "未登陆"),
    ADMIN_ACCOUNT_NOT_EXIST_ERROR(306, "账号不存在"),
    ADMIN_ACCOUNT_ERROR(307, "用户名或密码错误"),
    ADMIN_ACCOUNT_DISABLED_ERROR(308, "该用户已被禁用"),
    ADMIN_ACCESS_FORBIDDEN(309, "无访问权限"),
    APP_LOGIN_AUTH(501, "未登陆"),
    APP_LOGIN_PHONE_EMPTY(502, "手机号码为空"),
    APP_LOGIN_CODE_EMPTY(503, "验证码为空"),
    APP_SEND_SMS_TOO_OFTEN(504, "验证法发送过于频繁"),
    APP_LOGIN_CODE_EXPIRED(505, "验证码已过期"),
    APP_LOGIN_CODE_ERROR(506, "验证码错误"),
    APP_ACCOUNT_DISABLED_ERROR(507, "该用户已被禁用"),


    TOKEN_EXPIRED(601, "token过期"),
    TOKEN_INVALID(602, "token非法");


    private final Integer code;

    private final String message;

    ResultCodeEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

全局异常处理

package com.orchids.lovehouse.common.exception;

import com.orchids.lovehouse.common.result.ResultCodeEnum;
import lombok.Data;

/**
 * @Author qwh
 * @Date 2024/6/1 20:18
 */
@Data
public class LovehouseException extends RuntimeException {

    //异常状态码
    private Integer code;
    /**
     * 通过状态码和错误消息创建异常对象
     * @param message
     * @param code
     */
    public LovehouseException(String message, Integer code) {
        super(message);
        this.code = code;
    }

    /**
     * 根据响应结果枚举对象创建异常对象
     * @param resultCodeEnum
     */
    public LovehouseException(ResultCodeEnum resultCodeEnum) {
        super(resultCodeEnum.getMessage());
        this.code = resultCodeEnum.getCode();
    }

    @Override
    public String toString() {
        return "LovehouseException{" +
                "code=" + code +
                ", message=" + this.getMessage() +
                '}';
    }
}

配置所需依赖
登录接口需要为登录成功的用户创建并返回JWT,本项目使用开源的JWT工具Java-JWT,配置如下,具体内容可参考官方文档

  • 引入Maven依赖
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.2</version>
</dependency>


<dependency><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>
  <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>

创建JWT和工具类 common.utils.JwtUtil

package com.orchids.lovehouse.common.utils;

import com.orchids.lovehouse.common.exception.LovehouseException;
import com.orchids.lovehouse.common.result.ResultCodeEnum;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;

import javax.crypto.SecretKey;
import java.util.Date;

/**
 * @Author qwh
 * @Date 2024/6/2 21:01
 */
public class JwtUtil {
    private static long tokenExpiration = 60  * 60 * 1000L;
    public static SecretKey secretKey = Keys.hmacShaKeyFor("M0PKKI6pYGVWWfDZw90a0lTpGYX1d4AQ".getBytes());
    public static String createToken(Long userId,String username){
        String token  = Jwts.builder().
        setSubject("USER_INFO").
        setExpiration(new Date(System.currentTimeMillis()+tokenExpiration)).
        claim("userId",userId).
        claim("username",username).
        signWith(secretKey,SignatureAlgorithm.HS256).
        compact();
        return token;
    }
    public static Claims parsToken(String token){
        if (token==null) {
            throw new LovehouseException(ResultCodeEnum.ADMIN_LOGIN_AUTH);
        }
        try {
            JwtParser jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();
            Jws<Claims> claims = jwtParser.parseClaimsJws(token);
            return claims.getBody();
        } catch (ExpiredJwtException e) {
            throw new LovehouseException(ResultCodeEnum.TOKEN_EXPIRED);
        } catch (JwtException e){
            throw new LovehouseException(ResultCodeEnum.TOKEN_INVALID);
        }
    }

    public static void main(String[] args) {
        System.out.println(createToken(2l,"user"));
    }
}

controller逻辑

package com.orchids.lovehouse.web.admin.controller.login;


import com.orchids.lovehouse.common.login.LoginUserHolder;
import com.orchids.lovehouse.common.result.Result;
import com.orchids.lovehouse.common.utils.JwtUtil;
import com.orchids.lovehouse.web.admin.service.LoginService;
import com.orchids.lovehouse.web.admin.vo.login.CaptchaVo;
import com.orchids.lovehouse.web.admin.vo.login.LoginVo;
import com.orchids.lovehouse.web.admin.vo.system.user.SystemUserInfoVo;
import com.orchids.lovehouse.web.admin.vo.system.user.SystemUserItemVo;
import io.jsonwebtoken.Claims;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@Tag(name = "后台管理系统登录管理")
@RestController
@RequestMapping("/admin")
public class LoginController {
    @Autowired
    private LoginService loginService;

    @Operation(summary = "获取图形验证码")
    @GetMapping("login/captcha")
    public Result<CaptchaVo> getCaptcha() {
        CaptchaVo captcha = loginService.getCaptcha();
        return Result.ok(captcha);
    }

    @Operation(summary = "登录")
    @PostMapping("login")
    public Result<String> login(@RequestBody LoginVo loginVo) {
        String token =  loginService.login(loginVo);
        return Result.ok(token);
    }

    @Operation(summary = "获取登陆用户个人信息")
    @GetMapping("info")
    public Result<SystemUserInfoVo> info () {
        SystemUserInfoVo systemUserInfo  = loginService.getLoginUserInfo();
        return Result.ok(systemUserInfo);
    }
}

service逻辑

package com.orchids.lovehouse.web.admin.service;

import com.orchids.lovehouse.web.admin.vo.login.CaptchaVo;
import com.orchids.lovehouse.web.admin.vo.login.LoginVo;
import com.orchids.lovehouse.web.admin.vo.system.user.SystemUserInfoVo;

public interface LoginService {

    CaptchaVo getCaptcha();

    String login(LoginVo loginVo);

    SystemUserInfoVo getLoginUserInfo(Long userId);
}

sreviceImpl

package com.orchids.lovehouse.web.admin.service.impl;

import com.orchids.lovehouse.common.constant.RedisConstant;
import com.orchids.lovehouse.common.exception.GlobalExceptionHandler;
import com.orchids.lovehouse.common.exception.LovehouseException;
import com.orchids.lovehouse.common.result.ResultCodeEnum;
import com.orchids.lovehouse.common.utils.JwtUtil;
import com.orchids.lovehouse.model.entity.SystemUser;
import com.orchids.lovehouse.model.enums.BaseStatus;
import com.orchids.lovehouse.web.admin.mapper.SystemUserMapper;
import com.orchids.lovehouse.web.admin.service.LoginService;
import com.orchids.lovehouse.web.admin.vo.login.CaptchaVo;
import com.orchids.lovehouse.web.admin.vo.login.LoginVo;
import com.orchids.lovehouse.web.admin.vo.system.user.SystemUserInfoVo;
import com.orchids.lovehouse.web.admin.vo.system.user.SystemUserItemVo;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private SystemUserMapper systemUserMapper;

    @Override
    public CaptchaVo getCaptcha() {
        SpecCaptcha  specCaptcha = new SpecCaptcha(100, 40, 5);
        specCaptcha.setCharType(Captcha.TYPE_DEFAULT);

        String code = specCaptcha.text().toLowerCase();
        String key = RedisC
上一篇:spring boot中常用的多线程案例


下一篇:深度学习 - 激活函数