差異處

這裏顯示兩個版本的差異處。

連向這個比對檢視

java:web:restapi:x-http-method-override_filter [2016/02/26 22:32]
tony [Trace]
java:web:restapi:x-http-method-override_filter [2023/06/25 09:48]
行 1: 行 1:
-{{tag>​java spring rest}} 
-====== Incorrect response(401) when using X-HTTP-Method-Override ====== 
-===== Problem ===== 
-我們使用Spring的Rest,先前為了支援X-HTTP-Method-Override,在已存在的Filter做了此功能。某天,同事發現使用apache client api操作我們的API發生了401錯誤:​ 
-<​code>​  
-{"​code":​401,"​message":"​Incorrect response","​links":​[{"​rel":"​more info","​href":"​http://​192.168.1.110:​8080/​TestWeb/​api/​documents"​}]} 
-</​code>​ 
-要發生這個問題有兩個條件, 
-  - 使用Digest認證 
-  - 使用X-HTTP-Method-Override 
-因此在我有空時,就開始追蹤這問題。 
-===== Trace ===== 
-Sprint Security提供了DigestAuthentationFilter負責處理Digest認證。在使用者發出第一次請求後,Spring Security在察覺未經過驗證的情況下,會透過AuthenticationEntryPoint送出請求認證資訊。而為了在認證失敗時,能夠輸出xml或json格式的錯誤訊息(可參考上方),我們extend了DigestAuthenticationEntryPoint:​ 
-<code java> 
-public class CustomziedDigestAuthenticationEntryPoint extends DigestAuthenticationEntryPoint { 
  
- private static final Log logger = LogFactory.getLog(JsonDigestAuthenticationEntryPoint.class);​ 
-  
- @Autowired 
- private MessageProcessor mMessageProcessor;​ 
-  
- @Override 
- public void commence(HttpServletRequest request, 
- HttpServletResponse response, AuthenticationException authException) 
- throws IOException,​ ServletException { 
- 
- // ... skip 
-  
- httpResponse.addHeader("​WWW-Authenticate",​ authenticateHeader);​ 
- 
- try { 
- ErrorInfo erroInfo = ErrorResponseEntityCreator.createErrorInfo(request,​ HttpStatus.UNAUTHORIZED.value(),​ 
- authException.getMessage());​ 
- 
- response.setStatus(HttpStatus.UNAUTHORIZED.value());​ 
- mMessageProcessor.handle(erroInfo,​ request, response); 
- } catch (Exception e) { 
- throw new ServletException(e);​ 
- } 
- } 
- 
-} 
-</​code>​ 
-在這裡的authException,就是我們所看到的Incorrect Response。於上我就往上trace,發現DigestAuthentationFilter會透過request method去算reponse值。 
-===== Root Cause ===== 
-仔細看一下[[https://​en.wikipedia.org/​wiki/​Digest_access_authentication|Digest認證流程]],其中吸引我目光的是:​ 
-<​code>​ 
-HA1=MD5(username:​realm:​password) 
-HA2=MD5(method:​digestURI) 
-response=MD5(HA1:​nonce:​HA2) 
-</​code>​ 
-接著套入我們的情境:​\\ 
-{{:​java:​web:​restapi:​rest_digest_filter_with_override_method_bug.png?​600|}}\\ 
-  - Client收到Server認證請求。 
-  - Client對Server發送認證內容,其中包含response值。 
-  - Server的RestHeaderFilter首先處理X-HTTP-Method-Override請求,將Request method由Post改為Delete。 
-  - Server的DigestAuthenticationFilter發現response不相同。 
-  - Server回應錯誤訊息給Client。 
-這問題就在於:​ Client使用POST當method去計算HA2,而Server則用DELETE。 
-===== Solution ===== 
-那調整一下順序不就好了嗎?​ 的確,在我將RestHeaderFilter設定在Security相關Filter後,請求就可以正常處理了。但其實我們的RestHeaderFilter,還身兼將Rest API版本資訊塞到Header的責任。因此最後調整的順序為:​ 
-  - RestHeaderFilter 
-  - SpringSecurityFilter 
-  - HttpMethodOverrideFilter 
-<code java> 
-public class HttpMethodOverrideFilter extends OncePerRequestFilter implements Filter { 
- private static final Logger logger = LoggerFactory.getLogger(HttpMethodOverrideFilter.class);​ 
- protected static final String X_HTTP_METHOD_OVERRIDE_HEADER = "​X-HTTP-Method-Override";​ 
-  
- @Override 
- protected void doFilterInternal(HttpServletRequest aRequest, HttpServletResponse aResponse, FilterChain aFilterChain) 
- throws ServletException,​ IOException { 
-  
- String methodOverrideValue = aRequest.getHeader(X_HTTP_METHOD_OVERRIDE_HEADER);​ 
- if (methodOverrideValue != null && !methodOverrideValue.isEmpty()) { 
- String overrideMethod = methodOverrideValue.toUpperCase(Locale.ENGLISH);​  
- aRequest = new HttpMethodRequestWrapper(aRequest,​ overrideMethod);​ 
- } 
- aFilterChain.doFilter(aRequest,​ aResponse); 
- } 
- 
- private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper { 
- private final String method; 
- 
- public HttpMethodRequestWrapper(HttpServletRequest request, String method) { 
- super(request);​ 
- this.method = method; 
- } 
- 
- @Override 
- public String getMethod() { 
- return this.method;​ 
- } 
- } 
-} 
-</​code>​ 
-===== Note ===== 
-如果有使用PostMan,可以仔細看看選擇不同的HTTP method,是否會產生不同的digest response。 
-=====    ===== 
----- 
-\\ 
-~~DISQUS~~