微服务:从header中获取用户存入当前线程

文章讲述了如何在SpringCloudGateway中实现一个过滤器,解析JWTtoken获取用户信息,并将其添加到请求头。还介绍了如何使用ThreadLocal存储用户信息,并在业务拦截器中应用这些信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、从网关gateway工程filter中解析token携带的当前用户信息并添加到header中

            //获取token携带的id
            Object userid = claimsBody.get("id");
            //在header中添加新的信息
            ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
                httpHeaders.add("userId", userid + "");
            }).build();
            //重置header
            exchange.mutate().request(serverHttpRequest).build();

        filter完整代码

package com.heima.app.gateway.filter;

import com.heima.app.gateway.util.AppJwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
@Slf4j
public class AuthorizeFilter implements Ordered, GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        log.info("拦截到请求 {}", request.getURI().getPath());

        //2.判断是否是登录//在header中添加新的信息
        if(request.getURI().getPath().contains("/login")){
            //放行
            return chain.filter(exchange);
        }


        //3.获取token
        String token = request.getHeaders().getFirst("token");
        log.info("获取到token {}", token);

        //4.判断token是否存在
        if(StringUtils.isBlank(token)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            log.info("token不存在");
            return response.setComplete();
        }

        //5.判断token是否有效
        try {
            Claims claimsBody = AppJwtUtil.getClaimsBody(token);
            //是否是过期
            int result = AppJwtUtil.verifyToken(claimsBody);
            if(result == 1 || result  == 2){
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                log.info("token过期");
                return response.setComplete();
            }

            //获取token携带的id
            Object userid = claimsBody.get("id");
            //在header中添加新的信息
            ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
                httpHeaders.add("userId", userid + "");
            }).build();
            //重置header
            exchange.mutate().request(serverHttpRequest).build();

        }catch (Exception e){
            e.printStackTrace();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        log.info("放行===> {}",request.getURI().getPath());
        //6.放行
        return chain.filter(exchange);
    }

    /**
     * 优先级设置  值越小  优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

2、在utils工程下创建一个线程工具类WmThreadLocalUtil

package com.heima.utils.thread;


import com.heima.model.common.wemedia.model.po.WmUser;

public class WmThreadLocalUtil {

    private final static ThreadLocal<WmUser> WM_USER_THREAD_LOCAL = new ThreadLocal<>();

    //存入线程中
    public static void setUser(WmUser wmUser){
        WM_USER_THREAD_LOCAL.set(wmUser);
    }

    //从线程中获取
    public static WmUser getUser(){
        return WM_USER_THREAD_LOCAL.get();
    }

    //清理
    public static void clear(){
        WM_USER_THREAD_LOCAL.remove();
    }

}

     

3、在业务项目中创建拦截器 WmTokenInterceptor 

        得到header中的用户信息并存入当前线程

package com.heima.wemedia.interceptor;

import com.heima.model.common.wemedia.model.po.WmUser;
import com.heima.utils.thread.WmThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;

@Slf4j
public class WmTokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //得到header中的信息
        String userId = request.getHeader("userId");
        Optional<String> optional = Optional.ofNullable(userId);
        if(optional.isPresent()){
            //把用户id存入threadloacl中
            WmUser wmUser = new WmUser();
            wmUser.setId(Integer.valueOf(userId));
            WmThreadLocalUtil.setUser(wmUser);
            log.info("wmTokenFilter设置用户信息到threadlocal中...");
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("清理threadlocal...");
        WmThreadLocalUtil.clear();
    }
}

4、在业务工程中创建配置类WebMvcConfig并实现请求拦截

package com.heima.wemedia.config;

import com.heima.wemedia.interceptor.WmTokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new WmTokenInterceptor()).addPathPatterns("/**");
    }
}

 

<think>好的,用户想知道如何在Feign接口调用时默认将登录信息添加到请求头中。首先,我得回忆一下Feign的相关知识。记得Feign可以通过拦截器来修改请求,比如添加请求头。用户提供的引用资料里提到实现RequestInterceptor接口,创建一个拦截器类,然后在apply方法里设置请求头。比如引用3中的代码示例,直接给requestTemplate添加authorization头,值设为"2"。不过这可能只是示例,实际中需要动态获取用户信息。 接着,用户提到需要默认添加登录信息,所以拦截器需要在所有Feign客户端调用时自动生效。可能的问题是如何在拦截器中获取当前用户信息。引用5指出,可以使用RequestContextHolder来获取请求属性,这样在同一个线程中,拦截器能拿到原始的请求头信息。但需要注意,Feign调用可能是在异步线程中执行,这时候RequestContextHolder可能无法获取到原来的请求信息,需要传递上下文。 另外,引用4提到,如果值为空字符串,Feign的header方法会过滤掉该头,所以需要确保登录信息不为空。还有引用2建议遍历当前请求头并添加到Feign请求中,这样可以传递所有必要的参数,但用户可能只需要特定的登录信息,比如token。 所以,实现步骤应该是:1. 创建实现RequestInterceptor的拦截器类;2. 在apply方法中,从当前请求上下文中获取登录信息(比如token);3. 将token添加到Feign的请求头中。需要确保拦截器被Spring管理,并且Feign客户端配置正确。 可能的坑点包括:线程切换导致RequestContextHolder失效,这时候可能需要使用Hystrix的策略或者手动传递属性。另外,微服务之间传递敏感信息需要注意安全性,可能需要加密或使用JWT。 用户可能需要具体的代码示例,所以需要给出拦截器的实现,并说明如何注册到Spring中。同时提醒在网关或前置服务中正确设置原始请求头,确保登录信息能够传递到Feign拦截器。</think>### 如何在Feign接口调用时添加登录信息到请求头 #### 实现步骤 1. **创建Feign请求拦截器** ```java import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; public class AuthFeignInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { // 从请求上下文中获取当前请求头 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes != null) { // 获取登录信息(示例为authorization头) String token = attributes.getRequest().getHeader("authorization"); if (token != null && !token.isEmpty()) { template.header("authorization", token); //[^3][^5] } } } } ``` 2. **注册拦截器到Spring容器** ```java @Configuration public class FeignConfig { @Bean public AuthFeignInterceptor authFeignInterceptor() { return new AuthFeignInterceptor(); } } ``` 3. **网关前置处理(可选)** 在网关服务中确保已通过过滤器将登录信息存入请求头,例如: ```java // 网关过滤器示例 public class AuthFilter implements GlobalFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest() .mutate() .header("authorization", "Bearer xxxxx") // 实际应从登录上下文获取 .build(); return chain.filter(exchange.mutate().request(request).build()); } } ``` #### 关键原理 - **请求上下文传递**:通过`RequestContextHolder`获取当前线程绑定的请求属性,需确保Feign调用在同步线程中执行[^5] - **拦截器作用范围**:注册后的拦截器会自动作用于所有Feign客户端调用[^1] - **空值处理**:Feign会自动过滤空字符串的header值,需确保token有效[^4] #### 注意事项 1. 异步调用场景需手动传递上下文 2. 微服务间传递敏感信息建议使用JWT等加密方式 3. 在Zuul网关或Spring Cloud Gateway中需提前注入认证信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值