Propagate HTTP header (JWT Token) over services using spring rest template

后端 未结 3 1593
萌比男神i
萌比男神i 2020-12-14 04:23

I have a microservice architecture, both of them securized by spring security an JWT tokens.

So, when I call my first microservice, I want to take the JWT token and

相关标签:
3条回答
  • 2020-12-14 04:55

    I've accomplished the task, creating a custom Filter

    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 {}
    
    
    }
    

    Then, setting in my config

        @Bean
    public FilterRegistrationBean getPeticionFilter() {
    
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new RequestFilter());
        registration.addUrlPatterns("/*");
        registration.setName("requestFilter");
    
        return registration;
    }
    

    With that in mind, I've create another class with a ThreadLocal variable to pass the JWT token from the Controller to the Rest Templace interceptor

    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);
    
    }
    
    }
    

    Add interceptor to the config

      @PostConstruct
    public void addInterceptors() {
        List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
        interceptors.add(new RestTemplateInterceptor());
        restTemplate.setInterceptors(interceptors);
    }
    
    0 讨论(0)
  • 2020-12-14 04:58

    Basically your token should be located in the header of the request, like for example: Authorization: Bearer . For getting it you can retrieve any header value by @RequestHeader() in your controller:

    @GetMapping("/someMapping")
    public String someMethod(@RequestHeader("Authorization") String token) {
    
    }
    

    Now you can place the token within the header for the following request:

    HttpHeaders headers = new HttpHeaders();
    headers.set("Authorization", token);
    
    HttpEntity<RestRequest> entityReq = new HttpEntity<RestRequest>(request, headers);
    

    Now you can pass the HttpEntity to your rest template:

    template.exchange("RestSvcUrl", HttpMethod.POST, entityReq, SomeResponse.class);
    

    Hope I could help

    0 讨论(0)
  • 2020-12-14 05:06

    I think it is better to add the interceptor specifically to the RestTemplate, like this:

    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
        }
    }
    

    And add it to the RestTemplate like so:

    @Bean
    fun restTemplate(): RestTemplate {
        val restTemplate = RestTemplate()
    restTemplate.interceptors.add(RestTemplateHeaderModifierInterceptor(authenticationService)) // add interceptor to send JWT along with requests.
        return restTemplate
    }
    

    That way, every time you need a RestTemplate you can just use autowiring to get it. You do need to implement the AuthenticationService still to get the token from the TokenStore, like this:

    
    val details = SecurityContextHolder.getContext().authentication.details
    if (details is OAuth2AuthenticationDetails) {
       val token = tokenStore.readAccessToken(details.tokenValue)
       return token.value
    }
    
    
    0 讨论(0)
提交回复
热议问题