全链路追踪TransmittableThreadLocal-接口随处可获得需要的信息

步骤一:准备工作

引入依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.11.4</version>
</dependency>

构建拦截器,进行全局请求拦截

构建拦截器


/**
 * ThirdSession拦截器,校验每个请求的ThirdSession
 *
 * @author byChen
 * @date 2021/11/22
 */
@Component
@AllArgsConstructor
public class ThirdSessionInterceptor implements HandlerInterceptor {
    @Autowired
    private final RedisTemplate<String, String> redisTemplate;
//    @Autowired
//    private static ThirdSessionInterceptor interceptor;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return true;
}

设置全局拦截

/**
 * @author byChen
 * @date 2021/11/22
 */
@Configuration
@AllArgsConstructor
public class MyWebAppConfigurer implements WebMvcConfigurer {
    private final RedisTemplate<String, String> redisTemplate;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 多个拦截器组成一个拦截器链
        // addPathPatterns 用于添加拦截规则
        // excludePathPatterns 用户排除拦截
        registry.addInterceptor(new ThirdSessionInterceptor(redisTemplate)).addPathPatterns("/**");
    }
}

自建注解,区别哪些过滤哪些不过滤


/**
 * 加上此注解,接口必须登录带session才能访问
 * @author
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiLogin {

    /**
     * 必须登录才能访问,默认true
     * @return
     */
    boolean mustLogin() default true;

}

构建实体类,用以存储登录后获得的信息


/**
 * @author byChen
 * @date 2021/11/22
 */
@Data
public class ThirdSession implements Serializable {

    /**
     * 用户类型(1供货商2采购方)
     */
    private String userType;
    /**
     * 用户主键id
     */
    private String userId;
    /**
     * 用户名称
     */
    private String userName;
    /**
     * 用户手机号
     */
    private String phone;

}

步骤二:登录


/**
 * 供货商登录
 */
@PostMapping("/login")
public R login(@RequestBody ManufactorUser user) {
    if (StrUtil.isBlank(user.getPhone())) {
        return R.failed("没有维护手机号");
    }
    ManufactorUser one = service.getOne(Wrappers.<ManufactorUser>lambdaQuery()
            .eq(ManufactorUser::getPhone, user.getPhone()));
    if (StrUtil.isBlank(user.getPassWord()) || !ENCODER.matches(user.getPassWord(),one.getPassWord())) {
        return R.failed("账户密码信息错误");
    }
    //登录成功,存入全链路跟踪实体,并放入缓存
    String s = UUID.randomUUID().toString();
    ThirdSession thirdSession = new ThirdSession();
    thirdSession.setUserType("1");
    thirdSession.setUserId(one.getId());
    thirdSession.setUserName(one.getUserName());
    thirdSession.setPhone(one.getPhone());
    one.setSessionKey(s);
    String key = WxConstants.THIRD_SESSION_BEGIN + ":" + s;
    redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(thirdSession),WxConstants.TIME_OUT_SESSION, TimeUnit.HOURS);
    return R.ok(one);

}

登陆时,判断登录成功后,将需要的信息存入全链路实体中,并存入redis缓存,将生成的UUID返回给前台,前台放入third-session,每次访问都得携带

步骤三:添加全链路跟踪工具类

package com.admin.util;

import com.admin.domain.ThirdSession;
import com.alibaba.ttl.TransmittableThreadLocal;
import lombok.experimental.UtilityClass;

/**
 * thirdSession工具类
 * @author byChen
 * @date 2021/11/22
 */
@UtilityClass
public class ThirdSessionHolder {

    private final ThreadLocal<ThirdSession> THREAD_LOCAL_THIRD_SESSION = new TransmittableThreadLocal<>();

    /**
     * TTL 设置thirdSession
     *
     * @param thirdSession
     */
    public void setThirdSession(ThirdSession thirdSession) {
        THREAD_LOCAL_THIRD_SESSION.set(thirdSession);
    }

    /**
     * 获取TTL中的thirdSession
     *
     * @return
     */
    public ThirdSession getThirdSession() {
        ThirdSession thirdSession = THREAD_LOCAL_THIRD_SESSION.get();
        return thirdSession;
    }


    /**
     * 获取用户主键id
     *  登录的时候查询并存入
     * @return
     */
    public String getUserId(){
        if(getThirdSession() != null){
            return getThirdSession().getUserId();
        }
        return null;
    }

    /**
     * 获取用户类型(1供货商2采购方)
     *  登录的时候查询并存入
     * @return
     */
    public String getUserType(){
        if(getThirdSession() != null){
            return getThirdSession().getUserType();
        }
        return null;
    }

    /**
     * 清除
     */
    public void clear() {
        THREAD_LOCAL_THIRD_SESSION.remove();
    }
}

步骤四:补充过滤逻辑


/**
 * ThirdSession拦截器,校验每个请求的ThirdSession
 *
 * @author byChen
 * @date 2021/11/22
 */
