问题一:解决 feign 远程调用 验证失败问题:
我们在接入了SpringSecurity之后,请求的时候都会在header中带上JWT令牌,这样才能访问资源。假设这样一个情景:已经完成了认证服务的认证,前端页面的header现在是带着JWT令牌的,前端需要访问A服务,而在A服务中,需要通过Feign来远程调用B服务,A、B服务都是接入了SpringSecurity的。以下为图示:
- 前端请求A服务的时候,header中是带了token的,因为A服务需要认证,认证通过。
- A服务远程调用B服务,如果不做处理的话,因为B服务也需要认证,这时A服务是无法成功调用B服务的,因为A服务的请求没带token,无法通过B服务的认证,会被SpringSecurity拦截下来,造成调用失败。
这时可以想一个办法,让token一直在header头中传递下去:
- 使用全局配置设置Feign客户端的认证令牌:您可以在Feign客户端的全局配置中设置认证令牌,这样每个Feign请求都会自动包含该令牌。在您的A服务中,创建一个Feign配置类,并使用
RequestInterceptor
来设置"Authorization"头部,将JWT令牌添加到每个Feign请求中。这样,无论A服务调用哪个远程服务,都会自动带上认证令牌。
package com.ds.user.config;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Configuration
public class FeignConfig implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
// 在您的代码中获取JWT令牌的逻辑
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String token = request.getHeader("token");
// 从当前请求的上下文中获取JWT令牌
// String token = // 获取JWT令牌的逻辑,例如从请求的Header中获取
System.out.println("我是token 我被调用了");
// 将JWT令牌添加到Feign请求的Header中
if (token != null) {
requestTemplate.header("token",token);
}
}
}
实现:
问题二:解决多线程 feign远程调用 验证失败问题:
第一步:
我们要使用 InheritableThreadLocal 来进行存储token
在多线程环境中,ThreadLocal
的值在不同线程之间是独立的,无法共享。这意味着您在一个线程中设置的token
值,在另一个线程中是无法直接获取到的。
要在多线程环境中实现token
的共享,您可以考虑使用InheritableThreadLocal
而不是ThreadLocal
来存储token
值。InheritableThreadLocal
允许子线程继承父线程的token
值。这样,当您在父线程中设置token
时,子线程就能够获取到正确的token
值。
首先定义一个utils 类:
public class TokenHolder {
private static final InheritableThreadLocal<String> tokenHolder = new InheritableThreadLocal<>();
public static String getToken() {
return tokenHolder.get();
}
public static void setToken(String token) {
tokenHolder.set(token);
}
public static void clearToken() {
tokenHolder.remove();
}
}
第二步:
在我们使用到多线程之前就 可以把 token 存到 tokenHolder 中了。 调用其中的 setToken 方法即可
第三步:
然后,在FeignConfig
的apply
方法中使用TokenHolder.getToken()
来获取token
值: (可以和上面配合使用)
public class FeignConfig implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
String token = TokenHolder.getToken();
// ...
// 添加其他逻辑
// ...
}
}