[后端] java如何解决跨域问题

最近遇到跨域问题,以下为多服务与单服务下的跨域问题解决方案~


一.基于zuul网关解决跨域问题

1.跨域问题描述:
本人在基于Oauth2.0做鉴权/授权时,配合前端联调接口时,已经让前端请求时的请求头带上key:Authorization,但是前台报错为跨域问题,后台报错鉴权失败,于是跟踪错误,发现在网关处从header中一直拿不到这个 Authorization 这个属性的值(因为oauth 2的密码模式是需要请求头中带有token才能够完成鉴权),具体错误如下图所示:
跨域img-1

上图中可以明显看到请求中是存在 Authorization 这个属性的,但是从后台图片看是并没有拿到这个属性的:
跨域img-2

2.问题分析经过:
本人继续向下查找原因,发现通过 HttpRequest的api:getHeaderNames()可以找到请求头中的所有header属性,如下图:
跨域img-3

于是猜想是否有一层转换是将请求头中所有的key属性转化了小写,于是继续尝试 request.getHeader(“authorization”),但是结果还是出乎意料,返回结果也是空,于是开始从头理思路:

从前台开始报错为跨域问题,查看发生错误请求的类型为Preflighted,这让本人感到疑惑,于是查阅资料:
跨域img-4

查阅带预检(Preflighted)的跨域请求跨域原因有如下两种:

1). 除GET、HEAD和POST(only with application/x-www-form-urlencoded, multipart/form-data, text/plain Content-Type)以外的其他HTTP方法。
2). 请求中出现自定义HTTP头部。
以上摘自博客链接

此时豁然开朗,因为基于Oauth2的协议需要在请求中带上 Authorization 及其对应的token,所以自定义了请求头,且请求方不同源,所以浏览器(本次问题使用google浏览器)会发送两次请求:

1). 带预检(Preflighted)的跨域请求,此时请求类型为OPTIONS跨域img-5

2). 带真实数据的请求,此时才是我们真实想要的GET/POST请求跨域img-6

3.验证问题猜想:
如下图,断点处请求类型确实为 OPTIONS
跨域img-7

4.解决方案:
既然已经明确了问题所在,解决方案便随之而出了,由于本人是基于zuul网关做的鉴权/授权,所以解决方案也是基于zuul,仅提供参考:
1). 配置跨域过滤器,优先级最高,生效在路由前,判断如果是带预检的请求,则在响应头中放入允许跨域相关的属性,然后不再向下路由,具体代码如下:

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class CrossFilter extends ZuulFilter {

    /**
     * pre:可以在请求被路由之前调用
     * route:在路由请求时候被调用
     * post:在route和error过滤器之后被调用
     * error:处理请求时发生错误时被调用
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    /**
     * 优先级为0,数字越大,优先级越低
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        //只过滤OPTIONS 请求
        if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
            return true;
        }

        if (!ctx.sendZuulResponse()) {
            return false;
        }
        return false;
    }

    @Override
    public Object run() {
//        logger.info("*****************FirstFilter run start*****************");
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletResponse response = ctx.getResponse();
        HttpServletRequest request = ctx.getRequest();
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "Authorization, content-type, token, myauth, myAuth, userId, userid, RefreshToken");
        response.setHeader("Access-Control-Allow-Methods", "POST,GET");
        response.setHeader("Access-Control-Expose-Headers", "X-forwared-port, X-forwarded-host");
        response.setHeader("Vary", "Origin,Access-Control-Request-Method,Access-Control-Request-Headers");
        //不再路由
        ctx.setSendZuulResponse(false);
        ctx.setResponseStatusCode(200);
        return null;
    }
}

二. 另外附单体服务解决跨域方案
以下需要注意的点为 @Order(Integer.MIN_VALUE) ,这行代码的意思是将此过滤器的作用在所有过滤链最前端,至于原因本人会单独开一篇博客讨论。

import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Configuration
@Order(Integer.MIN_VALUE)
public class CrossFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request1, ServletResponse response1, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) request1;
        HttpServletResponse response = (HttpServletResponse) response1;
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "Authorization, content-type, token, myauth, myAuth,userId , userid,RefreshToken");
        response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS");
        response.setHeader("Access-Control-Expose-Headers", "X-forwared-port, X-forwarded-host,Content-Disposition");
        response.setHeader("Vary", "Origin,Access-Control-Request-Method,Access-Control-Request-Headers");
        if (!request.getMethod().equals(HttpMethod.OPTIONS.name())) {
            chain.doFilter(request, response);
        }

    }

}
Java 后端中,可以通过配置和使用 CORS(跨源资源共享)来解决跨域问题。以下是一些解决跨域问题的常见方法: 1. 使用注解:在需要支持跨域的后端接口方法上使用`@CrossOrigin` 注解。这个注解可以指定允许的域名、请求头、请求方法等。例如: ```java @CrossOrigin(origins = "http://example.com", methods = RequestMethod.GET) @GetMapping("/api/data") public ResponseEntity<?> getData() { // 处理请求 } ``` 2. 配置过滤器:创建一个过滤器,拦截所有请求,并在响应头中添加跨域相关的信息。例如: ```java @Component 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; response.setHeader("Access-Control-Allow-Origin", "http://example.com"); response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); chain.doFilter(req, res); } // 其他方法 } ``` 3. 使用拦截器:创建一个拦截器,在请求到达控制器前添加跨域相关的信息。例如: ```java @Component public class CorsInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { response.setHeader("Access-Control-Allow-Origin", "http://example.com"); response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); return true; } // 其他方法 } ``` 在以上的示例中,将`http://example.com`替换为你允许的域名。这样配置后,前端的请求就可以跨域访问后端接口了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值