跨域二三事之options请求


声明:本文转载自https://my.oschina.net/qixiaobo025/blog/1595565,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

背景

今天小伙伴问了一个比较奇怪的问题 在某个子系统中通过跨域请求发现POST请求无法正常拿到cookie导致报错

我们从代码来分析一下原因

思路

最重要的永远是碰到问题解决问题的思路。首先碰到xhr没有携带cookie导致重定向到cas登录页的问题之前碰到过

跨域二三事之withCredentials

那么小伙伴也确定是加了对应的withCredentials 并且get请求可以正常返回。

那么缘何出现了POST请求不成功呢?

知识点

我们知道使用cors做跨域请求是分两种的。

  • 简单请求
  • 非简单请求

The term user credentials for the purposes of this specification means cookies, HTTP authentication, and client-side SSL certificates that would be sent based on the user agent's previous interactions with the origin. Specifically it does not refer to proxy authentication or the Origin header. [COOKIES]

The term cross-origin is used to mean non same origin.

A method is said to be a simple method if it is a case-sensitive match for one of the following:

  • GET
  • HEAD
  • POST

To protect resources against cross-origin requests that could not originate from certain user agents before this specification existed a preflight request is made to ensure that the resource is aware of this specification. The result of this request is stored in a preflight result cache.

The steps below describe what user agents must do for a cross-origin request with preflight. This is a request to a non same-origin URL that first needs to be authorized using either a preflight result cacheentry or a preflight request.

  1. Go to the next step if the following conditions are true:

    Otherwise, make a preflight request. Fetch the request URL from originsource origin using referrer source as override referrer source with the manual redirect flag and the block cookies flag set, using the method OPTIONS, and with the following additional constraints:

这里的preflight也就是我们经常说的options请求【需要注意简单请求的命中是大小写敏感的】

但是options具有一个特点在 发送时 会排除author header,user credentials,request body

这样自然不会发送cookie

解析

之前的方案在做跨域并未使用filter做【filter的生命周期各位学习servlet应该很熟悉】而是采用的Spring interceptor

我们在配置跨域的时候springboot提供了较好的方案

@Override public void addCorsMappings(CorsRegistry registry) {     registry.addMapping("/**"); }

spring的cors使用了

