此文讲解在RESTful API中跨域问题在项目中如何处理的!html
CORS 是 Cross Origin Resource Sharing 的缩写, 定义了浏览器和服务器间共享内容的新方式,经过它浏览器和服务器能够安全地进行跨域访问,它是 JSONP 的现代继任 者。服务器上的 CORS 配置能够精细地指定容许跨域访问的条件:来源域、HTTP 方法、请求头、内容类型等等。并 且,CORS 让 XMLHttpRequest 也能够跨域,咱们能够像往常同样编写 AJAX 调用代码。全部现代浏览器都支持 CORS,因此你应该能够放心地使用它,只有在须要兼容老旧浏览器的场合,才用 JSONP作fallback。支持 CORS 的浏览器在尝试进行跨域 XMLHttpRequest 时,会先发出一个“事前检查”,就是一个OPTIONS 请求,其中会包括一些有用的请求头:java
Access-Controll-Request-Headers: accept, content-type Access-Controll-Request-Method: POST
接着服务器会作出响应:node
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 天)不须要再进行这种“事前检查”。json
在以上服务器响应头中,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。安全
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<String> 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
代码以下:cookie
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<ChannelHandler> customHandlers = Lists.newArrayList(new CorsHeadersChannelHandler(),new OPTIONHandler()) ; _netty.setCustomHandlers(customHandlers); _netty.start(); }
第一个app
Handler :OPTIONHandler @Sharable public class OPTIONHandler extends SimpleChannelInboundHandler<NettyHttpRequest> { @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<String> 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<NettyHttpRequest> { @Override protected void channelRead0(ChannelHandlerContext ctx, NettyHttpRequest request) throws Exception { List<String> 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作以下配置可容许资源的跨域访问:
设置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