Spring Boot utilise Hutool-jwt pour implémenter la vérification des jetons

Spring Boot utilise Hutool-jwt pour implémenter la vérification des jetons

1. Présentation de JWT

1. Introduction

En termes simples, JWT est un format d'authentification d'identité réseau et d'échange d'informations .

2. Structure

  • Informations d'en-tête, qui déclarent principalement l'algorithme de signature et d'autres informations de JWT ;
  • Informations sur la charge utile, qui contiennent principalement diverses déclarations et transmettent des données en texte clair ;
  • Signature signature, le JWT avec cette partie est appelé JWS, c'est-à-dire le JWS signé, qui est utilisé pour vérifier les données.
# 整体结构
header.payload.signature

3. Utiliser

Le cœur du module JWT est principalement constitué de deux classes :

  1. JWTLes classes sont utilisées pour générer, analyser ou vérifier en chaîne les informations JWT.
  2. JWTUtilLa classe est principalement un outil d'encapsulation de JWT, fournissant un travail de génération, d'analyse et de vérification JWT plus concis.

2. Utilisation de base

La logique est relativement simple et le code suivant est utilisé comme référence.

0. Réflexion globale

  1. Écrire une classe d'outils pour encapsuler les méthodes de génération, de vérification et d'analyse des jetons ;
  2. Générez un jeton lors de l'inscription et de la connexion, stockez le jeton généré dans redis et récupérez-le de redis la prochaine fois que vous vous connecterez, s'il existe, il reviendra directement, sinon il sera régénéré et stocké dans redis ;
  3. Vérifiez et analysez le jeton dans l'intercepteur et stockez les informations utiles dans le jeton private static final ThreadLocal<UserDto> *THREAD_LOCAL* = new ThreadLocal<>();pour un accès ultérieur.

1. Outils JWT

Ajustez userDto selon vos besoins

package com.zibo.common.util;

import cn.hutool.jwt.JWT;
import com.zibo.modules.user.dto.UserDto;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;

/**
 * JWT 工具类
 *
 * @author zibo
 * @date 2023/7/2 14:36
 * @slogan 慢慢学,不要停。
 */
public class JWTUtility {
    
    

    /**
     * 密钥
     */
    private static final byte[] KEY = "zibo".getBytes();

    /**
     * 过期时间(秒):7 天
     */
    public static final long EXPIRE = 7 * 24 * 60 * 60;

    private JWTUtility() {
    
    
    }

    /**
     * 根据 userDto 生成 token
     *
     * @param dto    用户信息
     * @return token
     */
    public static String generateTokenForUser(UserDto dto) {
    
    
        Map<String, Object> map = new HashMap<>();
        map.put("id", dto.getId());
        map.put("nickname", dto.getNickname());
        return generateToken(map);
    }

    /**
     * 根据 map 生成 token 默认:HS265(HmacSHA256)算法
     *
     * @param map    携带数据
     * @return token
     */
    public static String generateToken(Map<String, Object> map) {
    
    
        JWT jwt = JWT.create();
        // 设置携带数据
        map.forEach(jwt::setPayload);
        // 设置密钥
        jwt.setKey(KEY);
        // 设置过期时间
        jwt.setExpiresAt(new Date(System.currentTimeMillis() + EXPIRE * 1000));
        return jwt.sign();
    }

    /**
     * token 校验
     * @param token token
     * @return 是否通过校验
     */
    public static boolean verify (String token) {
    
    
        if (StringUtils.isBlank(token)) return false;
        return JWT.of(token).setKey(KEY).verify();
    }

    /**
     * token 校验,并获取 userDto
     * @param token token
     * @return userDto
     */
    public static UserDto verifyAndGetUser(String token) {
    
    
        if(!verify(token)) return null;
        // 解析数据
        JWT jwt = JWT.of(token);
        Long id = Long.valueOf(jwt.getPayload("id").toString());
        String nickname = jwt.getPayload("nickname").toString();
        // 返回用户信息
        return new UserDto(id, nickname);
    }

}

2. Vérifiez et analysez le jeton dans l'intercepteur

package com.zibo.common.config;

import com.zibo.common.enums.AppCode;
import com.zibo.common.pojo.ApiException;
import com.zibo.common.pojo.UserHolder;
import com.zibo.common.util.JWTUtility;
import com.zibo.modules.user.dto.UserDto;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

/**
 * 自定义拦截器
 * @author Administrator
 */
@Component
@Slf4j
public class UserInterceptor implements HandlerInterceptor {
    
    

