NULL http headers 从 Angular2 应用程序传递到服务器

NULL http headers are passed to the server from Angular2 app

我有一个使用 SpringBoot 开发的 REST api,它包含用于身份验证和授权的 JWT 实现。 FilterRegistrationBean 用于此。

我有一个名为 JwtFilter 的 class,它扩展了 GenericFilterBean。在那里我寻找请求的内容 headers 以授权用户。

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);
    }

}

我可以保证设置 headers 动态工作正常,因为我测试了它对同一服务器的一些其他 HTTP 请求,这些请求没有被 url 过滤(登录请求)。

但是,当涉及到上面 class 过滤的请求时,我看到每个 header 请求都是空的。

下面给出了我如何将 headers 添加到 Angular2 的请求中。我扩展了 BaserequestOptions class 并覆盖了合并方法,以便动态添加 header。

@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 中检查过滤后的请求时,这些 header 为空。正如我上面提到的,非过滤请求没有问题。静态追加('X-Requested-By')和动态追加('myheader')headers 并且在服务器中可用。

我不明白这里发生了什么。至少,我不知道错误来自哪一侧。

以下是从浏览器控制台为失败请求复制的请求 header。

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: */*

谁能告诉我这里可能是什么问题。我一无所知。

编辑

我猜问题出在服务器上,原因如下。

我能够成功检索到一个请求的所有 header 值,该请求是在设置授权 header 之后完成的,并且该请求不是经过过滤的请求。因此我猜,JWT Filter 实现有问题。

这可能是一个愚蠢的问题,但是,是不是因为我发送的请求不是ServletRequest类型的原因? doFilter JwtFilter 中的方法接受 ServletRequest 的参数。

我想在这里写下我找到的解决方案作为答案,因为它可能会对将来的人有所帮助。感谢 JB Nizet 的评论。他告诉我问题出在哪里。

这是 CORS 的问题。在发送实际的 GET 请求(在我的例子中)之前,浏览器发送了飞行前请求。正如@JB Nizet 指出的那样,我参考以下链接答案更改了代码。

Cross-Origin Resource Sharing with Spring Security

我的 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);

        }


    }

}

编码愉快!

这个问题我已经见过很多次了。这里的关键是Access-Control-Allow-Headershttpheader。 CORS 也是如此,如果未列出您的客户端和服务器尝试交换的 header,这可能会影响您。浏览器中的网络选项卡甚至可能会在响应和请求实体中显示 header,但应用层无法访问它。

您是否正在使用 Spring 启动应用程序服务器?如果是这样,WebSecurity 的配置方式就有些魔力了。通过 WebSecurityConfigurerAdapter 专门扩展和配置 HttpSecurity 将有助于处理所有 HTTP 动词上的 CORS header,如 OPTIONPOSTGET 等.

查看 https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Headers 中的示例,了解 header 的值应该是什么,基本上只是一个 comma-separated header 键列表。