浏览器调用API时request.getHeader("Authorization")为null,Postman正常
问题原因
- Postman属于接口调试工具,不受浏览器同源策略约束,不会自动发起CORS预检请求,会直接携带配置的所有请求头发送实际业务请求,因此可以正常读取到
Authorization头。 - 浏览器在跨域场景下发送包含
Authorization这类自定义请求头的请求前,会自动先发送OPTIONS方法的预检请求,预检请求本身不会携带任何业务自定义请求头(包括Authorization),原过滤器不分请求类型直接读取头,在预检请求阶段读到的必然是null。 - 原CORS配置存在兼容性问题:
Access-Control-Allow-Headers使用通配符*在部分浏览器版本、以及请求携带凭证(如Cookie、Authorization认证信息)的场景下不会生效,会导致预检不通过,浏览器根本不会发送后续携带Authorization的实际业务请求。另外如果请求开启了withCredentials,Access-Control-Allow-Origin设置为*也会被浏览器直接拦截。
解决方法
- 区分预检请求和实际业务请求:仅在非OPTIONS的实际业务请求中读取
Authorization等业务头,不要在预检请求阶段读取业务参数。 - 不要滥用通配符配置CORS头,明确声明允许的请求头包含
Authorization,如果业务需要携带凭证,要动态匹配允许的请求源,同时开启Access-Control-Allow-Credentials配置。 - 预检请求处理完成后直接返回200状态码结束响应,确保预检通过后浏览器自动发起实际业务请求。
修正后代码
package com.web.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Component @Order(Ordered.HIGHEST_PRECEDENCE) public class CORSFilter implements Filter{ @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; // 动态获取请求源,替换*解决带凭证场景的跨域问题 String origin = request.getHeader("Origin"); response.setHeader("Access-Control-Allow-Origin", origin == null ? "*" : origin); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS"); response.setHeader("Access-Control-Max-Age", "3600"); // 明确声明允许Authorization头,不要用通配符* response.setHeader("Access-Control-Allow-Headers", "Authorization,Content-Type,X-Requested-With"); if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { // 预检请求直接返回200,不需要执行业务逻辑、读取业务头 response.setStatus(HttpServletResponse.SC_OK); return; } // 仅非预检的实际业务请求才走后续链路,在这里读取Authorization头即可拿到值 String header = request.getHeader("Authorization"); chain.doFilter(req, res); } @Override public void init(FilterConfig filterConfig) { } @Override public void destroy() { } }
补充说明:如果业务不需要跨域携带Cookie等凭证,也可以保留
Access-Control-Allow-Origin为*,同时删除Access-Control-Allow-Credentials的配置,但依然需要明确在Access-Control-Allow-Headers中列出Authorization,不能仅用通配符。
内容的提问来源于stack exchange,提问作者Madan Mohan Mishra
相关产品推荐
相关产品推荐

