差異處
這裏顯示兩個版本的差異處。
Both sides previous revision 前次修改 下次修改 | 前次修改 | ||
java:web:restapi:x-http-method-override_filter [2016/02/26 21:47] tony |
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 ====== | ====== Incorrect response(401) when using X-HTTP-Method-Override ====== | ||
===== Problem ===== | ===== Problem ===== | ||
行 12: | 行 13: | ||
Sprint Security提供了DigestAuthentationFilter負責處理Digest認證。在使用者發出第一次請求後,Spring Security在察覺未經過驗證的情況下,會透過AuthenticationEntryPoint送出請求認證資訊。而為了在認證失敗時,能夠輸出xml或json格式的錯誤訊息(可參考上方),我們extend了DigestAuthenticationEntryPoint: | Sprint Security提供了DigestAuthentationFilter負責處理Digest認證。在使用者發出第一次請求後,Spring Security在察覺未經過驗證的情況下,會透過AuthenticationEntryPoint送出請求認證資訊。而為了在認證失敗時,能夠輸出xml或json格式的錯誤訊息(可參考上方),我們extend了DigestAuthenticationEntryPoint: | ||
<code java> | <code java> | ||
- | public class JsonDigestAuthenticationEntryPoint extends DigestAuthenticationEntryPoint { | + | public class CustomziedDigestAuthenticationEntryPoint extends DigestAuthenticationEntryPoint { |
private static final Log logger = LogFactory.getLog(JsonDigestAuthenticationEntryPoint.class); | private static final Log logger = LogFactory.getLog(JsonDigestAuthenticationEntryPoint.class); | ||
行 41: | 行 42: | ||
} | } | ||
</code> | </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由於經過Request method的取代,會用DELETE去計算HA2。 | ||
+ | ===== 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~~ |