跟踪如何解决CORS问题

Cec*_*uez 5 java ninjaframework

我正在处理的应用程序出现了CORS问题。

It's setup in Kubernetes, with a third party Java framework:

http://www.ninjaframework.org/

I am getting the following error:

Preflight response is not successful
XMLHttpRequest cannot load https://api.domain.com/api/v1/url/goes/here? due to access control checks.
Failed to load resource: Preflight response is not successful
Run Code Online (Sandbox Code Playgroud)

I don't think the problem is in Kubernetes, but just in case - here's my Kubernetes setup:

apiVersion: v1
kind: Service
metadata:
  name: domain-server
  annotations:
    dns.alpha.kubernetes.io/external: "api.domain.com"
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-east-2:152660121739:certificate/8efe41c4-9a53-4cf6-b056-5279df82bc5e
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
spec:
  type: LoadBalancer
  selector:
    app: domain-server
  ports:
    - port: 443
      targetPort: 8080
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: domain-server
spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 3
  revisionHistoryLimit: 10
  template:
    metadata:
      labels:
        app: domain-server
    spec:
      containers:
        - name: domain-server
          image: "location.aws.etc"
          imagePullPolicy: Always
    ...
Run Code Online (Sandbox Code Playgroud)

I am totally lost here - how do I enable CORS on my api endpoints? I'm sorry if this is a simple question or I haven't provided enough information here, but I have no clue how to do this and I've tried several pathways.

Note, just to be clear, api.domain.com is a replacement for my actual api domain, I just don't want to reveal what site I am working on

EDIT:

My guess is that it might have something to do with this:

private Result filterProtectedApi(FilterChain chain, Context context, boolean isMerchant, JwtAuthorizer jwtAuthorizer) {
    String authHeader = context.getHeader("Authorization");
    if (authHeader == null || !authHeader.startsWith("Bearer ")) {
        return this.forbiddenApi();
    }
    context.setAttribute("access-token", authHeader.substring("Bearer ".length()));
    return this.filterProtected(chain, context, isMerchant, jwtAuthorizer, parser -> parser.parseAuthHeader(authHeader), this::forbiddenResource);
}

private AuthLevel getAuthLevel(String requestPath) {
    log.info("REQUEST PATH: " + requestPath);
    if (requestPath.equals("/auth") || requestPath.equals("/auth/merchant") || requestPath.equals("/auth/app")
            || requestPath.startsWith("/assets/") || requestPath.equals("/privacy-policy.html")
            || requestPath.equals("/forbidden.html") || requestPath.equals("/favicon.ico")
            || requestPath.startsWith("/invite/ios/") || requestPath.startsWith("/stripe/")
            || requestPath.startsWith("/chat")) {
        return AuthLevel.UNPROTECTED_RESOURCE;
    }
    if (requestPath.startsWith("/merchant/api/")) {
        return AuthLevel.PROTECTED_MERCHANT_API;
    }
    if (requestPath.startsWith("/merchant/")) {
        return AuthLevel.PROTECTED_MERCHANT_RESOURCE;
    }
    if (requestPath.startsWith("/api/")) {
        return AuthLevel.PROTECTED_API;
    }
    return AuthLevel.PROTECTED_RESOURCE;
}
Run Code Online (Sandbox Code Playgroud)

I have tried adding something to ignore OPTIONS requests, but I still get failed the preflight check

private Result filterProtectedApi(FilterChain chain, Context context, boolean isMerchant,
        JwtAuthorizer jwtAuthorizer) {
    if (context.getMethod().toLowerCase().equals("options")) {
        return chain.next(context);
    }
    String authHeader = context.getHeader("Authorization");
    if (authHeader == null || !authHeader.startsWith("Bearer ")) {
        return this.forbiddenApi();
    }
    context.setAttribute("access-token", authHeader.substring("Bearer ".length()));
    return this.filterProtected(chain, context, isMerchant, jwtAuthorizer,
            parser -> parser.parseAuthHeader(authHeader), this::forbiddenResource);
}
Run Code Online (Sandbox Code Playgroud)

What do I need to do to have the preflight check succeed?

编辑-根据以下建议将其更改为此:

@Override
public Result filter(FilterChain chain, Context context) {
    if (context.getMethod().toLowerCase().equals("options")) {
        return Results.html().addHeader("Access-Control-Allow-Origin", "*")
                .addHeader("Access-Control-Allow-Headers", "Authorization")
                .addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS").render("OK");
    }
    AuthLevel authLevel = this.getAuthLevel(context.getRequestPath());
    switch (authLevel) {
    case PROTECTED_API: {
        return this.filterProtectedApi(chain, context, false, this.jwtAuthorizer);
    }
    case PROTECTED_MERCHANT_RESOURCE: {
        return this.filterProtectedResource(chain, context, "merchant-access-token", "/auth/merchant", true,
                this.merchantJwtAuthorizer);
    }
    case PROTECTED_MERCHANT_API: {
        return this.filterProtectedApi(chain, context, true, this.merchantJwtAuthorizer);
    }
    case UNPROTECTED_RESOURCE: {
        return this.filterUnprotectedResource(chain, context);
    }
    }
    return this.filterProtectedResource(chain, context, "access-token", "/auth", false, this.jwtAuthorizer);
}
Run Code Online (Sandbox Code Playgroud)

Jan*_*tis 5

您走在正确的道路上,尝试在身份验证验证之前忽略 OPTIONS 请求:

if (context.getMethod().toLowerCase().equals("options")) {
    return chain.next(context);
}
Run Code Online (Sandbox Code Playgroud)

此外还需要正确响应预检请求

if (context.getMethod().toLowerCase().equals("options")) {
    return Results.html()
                  .addHeader("Access-Control-Allow-Origin", "*")
                  .addHeader("Access-Control-Allow-Headers", "Authorization")
                  .addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE")
                  .render("OK");
}
Run Code Online (Sandbox Code Playgroud)

简而言之,您需要回应

  • 适当的http状态代码,通常为200或204
  • 添加所需的 http 响应标头
    • “Access-Control-Allow-Origin”带有“*”以允许来自所有域的 CORS 或http://www.domainA.com仅允许来自特定域
    • "Access-Control-Allow-Headers",允许 http 标头
    • "Access-Control-Allow-Methods",允许 http 方法
  • 响应正文无关紧要,您只需发送“OK”即可。

请注意,预检请求可以从任何路由完成,因此我建议使用上面的代码创建一个新的过滤器,并在任何其他路由之前将其用于所有路由。

所以你在实现filter()方法后使用它:

public Result filter(FilterChain chain, Context context) {
     if (context.getMethod().toLowerCase().equals("options")) {
          return Results.html()
                  .addHeader("Access-Control-Allow-Origin", "*")
                  .addHeader("Access-Control-Allow-Headers", "Authorization")
                  .addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE")
                  .render("OK");
     }
Run Code Online (Sandbox Code Playgroud)

Kubernetes Ingress Nginx 上的 CORS

尝试在注释配置中启用 CORS :

annotations:
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, OPTIONS"
    nginx.ingress.kubernetes.io/cors-allow-origin: "http://localhost:8100"
    nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
    nginx.ingress.kubernetes.io/cors-allow-headers: "authorization"
Run Code Online (Sandbox Code Playgroud)

请注意,字符串“*”不能用于支持凭据的资源 ( https://www.w3.org/TR/cors/#resource-requests ),请尝试使用您的域列表(逗号分隔)而不是 *

参考: