资讯专栏INFORMATION COLUMN

RESTful API中跨域问题

荆兆峰 / 2921人阅读

摘要:这个过程是浏览器会发一次请求,询问服务器是否允许代码如下完了以后,客户端就可以顺利请求服务器接口了。

此文讲解在RESTful API中跨域问题在项目中如何处理的!

CORS 是 Cross Origin Resource Sharing 的缩写, 定义了浏览器和服务器间共享内容的新方式,通过它浏览器和服务器可以安全地进行跨域访问,它是 JSONP 的现代继任 者。服务器上的 CORS 配置可以精细地指定允许跨域访问的条件:来源域、HTTP 方法、请求头、内容类型等等。并 且,CORS 让 XMLHttpRequest 也可以跨域,我们可以像往常一样编写 AJAX 调用代码。所有现代浏览器都支持 CORS,所以你应该可以放心地使用它,只有在需要兼容老旧浏览器的场合,才用 JSONP做fallback。支持 CORS 的浏览器在尝试进行跨域 XMLHttpRequest 时,会先发出一个“事前检查”,就是一个OPTIONS 请求,其中会包括一些有用的请求头:

Access-Controll-Request-Headers: accept, content-type
Access-Controll-Request-Method: POST

接着服务器会做出响应:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-Requested-With,     
Content-Type, Accept
Access-Control-Max-Age: 1728000

最后浏览器会根据服务器的响应头,判断请求是否在服务器规定的范围内。比如来源是否在 Access-Control-Allow- Origin 里,HTTP 方法是不是在 Access-Control-Allow-Methods 里面,有没有不在 Allow-Headers 里面的请求头。如果以上条件都符合,那么浏览器就会放行这次请求,并且在Access-Control-Max-Age 指定的时间内(单位是秒,以上设置的是 20 天)不需要再进行这种“事前检查”。

在以上服务器响应头中,Access-Control-Allow-Headers 不可以使用通配符。所以如果你要允许所有请求头,不妨把浏览器发来的 Access-Control-Request-Header 直接返回。

事实上,如果跨域请求是“简单请求”,也就是 HTTP 方法为 GET、HEAD、POST,请求体的 MIME Type 是以下其中一种:application/x-www-form-urlencoded、multipart/form-data 或者text/plain,并且没有自定义的请求头。这时浏览器只根据请求头中的 Origin 和服务器返回的Access-Control-Allow-Origin 就可以判断了。但我们是 RESTful API,请求体是application/json,所以只能用上面那种“事前检查”的方式。另外,利用 CORS 还可以在跨域请求中发送 Cookie,这个特性是很有用的。只需要为 XMLHttpRequest 对象设置 withCredentials 属性:

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
java在resteasy中设置 response.getOutputHeaders().putSingle("Access-Control-Allow-Credentials",true);

但这种情况下,就不能指定 Access-Control-Allow-Origin: *,而是必须指定一个来源,比如http://mydomain.com。

java 操作:

resteasy 设置如下
先提供options请求,告诉客户端允许客户端可以带什么样的头信息过来。比如:
客户要封装一个复杂json数据来请求服务器,这时服务器需要需要允许客户端头信息中的content-type 为application/json。这个过程是浏览器会发一次options请求,询问v服务器是否允许
代码如下:

    @OPTIONS
    @Path(value = "creatorunion/works")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response uploadWorks(@Context HttpRequest request,
            @Context HttpResponse response) {
        HttpHeaders header = request.getHttpHeaders();
        List requestHeader = header.getRequestHeader("Origin");
        if(CollectionUtils.isNotEmpty(requestHeader)){
            String host = requestHeader.get(0);
            response.getOutputHeaders().putSingle("Access-Control-Allow-Origin",host);
        }
        response.getOutputHeaders().putSingle("Access-Control-Allow-Headers","X-Requested-With, accept, origin, content-type");
        response.getOutputHeaders().putSingle("Content-Type","application/json;charset=utf-8");
        response.getOutputHeaders().putSingle("Access-Control-Allow-Credentials",true);
        response.getOutputHeaders().putSingle("Access-Control-Allow-Methods","GET,POST,PUT,DELETE,OPTIONS");
        return Response.status(200).entity("").build();
    }

完了以后,客户端就可以顺利请求服务器(接口:creatorunion/works)了。
但这样写有个问题,每个接口都需要去设置,由于在项目中使用了netty作为容器需要在netty容器里添加自定义handlers来统一处理resteasy &netty的渊源请参考:
http://docs.jboss.org/resteasy/docs/3.0.17.Final/userguide/html_single/index.html#d4e1485
代码如下:

