spring-boot - 使用 webclient 在微服务之间中继令牌
问题描述
我有两个微服务,我们将它们命名为 A 和 B,位于 Spring Cloud Gateway 后面。还有一个 OAuthServer。当用户向服务 A 发送请求时,他们必须首先使用密码流从 oAuthServer 获取 JWT 令牌。
需要注意的是,服务 A 需要与服务 B 通信,但为此它需要一个访问令牌。这个想法是在访问服务 A 时使用用户提供的 JWT,通过将其中继到服务 B 从而发出请求。
在以前版本的 Spring Boot 中我会使用OAuth2RestTemplate
,但现在我需要使用Webclient
.
我所做的是创建一个过滤器以从服务 A 中的传入请求中提取承载令牌,然后将其存储在单例类中并手动将其添加到出站调用中。
@Component
public class JWTAuthorizationFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(JWTAuthorizationFilter.class);
private final String HEADER = "Authorization";
private final String PREFIX = "Bearer ";
private TokenStore tokenStore;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
if (checkJWTToken(request, response)) {
String token = extractToken(request);
if (token != null) {
log.info("*****RECEIVED REQUEST WITH TOKEN {}. ", token);
log.info("***** Will store it for future requests.");
tokenStore = TokenStore.getInstance();
tokenStore.setToken(token);
// setUpSpringAuthentication(token);
} else {
SecurityContextHolder.clearContext();
}
}
chain.doFilter(request, response);
}
private String extractToken(HttpServletRequest request) {
String jwtToken = request.getHeader(HEADER).replace(PREFIX, "");
return jwtToken;
}
private boolean checkJWTToken(HttpServletRequest request, HttpServletResponse res) {
String authenticationHeader = request.getHeader(HEADER);
if (authenticationHeader == null || !authenticationHeader.startsWith(PREFIX))
return false;
return true;
}
这TokenStore
public class TokenStore {
private static TokenStore instance;
private String token;
private TokenStore() {
}
public static synchronized TokenStore getInstance() {
if (instance == null) {
instance = new TokenStore();
}
return instance;
}
public String getToken() {
return token;
}
public synchronized void setToken(String token) {
this.token = token;
}
}
最后是网络客户端:
@Bean
@LoadBalanced
@Profile("!default")
public WebClient.Builder loadBalancedWebClientBuilder() {
final WebClient.Builder builder = WebClient.builder();
return builder;
}
使用它:
Mono<Map<Long, DrawDTO>> mono = getWebClient().get().uri(url)
.header("Authorization", "Bearer " + tokenStore.getToken()).retrieve().bodyToFlux(DTO.class)
.map(d -> Tuples.of(Long.parseLong(d.getUniqueId()), d))
.collectMap(tuple -> tuple.getT1(), tuple -> tuple.getT2()).log()
.onErrorMap(WebClientResponseException.class, ex -> handleException(ex)).timeout(Duration.ofSeconds(5));
但是,我很确定以上是一个错误。如果两个请求同时来自用户X和Y ,则 TokenStore 是一个单例,第二个过滤器调用将覆盖第一个的令牌。所以本质上服务 A 将使用用户的Y令牌来完成用户X的请求。因此,如果用户的X访问级别低于Y,这是一个安全问题。
我对上述内容有误吗?如果是,我该怎么做才能解决它?
我发现的一种解决方案是使用客户端凭据进行服务内通信,但我不能 100% 确定如何使用 Spring Boot 2.2.6 和 Spring Security 5 来做到这一点。
有没有正确的方法来做我最初打算做的事情?
解决方案
推荐阅读
- collections - JAVA 中 currentTimeMillis() 的意外行为
- java - 有没有更简单的方法来计算 Java 中的面额?
- java - 我有这个问题,我需要使用堆栈反转第一个单词和第三个单词并交换反转单词的位置
- r - 将矩阵存储为字符而不是二进制 R
- javascript - 带有 html5 视频的慢速页面
- python - 在Python中使用递归将输入数字的数字相加
- django - 如何将我的表单应用于 django 中的多个模板
- android - 将 android app bundle 上传到 playstore 时出错。您的 app bundle 以无法识别的语言 jp 为目标
- firebase - 在 instagram、snapchat、facebook 等 Flutter 应用中实现添加好友功能
- python-3.x - 转换时间戳的时区的好方法是什么?