NULL http 标头从 Angular2 应用程序传递到服务器
Posted
技术标签:
【中文标题】NULL http 标头从 Angular2 应用程序传递到服务器【英文标题】:NULL http headers are passed to the server from Angular2 app 【发布时间】:2017-10-12 20:18:28 【问题描述】:我有一个使用 SpringBoot 开发的 REST api,它包含 JWT 用于身份验证和授权的实现。 FilterRegistrationBean
用于此目的。
我有一个名为JwtFilter
的类,它扩展了GenericFilterBean
。在那里,我查找请求标头的内容以授权用户。
public class JwtFilter extends GenericFilterBean
@Override
public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException
System.out.println("INSIDE JWT FILTER");
final HttpServletRequest request = (HttpServletRequest) req;
final String authHeader = request.getHeader("Authorization");
String reqHdr = request.getHeader("X-Requested-By");
String myHdr = request.getHeader("myheader");
System.out.println("Auth header = "+authHeader);
System.out.println("requested by header = "+reqHdr);
System.out.println("my header = "+myHdr);
if (authHeader == null || !authHeader.startsWith("Bearer "))
System.out.println("JwtFilter.doFilter -> auth header is null or token does not start with Bearer");
throw new ServletException("Missing or invalid Authorization header.");
final String token = authHeader.substring(7); // The part after "Bearer "
try
final Claims claims = Jwts.parser().setSigningKey("secretkey").parseClaimsJws(token).getBody();
request.setAttribute("claims", claims);
catch (final SignatureException e)
System.out.println("VIRGLK EXCEPTION : JwtFilter.doFilter -> Signature Exception, invalid token = "+e.getMessage());
throw new ServletException("Invalid token.");
chain.doFilter(req, res);
我可以确保动态设置标头可以正常工作,因为我测试了对同一服务器的一些其他 HTTP 请求,这些请求没有经过 url 过滤(登录请求)。
但是,当涉及到由上述类过滤的请求时,我看到请求的每个标头都是空的。
下面是我如何在 Angular2 的请求中添加标头。我扩展了BaserequestOptions
类并重写了merge 方法以动态添加标题。
@Injectable()
export class CustomRequestOptions extends BaseRequestOptions
constructor(private _globals: Globals)
super();
this.headers.set('Content-Type', 'application/json');
this.headers.set('X-Requested-By', 'Angular 2');
merge(options?: RequestOptionsArgs): RequestOptions
var newOptions = super.merge(options);
let hdr = this._globals.getAuthorization();
newOptions.headers.set("Authorization", hdr);
newOptions.headers.set("myheader", "my header value");
return newOptions;
但在 API 中检查过滤后的请求时,这些标头为空。正如我上面提到的,未过滤的请求没有问题。静态附加('X-Requested-By')和动态附加('myheader')标头都可以在服务器中使用。
我无法理解这里发生了什么。至少,我不知道错误来自哪一侧。
以下是从浏览器控制台复制的失败请求的请求标头。
OPTIONS /protected/get-roles/USER HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Access-Control-Request-Headers: authorization,content-type,myheader,x-requested-by
Accept: */*
有人可以指出这里可能是什么问题。我一无所知。
编辑
我猜这个问题来自服务器,原因如下。
我能够成功检索在设置授权标头后完成的请求的所有标头值,并且该请求不是经过过滤的请求。因此我猜想,JWT Filter 的实现有问题。
这可能是一个愚蠢的问题,但是,是不是因为我发送的请求不是ServletRequest
的类型的原因? JwtFilter
中的doFilter
方法接受ServletRequest
的参数。
【问题讨论】:
此请求是一个 OPTIONS 请求,很可能是浏览器在实际 GET/POST/PUT/DELETE 请求之前发送的飞行前请求,因为您使用的是 CORS。您应该忽略过滤器中的 OPTIONS 请求。 @JBNizet 非常感谢您的指导。它解决了我的问题。 :) 一些参考资料——***.com/questions/18264334/…***.com/a/43409061/3892439 【参考方案1】:我之前已经多次看到这个问题。这里的关键是Access-Control-Allow-Headers
http 标头。 CORS 也是如此,如果您的客户端和服务器尝试交换的标头未列出,这可能会咬您一口。浏览器中的网络选项卡甚至可以显示响应和请求实体中的标头,但是应用层无法访问它。
你在使用 Spring Boot 作为应用服务器吗?如果是这样,那么 WebSecurity 的配置方式就有了魔力。通过WebSecurityConfigurerAdapter
专门扩展和配置HttpSecurity
将有助于处理所有HTTP 动词(如OPTION
、POST
、GET
等)上的CORS 标头。
查看https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Headers 以获取有关标头值应该是什么的示例,基本上只是一个逗号分隔的标头键列表。
【讨论】:
是的,谢谢您的回答。我以某种方式想通了并通过配置添加了修复【参考方案2】:我想在这里写下我找到的解决方案作为答案,因为它将来可能会对某人有所帮助。感谢 JB Nizet 的评论。他告诉我问题出在哪里。
这是 CORS 的一个问题。在发送实际 GET 请求(在我的情况下)之前,浏览器发送了飞行前请求。正如@JB Nizet 指出的那样,我更改了我的代码,引用了以下链接的答案。
Cross-Origin Resource Sharing with Spring Security
Header in the response must not be the wildcard '*' when the request's credentials mode is 'include'
我的JwtFilter
现在看起来像这样
public class JwtFilter extends GenericFilterBean
private final List<String> allowedOrigins = Arrays.asList("http://localhost:3000");
@Override
public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException
System.out.println("INSIDE JWT FILTER");
final HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// Access-Control-Allow-Origin
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", allowedOrigins.contains(origin) ? origin : "");
response.setHeader("Vary", "Origin");
// Access-Control-Max-Age
response.setHeader("Access-Control-Max-Age", "3600");
// Access-Control-Allow-Credentials
response.setHeader("Access-Control-Allow-Credentials", "true");
// Access-Control-Allow-Methods
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
// Access-Control-Allow-Headers
response.setHeader("Access-Control-Allow-Headers",
"Origin, Authorization, myheader, X-Requested-By, X-Requested-With, Content-Type, Accept, " + "X-CSRF-TOKEN");
if (request.getMethod().equals("OPTIONS"))
response.flushBuffer();
else
final String authHeader = request.getHeader("Authorization");
String reqHdr = request.getHeader("X-Requested-By");
String myHdr = request.getHeader("myheader");
System.out.println("=====================================================");
System.out.println("Auth header = "+authHeader);
System.out.println("requested by header = "+reqHdr);
System.out.println("my header = "+myHdr);
System.out.println("=====================================================");
if (authHeader == null || !authHeader.startsWith("Bearer "))
System.out.println("JwtFilter.doFilter -> auth header is null or token does not start with Bearer");
throw new ServletException("Missing or invalid Authorization header.");
final String token = authHeader.substring(7); // The part after "Bearer "
try
final Claims claims = Jwts.parser().setSigningKey("secretkey").parseClaimsJws(token).getBody();
request.setAttribute("claims", claims);
catch (final SignatureException e)
System.out.println("JwtFilter.doFilter -> Signature Exception, invalid token = "+e.getMessage());
throw new ServletException("Invalid token.");
chain.doFilter(req, res);
编码愉快!
【讨论】:
以上是关于NULL http 标头从 Angular2 应用程序传递到服务器的主要内容,如果未能解决你的问题,请参考以下文章
Angular2 - HTTP RequestOptions 标头
在 Angular2 HTTP POST 中设置 JSON 请求标头
Angular2:http.get 返回“请求的资源上不存在‘Access-Control-Allow-Origin’标头。”