2017-01-18 16 views
10

Tôi có ứng dụng Spring + CXF tiêu thụ API truyền: Transmission RPC chạy trong một máy chủ khác.Spring - Thử lại yêu cầu nếu dịch vụ trả về 409 Mã HTTP

Theo tài liệu truyền, bạn cần gửi mã thông báo được tạo theo yêu cầu đầu tiên. Sau đó, máy chủ phản hồi với mã http 409 cùng với tiêu đề chứa mã thông báo. Mã thông báo này phải được gửi trên tất cả các cuộc gọi tiếp theo:

2.3.1. Bảo vệ CSRF Hầu hết các máy chủ RPC truyền yêu cầu tiêu đề X-Transmission-Session-Id để gửi đi với các yêu cầu, để ngăn chặn các cuộc tấn công CSRF . Khi yêu cầu của bạn có id sai - chẳng hạn như khi bạn gửi yêu cầu đầu tiên của bạn hoặc khi máy chủ hết hạn mã thông báo CSRF - máy chủ RPC truyền sẽ trả lại lỗi HTTP 409 với đúng X-Transmission-Session- Id trong các tiêu đề của chính nó. Vì vậy, cách chính xác để xử lý phản hồi 409 là cập nhật X-phiên truyền-phiên-Id của bạn và gửi lại yêu cầu trước đó.

Tôi đang tìm giải pháp hoặc sử dụng bộ lọc CXF hoặc bộ chặn, về cơ bản sẽ xử lý phản hồi 409 và thử lại yêu cầu ban đầu thêm tiêu đề mã thông báo. Tôi nghĩ rằng khách hàng có thể duy trì mã thông báo này và gửi mã đó trong các cuộc gọi trong tương lai.

Tôi không quen thuộc với cxf vì vậy tôi đã tự hỏi nếu điều này có thể được thực hiện và làm thế nào. Bất kỳ gợi ý nào đều hữu ích.

Cảm ơn!

Trả lời

3

Ở đây spring-retry có thể được sử dụng mà hiện là một dự án độc lập và không còn là một phần của đợt mùa xuân nữa.

Khi được giải thích here thử lại gọi lại sẽ giúp thực hiện một cuộc gọi khác được cập nhật với tiêu đề mã thông báo.

Pseudo code/logic trong trường hợp này sẽ giống như dưới đây

RetryTemplate template = new RetryTemplate(); 
Foo foo = template.execute(new RetryCallback<Foo>() { 
    public Foo doWithRetry(RetryContext context) { 
     /* 
     * 1. Check if RetryContext contains the token via hasAttribute. If available set the header else proceed 
     * 2. Call the transmission API 
     * 3.a. If API responds with 409, read the token 
     * 3.a.1. Store the token in RetryContext via setAttribute method 
     * 3.a.2. Throw a custom exception so that retry kicks in 
     * 3.b. If API response is non 409 handle according to business logic 
     * 4. Return result 
     */ 
    } 
}); 

Hãy chắc chắn để cấu hình RetryTemplate với chính sách retry & backoff hợp lý để tránh bất kỳ tài nguyên tranh/bất ngờ.

Hãy biết trong nhận xét trong trường hợp có bất kỳ truy vấn/khoanh vùng nào.

N.B.: thực hiện RetryContext 's RetryContextSupport có phương pháp hasAttribute & setAttribute thừa hưởng từ mùa xuân lõi AttributeAccessor

1

Giả sử bạn đang sử dụng Apache CXF JAX RS Khách hàng nó rất dễ dàng để làm bằng cách chỉ cần tạo ra một ngoại lệ Runtime tùy chỉnh và ResponseExceptionMapper cho nó. Vì vậy, ý tưởng là để tự chuyển đổi 409 kết quả thành một số ngoại lệ và sau đó xử lý chúng một cách chính xác (trong trường hợp của bạn thử lại cuộc gọi dịch vụ).

Xem mã sau được lấy làm ví dụ đầy đủ.

@SpringBootApplication 
@EnableJaxRsProxyClient 
public class SpringBootClientApplication { 
    // This can e stored somewhere in db or elsewhere 
    private static String lastToken = ""; 

    public static void main(String[] args) { 
     SpringApplication.run(SpringBootClientApplication.class, args); 
    } 

    @Bean 
    CommandLineRunner initWebClientRunner(final TransmissionService service) { 
     return new CommandLineRunner() { 
      @Override 
      public void run(String... runArgs) throws Exception { 
       try { 
        System.out.println(service.sayHello(1, lastToken)); 
       // catch the TokenExpiredException get the new token and retry 
       } catch (TokenExpiredException ex) { 
        lastToken = ex.getNewToken(); 
        System.out.println(service.sayHello(1, lastToken)); 
       } 
      } 
     }; 
    } 

    public static class TokenExpiredException extends RuntimeException { 
     private String newToken; 

     public TokenExpiredException(String token) { 
      newToken = token; 
     } 

     public String getNewToken() { 
      return newToken; 
     } 
    } 

    /** 
     * This is where the magic is done !!!! 
    */ 
    @Provider 
    public static class TokenExpiredExceptionMapper implements ResponseExceptionMapper<TokenExpiredException> { 

     @Override 
     public TokenExpiredException fromResponse(Response r) { 
      if (r.getStatus() == 409) { 
       return new TokenExpiredException(r.getHeaderString("X-Transmission-Session-Id")); 
      } 
      return null; 
     } 

    } 

    @Path("/post") 
    public interface TransmissionService { 
     @GET 
     @Path("/{a}") 
     @Produces(MediaType.APPLICATION_JSON_VALUE) 
     String sayHello(@PathParam("a") Integer a, @HeaderParam("X-Transmission-Session-Id") String sessionId) 
      throws TokenExpiredException; 
    } 
} 
Các vấn đề liên quan