/*  * Copyright 2002-2016 the original author or authors.  *  * Licensed under the Apache License, Version 2.0 (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  *  *      http://www.apache.org/licenses/LICENSE-2.0  *  * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */   package org.springframework.web.cors;   import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;   import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;   import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.util.CollectionUtils; import org.springframework.web.util.WebUtils;   /**  * The default implementation of {@link CorsProcessor}, as defined by the  * <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>.  *  * <p>Note that when input {@link CorsConfiguration} is {@code null}, this  * implementation does not reject simple or actual requests outright but simply  * avoid adding CORS headers to the response. CORS processing is also skipped  * if the response already contains CORS headers, or if the request is detected  * as a same-origin one.  *  * @author Sebastien Deleuze  * @author Rossen Stoyanchev  * @since 4.2  */ public class DefaultCorsProcessor implements CorsProcessor {      private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");      private static final Log logger = LogFactory.getLog(DefaultCorsProcessor.class);        @Override    @SuppressWarnings("resource")    public boolean processRequest(CorsConfiguration config, HttpServletRequest request, HttpServletResponse response)          throws IOException {         if (!CorsUtils.isCorsRequest(request)) {          return true;       }         ServletServerHttpResponse serverResponse = new ServletServerHttpResponse(response);       if (responseHasCors(serverResponse)) {          logger.debug("Skip CORS processing: response already contains \"Access-Control-Allow-Origin\" header");          return true;       }         ServletServerHttpRequest serverRequest = new ServletServerHttpRequest(request);       if (WebUtils.isSameOrigin(serverRequest)) {          logger.debug("Skip CORS processing: request is from same origin");          return true;       }         boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);       if (config == null) {          if (preFlightRequest) {             rejectRequest(serverResponse);             return false;          }          else {             return true;          }       }         return handleInternal(serverRequest, serverResponse, config, preFlightRequest);    }      private boolean responseHasCors(ServerHttpResponse response) {       try {          return (response.getHeaders().getAccessControlAllowOrigin() != null);       }       catch (NullPointerException npe) {          // SPR-11919 and https://issues.jboss.org/browse/WFLY-3474          return false;       }    }      /**     * Invoked when one of the CORS checks failed.     * The default implementation sets the response status to 403 and writes     * "Invalid CORS request" to the response.     */    protected void rejectRequest(ServerHttpResponse response) throws IOException {       response.setStatusCode(HttpStatus.FORBIDDEN);       response.getBody().write("Invalid CORS request".getBytes(UTF8_CHARSET));    }      /**     * Handle the given request.     */    protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response,          CorsConfiguration config, boolean preFlightRequest) throws IOException {         String requestOrigin = request.getHeaders().getOrigin();       String allowOrigin = checkOrigin(config, requestOrigin);         HttpMethod requestMethod = getMethodToUse(request, preFlightRequest);       List<HttpMethod> allowMethods = checkMethods(config, requestMethod);         List<String> requestHeaders = getHeadersToUse(request, preFlightRequest);       List<String> allowHeaders = checkHeaders(config, requestHeaders);         if (allowOrigin == null || allowMethods == null || (preFlightRequest && allowHeaders == null)) {          rejectRequest(response);          return false;       }         HttpHeaders responseHeaders = response.getHeaders();       responseHeaders.setAccessControlAllowOrigin(allowOrigin);       responseHeaders.add(HttpHeaders.VARY, HttpHeaders.ORIGIN);         if (preFlightRequest) {          responseHeaders.setAccessControlAllowMethods(allowMethods);       }         if (preFlightRequest && !allowHeaders.isEmpty()) {          responseHeaders.setAccessControlAllowHeaders(allowHeaders);       }         if (!CollectionUtils.isEmpty(config.getExposedHeaders())) {          responseHeaders.setAccessControlExposeHeaders(config.getExposedHeaders());       }         if (Boolean.TRUE.equals(config.getAllowCredentials())) {          responseHeaders.setAccessControlAllowCredentials(true);       }         if (preFlightRequest && config.getMaxAge() != null) {          responseHeaders.setAccessControlMaxAge(config.getMaxAge());       }         response.flush();       return true;    }      /**     * Check the origin and determine the origin for the response. The default     * implementation simply delegates to     * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)}.     */    protected String checkOrigin(CorsConfiguration config, String requestOrigin) {       return config.checkOrigin(requestOrigin);    }      /**     * Check the HTTP method and determine the methods for the response of a     * pre-flight request. The default implementation simply delegates to     * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)}.     */    protected List<HttpMethod> checkMethods(CorsConfiguration config, HttpMethod requestMethod) {       return config.checkHttpMethod(requestMethod);    }      private HttpMethod getMethodToUse(ServerHttpRequest request, boolean isPreFlight) {       return (isPreFlight ? request.getHeaders().getAccessControlRequestMethod() : request.getMethod());    }      /**     * Check the headers and determine the headers for the response of a     * pre-flight request. The default implementation simply delegates to     * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)}.     */    protected List<String> checkHeaders(CorsConfiguration config, List<String> requestHeaders) {       return config.checkHeaders(requestHeaders);    }      private List<String> getHeadersToUse(ServerHttpRequest request, boolean isPreFlight) {       HttpHeaders headers = request.getHeaders();       return (isPreFlight ? headers.getAccessControlRequestHeaders() : new ArrayList<String>(headers.keySet()));    }   }

 

/**  * Utility class for CORS request handling based on the  * <a href="http://www.w3.org/TR/cors/">CORS W3C recommandation</a>.  *  * @author Sebastien Deleuze  * @since 4.2  */ public abstract class CorsUtils {      /**     * Returns {@code true} if the request is a valid CORS one.     */    public static boolean isCorsRequest(HttpServletRequest request) {       return (request.getHeader(HttpHeaders.ORIGIN) != null);    }      /**     * Returns {@code true} if the request is a valid CORS pre-flight one.     */    public static boolean isPreFlightRequest(HttpServletRequest request) {       return (isCorsRequest(request) && HttpMethod.OPTIONS.matches(request.getMethod()) &&             request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD) != null);    }   }

看到在PreFlightRequest的请求时会返回response

 

但是当我们使用casFilter的时候 由于没有帮我们处理Options请求 在收到Options请求的时候已经判断cookie没有携带 自然导致重定向到cas画面

也可以参考  https://stackoverflow.com/questions/10133497/cors-withcredentials-xhr-preflight-not-posting-cookies-in-firefox

本文发表于2017年12月27日 16:32
(c)注:本文转载自https://my.oschina.net/qixiaobo025/blog/1595565,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2743 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1