    /**
     * 进入controller方法之前执行。如果返回false,则不会执行 controller 的方法
     *
     * @param request 请求
     * @param response 响应
     * @param handler 处理器
     * @return 是否放行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    
    
        // 获取 header 中的 Authorization 信息
        String token = request.getHeader("token");
        if (StringUtils.isNotBlank(token)) {
    
    
            UserDto dto = JWTUtility.verifyAndGetUser(token);
            if (dto != null) {
    
    
                UserHolder.setUserInfo(dto);
            } else {
    
    
                log.error("token 验证失败!token is {}, uri is {}", token, request.getRequestURI());
                throw new ApiException(AppCode.TOKEN_ERROR, "token 校验不通过!");
            }
        } else {
    
    
            log.error("token 验证失败!token is {}, uri is {}", token, request.getRequestURI());
            throw new ApiException(AppCode.TOKEN_ERROR, "token 为空!");
        }
        return true;
    }

    /**
     * 响应结束之前
     * @param request 请求
     * @param response 响应
     * @param handler 处理器
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    
    
        // 清理掉当前线程中的数据,防止内存泄漏
        UserHolder.remove();
    }
}

3. Émettez des jetons lors de l'inscription et de la connexion

@PostMapping("/loginOrRegister")
public UserDto loginOrRegister(@RequestBody @Validated UserDto dto) {
    
    
    // 通过手机号查询
    UserDto byPhone = service.findByPhone(dto.getPhone());
    // 如果操作标记为空,则报错
    if (ObjectUtils.isEmpty(dto.getOperation())) {
    
    
        throw new ApiException("操作标记不能为空!");
    }
    // 如果是注册
    if (dto.getOperation() == 0) {
    
    
        // 如果用户已经存在,则报错
        if (ObjectUtils.isNotEmpty(byPhone)) {
    
    
            throw new ApiException("注册失败!账号已存在!");
        }
        // 创建用户
        Long save = service.save(dto);
        // 返回用户信息
        UserDto userDto = service.findById(save);
        // 根据用户生成 token
        String token = JWTUtility.generateTokenForUser(userDto);
        // 保存到 redis
        service.saveToken(userDto.getId(), token);
        // 设置 token
        userDto.setToken(token);
        LogUtility.baseInfoWith("注册成功!" + userDto);
        return userDto;
    }
    // 登录
    if (ObjectUtils.isEmpty(byPhone)) {
    
    
        log.error("登录失败!账号不存在!" + dto);
        // 账号不存在
        throw new ApiException("登录失败!账号不存在!com/zibo/controller/user/UserController.java:62");
    } else {
    
    
        // 比较密码是否一致
        if (!byPhone.getPassword().equals(dto.getPassword())) {
    
    
            throw new ApiException("登录失败!账号或密码错误!");
        }
        // 更新最后登录时间
        byPhone.setLastLoginTime(LocalDateTime.now());
        service.save(byPhone);
        // 从 redis 获取 token
        String token = service.getToken(byPhone.getId());
        if (StringUtils.isBlank(token)) {
    
    
            // 根据用户生成 token
            token = JWTUtility.generateTokenForUser(byPhone);
            log.info("用户登录,并生成token,id 为:{}, 昵称为:{},token 为:{}", byPhone.getId(), byPhone.getNickname(), token);
            // 保存到 redis
            service.saveToken(byPhone.getId(), token);
        }
        // 设置 token
        byPhone.setToken(token);
        LogUtility.baseInfoWith("登录成功!" + byPhone);
        return byPhone;
    }
}

4. Informations utilisateur UserHolder

package com.zibo.common.pojo;

import com.zibo.modules.user.dto.UserDto;

/**
 * 存放用户信息的容器
 * @author Administrator
 */
public class UserHolder {
    
    

    private static final ThreadLocal<UserDto> THREAD_LOCAL = new ThreadLocal<>();

    private UserHolder() {
    
    
    }

    /**
     * 获取线程中的用户
     * @return 用户信息
     */
    public static UserDto getUserInfo() {
    
    
        return THREAD_LOCAL.get();
    }

    /**
     * 设置当前线程中的用户
     * @param info 用户信息
     */
    public static void setUserInfo(UserDto info) {
    
    
        THREAD_LOCAL.set(info);
    }

    public static Long getUserId() {
    
    
        UserDto dto = THREAD_LOCAL.get();
        if (dto != null) {
    
    
            return dto.getId();
        } else {
    
    
            // 注册或登录时没有,返回 0
            return 0L;
        }
    }

    public static void remove() {
    
    
        THREAD_LOCAL.remove();
    }

}

Je suppose que tu aimes

Origine blog.csdn.net/qq_29689343/article/details/131522359
conseillé
Classement