@Component
@AllArgsConstructor
public class ThirdSessionInterceptor implements HandlerInterceptor {
    @Autowired
    private final RedisTemplate<String, String> redisTemplate;
//    @Autowired
//    private static ThirdSessionInterceptor interceptor;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("===请求被拦截===");
        HandlerMethod method = (HandlerMethod) handler;
        //判断访问的control是否添加ApiLogin注解
        ApiLogin apiLogin = method.getMethodAnnotation(ApiLogin.class);
        String thirdSessionHeader = request.getHeader("third-session");
        if (apiLogin != null && apiLogin.mustLogin()) {
            if (StrUtil.isNotBlank(thirdSessionHeader)) {
                //获取缓存中的ThirdSession
                String key = WxConstants.THIRD_SESSION_BEGIN + ":" + thirdSessionHeader;
                Object thirdSessionObj = redisTemplate.opsForValue().get(key);
                if (thirdSessionObj == null) {//session过期
                    ThirdSessionHolder.clear();
                    R r = R.failed(MyReturnCode.ERR_60001.getCode(), MyReturnCode.ERR_60001.getMsg());
                    this.writerPrint(response, r);
                    return Boolean.FALSE;
                } else {
                    //不过期就存入全链路跟踪中
                    String thirdSessionStr = String.valueOf(thirdSessionObj);
                    ThirdSession thirdSession = JSONUtil.toBean(thirdSessionStr, ThirdSession.class);
                    ThirdSessionHolder.setThirdSession(thirdSession);
                }
            } else {
                //没有携带session
                ThirdSessionHolder.clear();
                R r = R.failed(MyReturnCode.ERR_60002.getCode(), MyReturnCode.ERR_60002.getMsg());
                this.writerPrint(response, r);
                return Boolean.FALSE;
            }
        }
        return Boolean.TRUE;
    }

    /**
     * 向页面返回的
     *
     * @param response
     * @param r
     * @throws IOException
     */
    private void writerPrint(HttpServletResponse response, R r) throws IOException {
        //返回超时错误码
        response.setCharacterEncoding("UTF-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        PrintWriter writer = response.getWriter();
        writer.print(JSONUtil.parseObj(r));
        if (writer != null) {
            writer.close();
        }
    }
}

对添加了自建注解ApiLogin 的接口,请求必须携带 third-session 以此来获取信息。

一些枚举类


/**
 * @author byChen
 * @date 2021/11/22
 */
public enum  MyReturnCode {

    ERR_60000(60000, "系统错误,请稍候再试"){},//其它错误
    ERR_60001(60001, "登录超时,请重新登录"){},
    ERR_60002(60002, "session不能为空"){},
    ERR_60003(60003, "请先登录"){},
    ;

    MyReturnCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    private int code;
    private String msg;

    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "MyReturnCode{" + "code='" + code + '\'' + "msg='" + msg + '\'' + '}';
    }

}

缓存的键枚举


/**
 * @author byChen
 * @date 2021/11/22
 */
public interface WxConstants {
    /**
     * redis中3rd_session过期时间(单位:小时)
     */
    long TIME_OUT_SESSION = 24 * 2;
    /**
     * redis中3rd_session拼接前缀
     */
    String THIRD_SESSION_BEGIN = "app:3rd_session";
}

返回类


/**
 * 响应信息主体
 * 参照mybatis-plus R类
 * @param <T>
 * @author
 */
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class R<T> implements Serializable {
    private static final long serialVersionUID = 1L;

    @Getter
    @Setter
    private int code;

    @Getter
    @Setter
    private String msg;


    @Getter
    @Setter
    private T data;

    public Boolean isOk() {
        return code == 0;
    }

    public static <T> R<T> ok() {
        return restResult(null, 0, null);
    }

    public static <T> R<T> ok(T data) {
        return restResult(data, 0, null);
    }

    public static <T> R<T> ok(T data, String msg) {
        return restResult(data, 0, msg);
    }

    public static <T> R<T> failed() {
        return restResult(null, 1, null);
    }

    public static <T> R<T> failed(String msg) {
        return restResult(null, 1, msg);
    }

    public static <T> R<T> failed(T data) {
        return restResult(data, 1, null);
    }

    public static <T> R<T> failed(T data, String msg) {
        return restResult(data, 1, msg);
    }

    public static <T> R<T> failed(T data, int code, String msg) {
        return restResult(data, code, msg);
    }

    public static <T> R<T> failed(int code, String msg) {
        return restResult(null, code, msg);
    }

    private static <T> R<T> restResult(T data, int code, String msg) {
        R<T> apiResult = new R<>();
        apiResult.setCode(code);
        apiResult.setData(data);
        apiResult.setMsg(msg);
        return apiResult;
    }
}

步骤五:流程测试

登录后将信息存入,并返回third-session

再次访问携带third-session

没有携带报 session不能为空的错误

携带了就可以直接使用

/**
 * 供货商登录
 */
@PostMapping("/test123")
@ApiLogin
public R test123() {
    String userId = ThirdSessionHolder.getUserId();
    ManufactorUser one = service.getOne(Wrappers.<ManufactorUser>lambdaQuery()
            .eq(ManufactorUser::getId, userId));
    return R.ok(one);
}

可以直接获取到信息

接口不携带自建注解,就不需要前端携带session

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值