【发布时间】:2018-03-25 13:09:03
【问题描述】:
我有一个微服务架构,它们都由 Spring Security 和 JWT 令牌保护。
所以,当我调用我的第一个微服务时,我想获取 JWT 令牌并使用这些凭据向另一个服务发送请求。
如何检索令牌并再次发送到其他服务?
【问题讨论】:
-
有一些代码示例会很棒:)
我有一个微服务架构,它们都由 Spring Security 和 JWT 令牌保护。
所以,当我调用我的第一个微服务时,我想获取 JWT 令牌并使用这些凭据向另一个服务发送请求。
如何检索令牌并再次发送到其他服务?
【问题讨论】:
基本上,您的令牌应该位于请求的标头中,例如: Authorization: Bearer 。要获取它,您可以通过控制器中的 @RequestHeader() 检索任何标头值:
@GetMapping("/someMapping")
public String someMethod(@RequestHeader("Authorization") String token) {
}
现在您可以将令牌放在以下请求的标头中:
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);
HttpEntity<RestRequest> entityReq = new HttpEntity<RestRequest>(request, headers);
现在您可以将 HttpEntity 传递给您的 rest 模板:
template.exchange("RestSvcUrl", HttpMethod.POST, entityReq, SomeResponse.class);
希望我能帮上忙
【讨论】:
我已经完成了任务,创建了一个自定义过滤器
public class RequestFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader(RequestContext.REQUEST_HEADER_NAME);
if (token == null || "".equals(token)) {
throw new IllegalArgumentException("Can't retrieve JWT Token");
}
RequestContext.getContext().setToken(token);
chain.doFilter(request, response);
}
@Override
public void destroy() { }
@Override
public void init(FilterConfig arg0) throws ServletException {}
}
然后,在我的配置中设置
@Bean
public FilterRegistrationBean getPeticionFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new RequestFilter());
registration.addUrlPatterns("/*");
registration.setName("requestFilter");
return registration;
}
考虑到这一点,我创建了另一个带有 ThreadLocal 变量的类,以将 JWT 令牌从 Controller 传递到 Rest Templace 拦截器
public class RequestContext {
public static final String REQUEST_HEADER_NAME = "Authorization";
private static final ThreadLocal<RequestContext> CONTEXT = new ThreadLocal<>();
private String token;
public static RequestContext getContext() {
RequestContext result = CONTEXT.get();
if (result == null) {
result = new RequestContext();
CONTEXT.set(result);
}
return result;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor{
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
String token = RequestContext.getContext().getToken();
request.getHeaders().add(RequestContext.REQUEST_HEADER_NAME, token);
return execution.execute(request, body);
}
}
在配置中添加拦截器
@PostConstruct
public void addInterceptors() {
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
interceptors.add(new RestTemplateInterceptor());
restTemplate.setInterceptors(interceptors);
}
【讨论】:
CONTEXT.remove()。 Web 服务器通常使用线程池,如果您将值设置为 threadlocal 并且不删除它,它将在其他请求中可用。 stackoverflow.com/questions/30328437/…
我认为最好将拦截器专门添加到RestTemplate中,如下所示:
class RestTemplateHeaderModifierInterceptor(private val authenticationService: IAuthenticationService) : ClientHttpRequestInterceptor {
override fun intercept(request: org.springframework.http.HttpRequest, body: ByteArray, execution: ClientHttpRequestExecution): ClientHttpResponse {
if (!request.headers.containsKey("Authorization")) {
// don't overwrite, just add if not there.
val jwt = authenticationService.getCurrentUser()!!.jwt
request.headers.add("Authorization", "Bearer $jwt")
}
val response = execution.execute(request, body)
return response
}
}
然后像这样将它添加到 RestTemplate 中:
@Bean
fun restTemplate(): RestTemplate {
val restTemplate = RestTemplate()
restTemplate.interceptors.add(RestTemplateHeaderModifierInterceptor(authenticationService)) // add interceptor to send JWT along with requests.
return restTemplate
}
这样,每次您需要 RestTemplate 时,您都可以使用自动装配来获取它。您仍然需要实现 AuthenticationService 才能从 TokenStore 获取令牌,如下所示:
val details = SecurityContextHolder.getContext().authentication.details
if (details is OAuth2AuthenticationDetails) {
val token = tokenStore.readAccessToken(details.tokenValue)
return token.value
}
【讨论】:
request.headers.add("Authorization", "Bearer $jwt") 可以替换为 request.headers.setBearerAuth(jwt)