springboot+拦截器+本地线程实现token的解析及用户信息上下文使用

springboot+拦截器+本地线程实现token的解析及用户信息上下文使用

1、先创建一个本地线程内的用户对象AccountInfo

package com.nuoyi.study.common.thread.accountthread;

import com.nuoyi.study.common.sql.SqlLogInfo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * @desc: 用户信息
 * @Author: nuoyi
 * @Date: 2024/01/25 11:06
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AccountInfo implements Serializable {

    /**
     * 用户Id
     */
    private Integer id;

    /**
     * 姓名
     */
    private String name;

    /**
     * 手机号
     */
    private String mobile;

    /**
     * 角色ID
     */
    private Integer roleId;

    /**
     * 角色名称
     */
    private String roleName;

    /**
     * 是否启用 1 启用 0 禁用
     */
    private Integer state;

    /**
     * 请求开始时间戳 用于计算请求时长
     */
    private Long reqTimestamp;

    /**
     * 请求体,请求参数
     */
    private String requestBody;

    /**
     * 头像
     */
    private String avatar;

    /**
     * sql记录集合
     */
    private Map<String, SqlLogInfo> sqlList;
}

2、创建一个本地用户线程AccountThreadLocal

package com.nuoyi.study.common.thread.accountthread;

import cn.hutool.core.util.ObjectUtil;
import com.nuoyi.study.common.sql.SqlLogInfo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @desc: 用户本地线程
 * @Author: nuoyi
 * @Date: 2024/01/25 11:06
 */
public class AccountThreadLocal {

    private AccountThreadLocal() {
    }

    private static final ThreadLocal<AccountInfo> LOCAL = new ThreadLocal<>();

    public static void set(AccountInfo accountInfo) {
        LOCAL.set(accountInfo);
    }

    public static AccountInfo get() {
        AccountInfo accountInfo = LOCAL.get();
        if (ObjectUtil.isNull(accountInfo)) {
            return new AccountInfo();
        }
        return accountInfo;
    }

    /**
     * 设置请求体
     *
     * @param jsonStr 请求体
     */
    public static void setRequestBody(String jsonStr) {
        AccountInfo accountInfo = LOCAL.get();
        if (ObjectUtil.isNotNull(accountInfo)) {
            accountInfo.setRequestBody(jsonStr);
        }
    }

    /**
     * 获取用户id
     */
    public static Integer getAccountId() {
        return LOCAL.get() == null ? 0 : LOCAL.get().getId();
    }

    /**
     * 移除本地线程
     */
    public static void remove() {
        LOCAL.remove();
    }

    /**
     * 设置sql日志
     *
     * @param sqlMap 接口所有sql相关信息
     */
    public static void setSqlLog(Map<String, SqlLogInfo> sqlMap) {
        AccountInfo accountInfo = LOCAL.get();
        if (ObjectUtil.isNull(accountInfo)) {
            accountInfo = new AccountInfo();
        }
        accountInfo.setSqlList(sqlMap);
    }

    /**
     * 获取sql日志
     */
    public static Map<String, SqlLogInfo> getSqlLog() {
        AccountInfo accountInfo = LOCAL.get();
        if (ObjectUtil.isNull(accountInfo)) {
            return new HashMap<>(1);
        } else {
            return accountInfo.getSqlList();
        }
    }


}

3、创建一个拦截器AuthTokenIntercept用户校验用户token相关功能 内部的枚举异常码ApiCodeEnum可自定义

package com.nuoyi.study.handle.intercept;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.nuoyi.study.common.config.GlobalConfiguration;
import com.nuoyi.study.common.enums.RedisKeyEnum;
import com.nuoyi.study.common.thread.accountthread.AccountInfo;
import com.nuoyi.study.common.thread.accountthread.AccountThreadLocal;
import com.nuoyi.study.common.thread.sqlthread.SqlLogThreadLocal;
import com.nuoyi.study.dao.mapper.RoleMapper;
import com.nuoyi.study.dao.mapper.UserMapper;
import com.nuoyi.study.dao.po.Role;
import com.nuoyi.study.dao.po.User;
import com.nuoyi.tool.entity.BaseResponse;
import com.nuoyi.tool.enums.ApiCodeEnum;
import com.nuoyi.tool.exception.ServiceException;
import com.nuoyi.tool.utils.AesUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Optional;

/**
 * @author nuoyi
 * @date 2024/1/25 11:08
 * @description token访问权限拦截器
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class AuthTokenIntercept implements HandlerInterceptor {

    private final RedisTemplate<String, String> redisTemplate;
    private final GlobalConfiguration configuration;
    private final RoleMapper roleMapper;
    private final UserMapper userMapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从请求头中获取token
        String token = request.getHeader("token");
        printUrLLog(request.getRequestURI(), token);
        if (StrUtil.isEmpty(token)) {
            returnResponse(response, ApiCodeEnum.TokenDeletion.getCode(), ApiCodeEnum.TokenDeletion.getDescription());
            return false;
        }
        try {
            // 解析token
            String tokenStr = AesUtil.decrypt(URLDecoder.decode(token,"UTF-8"), configuration.getSecretKey());
            User user = JSON.parseObject(tokenStr, User.class);
            String redisToken = redisTemplate.opsForValue().get(RedisKeyEnum.Token.getKey() + ":" + user.getId());
            if(StrUtil.isBlank(redisToken)){
                returnResponse(response, ApiCodeEnum.TokenExpire.getCode(), ApiCodeEnum.TokenExpire.getDescription());
                return false;
            }
            AccountThreadLocal.set(userMapper.getAccountInfo(user.getId()));
        } catch (Exception e) {
            e.printStackTrace();
            returnResponse(response, ApiCodeEnum.TokenError.getCode(), ApiCodeEnum.TokenError.getDescription());
            return false;
        }
        return true;
    }

    /**
     * 打印url日志
     *
     * @param url   请求路径
     * @param token 请求token
     */
    private void printUrLLog(String url, String token) {
        log.info("url>>>>>{},token>>>>>>{}",url,token);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception
            ex) throws Exception {
        // 移除用户信息
        AccountThreadLocal.remove();
    }

    /**
     * 拦截器返回值
     *
     * @param response
     * @param code
     * @param description
     */
    private void returnResponse(HttpServletResponse response, Integer code, String description) {
        try {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            response.getWriter().print(JSON.toJSONString(new BaseResponse(code, description)));
        } catch (IOException e) {
            throw new ServiceException(ApiCodeEnum.AppError.getCode(), e.getMessage());
        }
    }


}

4、即可在项目上下文中自由使用用户信息啦

AccountThreadLocal.getAccountId();
AccountInfo accountInfo = AccountThreadLocal.get();
AccountThreadLocal.get().getName();//可以获取用户各种属性

你可能感兴趣的:(springboot,spring,boot,后端,java)