RESTful API中跨域問題

此文講解在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安全

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<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操做:

對於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

相關文章
相關標籤/搜索