private void start(ResteasyDeployment deployment,SecurityDomain domain) throws Exception {
        _netty = new MyNettyJaxrsServer();
        _netty.setDeployment(deployment);
        _netty.setPort(_port);
        _netty.setRootResourcePath(_serverIP);
        _netty.setSecurityDomain(domain);
        //添加自定义handler
        List customHandlers = Lists.newArrayList(new CorsHeadersChannelHandler(),new OPTIONHandler()) ;
        _netty.setCustomHandlers(customHandlers);

        _netty.start();
}

第一个

    Handler :OPTIONHandler
    @Sharable
    public class OPTIONHandler extends SimpleChannelInboundHandler {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, NettyHttpRequest request) throws Exception {
                if("OPTIONS".equals(request.getHttpMethod().toUpperCase())){
                        NettyHttpResponse response = request.getResponse();
                        response.reset();
            response.setStatus(200);
            List requestHeader = request.getMutableHeaders().get("Origin");
                String host = "";
                if(requestHeader!=null && !requestHeader.isEmpty()){
                        host = requestHeader.get(0);
                }
            response.getOutputHeaders().add("Access-Control-Allow-Origin", host);
            response.getOutputHeaders().add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
            response.getOutputHeaders().putSingle("Access-Control-Allow-Credentials",true);//允许带cookie访问
            response.getOutputHeaders().add("Access-Control-Allow-Headers",
                                "X-Requested-With, Content-Type, Content-Length");
            if (!request.getAsyncContext().isSuspended()) {
                response.finish();
             }
                }
                ctx.fireChannelRead(request);
        }
}

第二个

    Handler :CorsHeadersChannelHandler
    @Sharable
    public class CorsHeadersChannelHandler extends SimpleChannelInboundHandler {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, NettyHttpRequest request) throws Exception {
                List requestHeader = request.getMutableHeaders().get("Origin");
                String host = "";
                if(requestHeader!=null && !requestHeader.isEmpty()){
                        host = requestHeader.get(0);
                }
                request.getResponse().getOutputHeaders().add("Access-Control-Allow-Origin", host);
                request.getResponse().getOutputHeaders().add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
                request.getResponse().getOutputHeaders().add("Access-Control-Allow-Credentials",true);//允许带cookie访问
                request.getResponse().getOutputHeaders().add("Access-Control-Allow-Headers",
                                "X-Requested-With, Content-Type, Content-Length");
                ctx.fireChannelRead(request);
        }
}
nodejs操作:

对于nodejs做如下配置可允许资源的跨域访问:
设置CORS跨域访问

app.all("*", function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With, accept, origin, content-type");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By", " 3.2.1")
res.header("Content-Type", "application/json;charset=utf-8");
next();
});

补充:
参考https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66026.html

相关文章

  • js跨域的方法

    摘要:但是这种方法适用于和窗口,和无法通过这种方法规避同源策略。逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。 在制作oneday-music-player的时候要使用ajax向百度音乐的api发送请求,然后出现了XMLHttpRequest cannot load http://.... . No Access-Control-Allow-Origin header is pr...

    Jacendfeng 评论0 收藏0
  • JSONP的一点笔记<修改中>

    摘要:请求服务器数据并规定回调函数为上面代码通过动态添加元素,向服务器发出请求。另外假设向服务发送的请求是这样的在这种情况下,是表示请求的请求参数,而是应用程序的回调函数的名称。清单调用回调服务注意,我们使用作为回调函数名,而非真实的函数名。 同源策略 同源策略(Same origin policy),它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript的浏览器都...

    姘存按 评论0 收藏0
  • 跨域问题导致设置 cookie 不生效

    摘要:我们看下跨域不生效的问题,首先抛出两个问题我们如何设置又如何确定设置是否生效了首先,我们实现一个简单的接口新建一个文件,将如下代码复制进去,通过启动服务,在本地就可以通过来访问了我们创建的接口了环境安装的教程网上有很多详细的教程,本文不再赘 我们看下跨域不生效的问题,首先抛出两个问题: 我们如何设置 cookie ? 又如何确定 cookie 设置是否生效了 ? 首先,我们实现一个...

    Taste 评论0 收藏0
  • SpringMVC+RestFul详细示例实战教程(实现跨域访问)

    摘要:方法和对应的方法定义定义重启服务器,运行上面的程序。五添加支持实现跨域访问当访问时,你可能需要面对同源策略问题。错误如下一般来说,在服务器端,我们在响应中返回额外的访问控制头,实现跨域链接。 一、理解 REST REST(Representational State Transfer),中文翻译叫表述性状态转移。是 Roy Thomas Fielding 在他2000年的博士论文中提出...

    U2FsdGVkX1x 评论0 收藏0

发表评论

0条评论

荆兆峰

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<