AJAX cors 问题:预检响应中的 Access-Control-Allow-Headers 不允许请求标头字段 access-control-allow-credentials

Posted

技术标签:

【中文标题】AJAX cors 问题:预检响应中的 Access-Control-Allow-Headers 不允许请求标头字段 access-control-allow-credentials【英文标题】:AJAX cors issue : Request header field access-control-allow-credentials is not allowed by Access-Control-Allow-Headers in preflight response 【发布时间】:2021-06-06 02:04:35 【问题描述】:

我正在从主机(http://abc.com.au) 向运行在主机 http://localhost:8081 上的 Spring Cloud API 网关发送一个 ajax 请求。

jquery AJAX 请求代码。

SimplifiedAdoptionBulkUpload.jsp

setCookie("username", 'abc', 30);

$.ajax(
            type: "POST",
            enctype: 'multipart/form-data',
            url: "http://localhost:8081/api/v1/users/bulkUpload",
            xhrFields: 
                withCredentials: true
            ,
            data: newData,
            processData: false,
            contentType: false,
            crossDomain: true,
            cache: false,
            timeout: 600000,
            success: function (data) 

                .....
                
                
            ,
            error: function (e) 
                $('#btnSubmit').prop("disabled", false);
                $('#txtMessage').text('Error Occured');
            
            ,
            beforeSend: function (xhr) 
                xhr.setRequestHeader('Authorization', 'Bearer ' + token);
                xhr.setRequestHeader('Access-Control-Allow-Credentials', 'true');
                xhr.setRequestHeader('Access-Control-Allow-Headers', "Origin,Cookie,X-Requested-With, Content-Type, Accept, Authorization");
                $('body').addClass("loading");
            
            
        );

spring 云网关 cors 配置。

@Configuration
public class PreFlightCorsConfiguration 

    private static final String ALLOWED_HEADERS = "Origin,Cookie,X-Requested-With, Content-Type, Accept, Authorization";
    private static final String ALLOWED_METHODS = "*";
    private static final String ALLOWED_ORIGIN = "http://abc.com.au";
    private static final String ALLOWED_EXPOSE = "*";
    private static final String MAX_AGE = "3600";

    @Bean
    public WebFilter corsFilter() 
        return (ServerWebExchange ctx, WebFilterChain chain) -> 
            ServerHttpRequest request = ctx.getRequest();
            if (CorsUtils.isCorsRequest(request)) 
                ServerHttpResponse response = ctx.getResponse();
                HttpHeaders headers = response.getHeaders();
                headers.set("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
                headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
                headers.add("Access-Control-Max-Age", MAX_AGE);
                headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
                headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE);
                headers.add("Access-Control-Allow-Credentials", "true");
                if (request.getMethod() == HttpMethod.OPTIONS) 
                    response.setStatusCode(HttpStatus.OK);
                    return Mono.empty();
                
            
            return chain.filter(ctx);
        ;
    

我在 chrome 控制台中收到以下错误。

SimplifiedAdoptionBulkUpload:1 访问 XMLHttpRequest at 来自原点的“http://localhost:8081/api/v1/users/bulkUpload” “http://abc.com.au”已被 CORS 策略阻止:请求标头 字段 access-control-allow-credentials 不允许 预检响应中的 Access-Control-Allow-Headers。

更新:

我从 ajax 请求中删除了这一行。

xhr.setRequestHeader('Access-Control-Request-Headers', "Origin,Cookie,X-Requested-With, Content-Type, Accept, Authorization");

我在 jsp 和 servlet 过滤器中为 cookie 设置了两个 cookie(accces_token,refresh_token)。

public class AdminSecurityFilter implements Filter 


 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException 
        boolean error = false;

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        request.setCharacterEncoding("UTF-8");


        if(authenticated) 
                    String accessToken = getUserAccessToken(userStr,passStr);
                    if(!accessToken.isEmpty()) 
                    Cookie cookie = new Cookie("access_token", accessToken);
                    cookie.setPath("/");
                    //cookie.setHttpOnly(true);
                    //cookie.setDomain("localhost");
                    //TODO: When in production must do cookie.setSecure(true);
                    cookie.setMaxAge(3600);
                    response.setHeader("Access-Control-Allow-Credentials", "true");
                    response.setHeader("Access-Control-Allow-Origin", "http://localhost:8081");

                    response.addCookie(cookie);
                    
                    
                    if(!refreshToken.isEmpty()) 
                        Cookie cookie = new Cookie("refresh_token", refreshToken);
                        cookie.setPath("/");
                        //cookie.setHttpOnly(true);
                        //cookie.setDomain("localhost");
                        //TODO: When in production must do cookie.setSecure(true);
                        cookie.setMaxAge(10000);
                        response.setHeader("Access-Control-Allow-Credentials", "true");
                        response.setHeader("Access-Control-Allow-Origin", "http://localhost:8081");

                        response.addCookie(cookie);
                    
                



在 jsp 文件中我也使用 jquery 设置值 cookie。

 function setCookie(cname,cvalue,exdays) 
              var d = new Date();
              d.setTime(d.getTime() + (exdays*24*60*60*1000));
              var expires = "expires=" + d.toGMTString();
              document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
            

setCookie("username", 'Nirangaa', 30);

但这些 cookie 值(用户名、refresh_token、access_token)在 Spring Cloud Gateway 的标头中缺失。 为什么 AJAX 请求没有将附加在 post 请求中的 cookie 值发送到 apigteway 端。 @T.J。如果您能解释为什么会发生这种情况,Crowder 将不胜感激

@Component
public class AccessTokenCheckingGlobalFilterPre extends AbstractGatewayFilterFactory<AccessTokenCheckingGlobalFilterPre.Config> 


    public AccessTokenCheckingGlobalFilterPre() 
        super(AccessTokenCheckingGlobalFilterPre.Config.class);
    

    @Override
    public GatewayFilter apply(AccessTokenCheckingGlobalFilterPre.Config config) 
        return (exchange, chain) -> 
             Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
             ServerHttpRequest request = null;
             if (route != null) 
                
                
                 request = exchange.getRequest();
                 
                 
                 
                 **//[Host:"localhost:8081", Connection:"keep-alive", Content-Length:"7391", Accept:"*/*", Access-Control-Allow-Credentials:"true", Authorization:"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiamFjcGx1c19yZXNvdXJjZSJdLCJFeHBpcmVzSW4iOjM1OTksInVzZXJfbmFtZSI6Im5zYW5kYXJ1d2FAd2lsZXkuY29tIiwic2NvcGUiOlsiUkVBRCxXUklURSJdLCJleHAiOjE2MTUxOTQwNjIsInVzZXJOYW1lIjoibnNhbmRhcnV3YUB3aWxleS5jb20iLCJhdXRob3JpdGllcyI6WyJST0xFX0FkbWluaXN0cmF0b3IiLCJST0xFX3RlYWNoZXIiLCJST0xFX3NjaGFkbWluIiwiUk9MRV9zdHVkZW50Il0sImp0aSI6IjBkMjQ3ZjJkLTg0NjgtNDUwYS04NjI3LTAzOGI4ZmRlZjQ2MCIsImNsaWVudF9pZCI6ImphY3BsdXMifQ.GT0ayX01TO0fNY834n5_vXsqmt2P3s2C7hfMH-_FLvsNT-p9uIj5i0U66ZdouDUaL9xu1iy4wdfxLexfvilwnO10fTX5I0U7DHqHQetLUwCJOhET0hN-PyFcJRe7k3C3BUe8f7iuDd6Y4VbLCYTTpp4KSjWcS5fv-BTrvTvktwtkq5oFK2iCtPS6JwqPcFsvt2MYNi9UlGFeik_a-iyuTTa7xf7VgL2XFnGqcu3bOTUNxj4AhG91YL5har3OnFTbZnKFxVWykEz7MJFSXzmhmSS5rNYAbC5FBg65VFmzD0j9G4j1xPODxmLEEeE2Qj5FSe6VTnEZIiFHTLd27IuYyA", User-Agent:"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/75.0.3770.142 Safari/537.36", Origin:"http://abc.com.au", Content-Type:"multipart/form-data; boundary=----WebKitFormBoundaryV5bORQSNHAa3kxFR", Referer:"http://admin.jacplus.com.au/admin/SimplifiedAdoptionBulkUpload", Accept-Encoding:"gzip, deflate, br", Accept-Language:"en-US,en;q=0.9"]**

                 HttpHeaders headers = request.getHeaders();
                 
                 
                 
                 
                 
                 
                 
                 List<String> authorizationHeader = headers.get(HttpHeaders.AUTHORIZATION);
                 String value = authorizationHeader.get(0);
                 
                 MultiValueMap<String, HttpCookie> cookies = request.getCookies(); /// nulll 

                
                 
             
             
             return chain.filter(exchange.mutate().request(request).build());
            
            
            
        ;
    

【问题讨论】:

【参考方案1】:

Access-Control-Allow-XYZ 标头是 响应 标头,而不是请求标头。从您的$.ajax 电话中删除它们。您可能需要 Access-Control-Request-XYZ 标头,例如:

xhr.setRequestHeader('Access-Control-Request-Headers', "Origin,Cookie,X-Requested-With, Content-Type, Accept, Authorization");
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^

这是一个有效的请求标头。 (不过,CORS spec 中没有列出 Access-Control-Request-Credentials 标头,因此应该删除添加 Access-Control-Allow-Credentials 标头的另一行。)

更一般地说,请求中的任何标头都必须由您的响应的 Access-Control-Allow-Headers 值允许,而您的响应中没有。但解决方法是将其从请求中删除,而不是将其添加到 Access-Control-Allow-Headers 值中。

另见:CORS specification

【讨论】:

我更新了问题。你能看看吗? @NirangaSandaruwan - 在您的编辑中,您说 “我从 ajax 请求中删除了这一行。xhr.setRequestHeader('Access-Control-Request-Headers', "Origin,Cookie,X-Requested-With, Content-Type, Accept, Authorization"); 该行不在 ajax 请求中。 ajax 请求设置为Access-Control-Allow-Headers,而不是Access-Control-Request-Headers。您可能需要Access-Control-Request-Headers。但后来你开始谈论饼干。问题是因为您的 CORS 设置错误而被 SOP 拒绝。我就是这么回答的。 我更正了 ajax 请求标头。您知道 cookie 值没有与 ajax 请求一起发送到 apigatewat 端的原因吗?我在 apigateway 端得到了这些标头。[Host:"localhost:8081", Connection:"keep-alive", Content-Length:"7391", Accept:"/", Access-Control- Allow-Credentials:"true", Authorization:"Bearer ey_FLvsNT-p", User-Agent:"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36", Origin:"abc.com.au", Content-Type:"multip", Referer:"http:///abc/", Accept-Encoding:"gzip, deflate, br", Accept-Language:"en-US,en ;q=0.9"]

以上是关于AJAX cors 问题:预检响应中的 Access-Control-Allow-Headers 不允许请求标头字段 access-control-allow-credentials的主要内容,如果未能解决你的问题,请参考以下文章

来自 ASP NET Web API 的 CORS 阻止 Ajax POST 调用 - 对预检请求的响应未通过访问控制检查

预检请求没问题,然后,经过身份验证,响应不包含允许 cors 标头

CORS 预检响应错误

Yii2 和 reactjs CORS 过滤器给出错误:预检响应具有无效的 HTTP 状态代码 401

cors(cors 策略:对预检请求的响应未通过访问控制检查)角度 7 中的错误

OAuth 中的 CORS:对预检请求的响应未通过访问控制检查