CORS article understand cross-domain (the front end + examples to explain the back-end code)

Frequently asked questions, such as writing Java server-side students to ask: I obviously correct server returned, the test environment debug can see why the front end was unable to get the data? Then write front-end students will ask: Why I obviously set withCredentials = true, the server or the students get cookie?

So we decided to re-use CORS stroked a stroke solve the problem of cross-domain, the front and rear ends how to do? Why do this?

Why is there the problem of cross-domain?

To ensure the security of user information, all browsers follow the same origin policy, then under what circumstances considered homologous to it? What same-origin policy is it?

Remember: the protocol, when the exact same domain name, port number, is homologous

Can refer to  Web Security - browser's same-origin policy

In the same origin policy, have the following restrictions:

  • Unable to get non-homologous Cookie, LocalStorage, SessionStorage etc.
  • Unable to get non-homologous dom
  • Ajax request could not be sent to non-homologous server

But we often encounter before and after the end of the separation, not under the same domain name, the situation needs to ajax request data. Then we have to circumvent this limitation.

Can be found online are many ways to solve cross-domain, some method is relatively old, with a relatively large number of projects are now jsonp and CORS (cross-domain resource sharing), this is mainly about the principles and practice of CORS.

CORS cross-domain principle

CORS is actually the principle of cross-domain browser and server via HTTP protocol header to do some number of conventions and restrictions. You can view HTTP- access control (CORS)

Protocol headers associated with cross-domain

Request header Explanation
Origin Preflight request URI indicates that the source station, or actually requested, regardless of whether a cross-domain field is always transmitted ORIGIN
Access-Control-Request-Method The actual request tells the server using HTTP method
Access-Control-Request-Headers The actual request header field carried by telling the server

 

Response header Explanation
Access-Control-Allow-Origin Designated to allow access to the resource domain URI, for carrying the credential request can not use the wildcard *
Access-Control-Expose-Headers The specified XMLHttpRequest can access response header getResponseHeader
Access-Control-Max-Age Specify the preflight result of the request can be cached how long
Access-Control-Allow-Credentials Whether to allow the browser to read the contents of the response;
when used in response preflight preflight request, the request may specify whether actual use credentials
Access-Control-Allow-Methods The method of HTTP request using the specified actual allowed
Access-Control-Allow-Headers Allowing the request indicating the actual header field carries

 

Code examples

Here wrote a demo, a step by step analysis. Contents are as follows:

.
├── README.md
├── client
│   ├── index.html
│   └── request.js
└── server
    ├── pom.xml
    ├── server-web
    │   ├── pom.xml
    │   ├── server-web.iml
    │   └── src
    │       └── main
    │           ├── java
    │           │   └── com
    │           │       └── example
    │           │           └── cors
    │           │               ├── constant
    │           │               │   └── Constants.java
    │           │               ├── controller
    │           │               │   └── CorsController.java
    │           │               └── filter
    │           │                   └── CrossDomainFilter.java
    │           ├── resources
    │           │   └── config
    │           │       └── applicationContext-core.xml
    │           └── webapp
    │               ├── WEB-INF
    │               │   ├── dispatcher-servlet.xml
    │               │   └── web.xml
    │               └── index.jsp
    └── server.iml
复制代码

 

  • Client: a front end, a simple request ajax

In the client folder, start static server, the front page by http://localhost:8000/index.htmlaccessing:

anywhere -h localhost -p 8000
复制代码

 

  • Server: java project, SpringMVC

IntelliJ IDEA the local start tomcat, provided Host:  http://localhost:8080/, by the server data http://localhost:8080/server/corsrequest.

Here the front and rear because the port number is different , cross-domain restrictions, as the following to solve the problem by not cross-domain ajax request data by CORS.

 

The situation did not allow cross-domain

This situation is not doing what the front end, the server also do nothing.

Client: After the request is successful, the data will be displayed on the page

new Request().send('http://localhost:8080/server/cors',{
	success: function(data){
		document.write(data)
	}
});
复制代码

Server:

@Controller
@RequestMapping("/server")
public class CorsController {
    @RequestMapping(value="/cors", method= RequestMethod.GET)
    @ResponseBody
    public String ajaxCors(HttpServletRequest request) throws Exception{
        return "SUCCESS";
    }
}
复制代码

In the browser address bar http://localhost:8080/server/corsdirectly request the service side, you can see the returned results: 'SUCCESS'

 

 

 

In the browser address bar enter http://localhost:8000/index.html, send ajax request to Server page from a different domain. We can see several aspects:

It can be seen from the network, a request to return to normal.

 

 

 

Response but no content to display  Failed to load response data.

 

 

 

And the console error:

 

 

 

to sum up:

1, browser requests are sent out, the server will return the correct, but we can not get the content of the response

2, the browser may prompt the console will get an error how to do it, and prompted it very clear: xhr not request http://localhost:8080/server/cors, do not set Access-Control-Allow-Origin, Origin of resources in response to the first request: http://localhost:8000is not allowed to cross-domain requests.

When's the next step, we need to respond to cross-domain requests on the server, set the response header: Access-Control-Allow-Origin

Set Access-Control-Allow-Origin allowed Cross

First explain why you want to set Access-Control-Allow-Origin, can the Access-Control-Allow-Origin as a command server set up Access-Control-Allow-Origin tells the browser allows requests to the server domain resources, the browser can know whether or not the data spit out by Access-Control-Allow-Origin Response in.

The official explanation is this: Access-Control-Allow-Origin header specifies the response resources of the response is allowed to be shared with a given origin.

Value may be set to Access-Control-Allow-Origin are:

Access-Control-Allow-Origin: *
Access-Control-Allow-Origin:
复制代码

That the java server to the response header Access-Control-Allow-Origin can do this:

1, a filter was added

public class CrossDomainFilter implements Filter{
    public void init(FilterConfig filterConfig) throws ServletException {}
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse resp = (HttpServletResponse)servletResponse;
        resp.setHeader("Access-Control-Allow-Origin", "http://localhost:8000");
        filterChain.doFilter(servletRequest,servletResponse);
    }
    public void destroy() {}
}
复制代码

2, then add in the filter configuration file web.xml:

crossDomainFilter
    com.example.cors.filter.CrossDomainFilter
    crossDomainFilter
    /*
复制代码

3, and then restart the tomcat, client resend requesthttp://localhost:8000/index.html

 

 

 

 

 

 

You can see that we can get the results returned, the response header there Access-Control-Allow-Origin we set in the service side: http://localhost:8000this should be consistent with the request header of origin, or set up Access-Control-Allow-Origin: * also possible, which allows any website to access resources (if this was without credentials, behind this talk)

The above is a simple way to allow cross-domain request, the server only needs to set the response header Access-Control-Allow-Origin.

A simple request and request preflight

The above describes a simple request by providing the response header Access-Control-Allow-Origin can be completed at the end of the cross-domain service request.

That kind of request can be considered a simple request? And what kind of a simple request corresponding to the request is it? The way to solve cross-domain what is the difference?

Meets the following criteria as a simple request:

1, use one of the following HTTP methods

- GET
- HEAD
- POST,并且Content-Type的值在下列之一:
  - text/plain
  - multipart/form-data
  - application/x-www-form-urlencoded
复制代码

2, only the request header and the following

- Accept
- Accept-Language
- Content-Language
- Content-Type (需要注意额外的限制)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
复制代码

Does not meet the above requirements must first send a request before sending a formal request preflight, preflight OPTIONS method to request transmission, and the method by requesting browser request header can be determined whether to transmit the preflight request.

Client request to send the following example:

new Request().send('http://localhost:8080/server/options',{
	method: 'POST',
	header: {
		'Content-Type': 'application/json'  //告诉服务器实际发送的数据类型
	},
	success: function(data){
		document.write(data)
	}
});
复制代码

Server side processing request controller:

@Controller
@RequestMapping("/server")
public class CorsController {
    @RequestMapping(value="/options", method= RequestMethod.POST)
    @ResponseBody
    public String options(HttpServletRequest request) throws Exception{
        return "SUCCESS";
    }
}
复制代码

Because the request, the request header stuffed header, 'Content-Type': 'application / json'. The described earlier can be known, the browser will send a request to a preflight OPTIONS method, will be added to the browser in the request header:

Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
复制代码

The role of this preflight request here is to tell the server: request headers I will ask later in the POST method to send data type is application / json request, ask the server whether to allow.

 

 

 

Here have not done any server settings allow this request, so the browser console error:

 

 

 

Also clearly illustrates the cause of the error:  the server tells the browser does not request a response preflight protocol header allows the Type-Content , i.e., the server needs to set the response header Access-Control-Allow-Headers, allows the browser to send with Content -Type request.

Add Access-Control-Allow-Headers end of the filter in the Server:

public class CrossDomainFilter implements Filter{
    public void init(FilterConfig filterConfig) throws ServletException {}
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse resp = (HttpServletResponse)servletResponse;
        resp.setHeader("Access-Control-Allow-Origin", "http://localhost:8000");
        resp.setHeader("Access-Control-Allow-Headers", "Content-Type");
        filterChain.doFilter(servletRequest,servletResponse);
    }
    public void destroy() {}
}
复制代码

You can see the request was successful

 

 

 

Look specific information request, first transmits a request to preflight OPTIONS method, browser settings request header:

Access-Control-Request-Headers:content-type //请求中加入的请求头
Access-Control-Request-Method:POST  //跨域请求的方法
复制代码

End of the service setting response header:

Access-Control-Allow-Headers:Content-Type   //允许的header
Access-Control-Allow-Origin:http://localhost:8000 //允许跨域的源
复制代码

 

 

 

You may be provided Access-Control-Allow-Methods to restrict a client's request method.

Such a preflight request is successful, the browser will issue a second request, this request is true of the requested data:

 

 

 

See successful POST request, the second request header is not provided Access-Control-Request-Headers and Access-Control-Request-Method.

But there is a problem , if necessary preflight request, the browser will issue two requests, once OPTIONS, once POST. Both times returned data. If this server complex logic, such as database lookups to data from the web layer, service logic to this database will go twice, the browser will get the same data twice, so the service side of the filter can change it if OPTIONS request is not walk logic behind the cross-domain request after setting response header returned directly.

public class CrossDomainFilter implements Filter{
    public void init(FilterConfig filterConfig) throws ServletException {}
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse resp = (HttpServletResponse)servletResponse;
        resp.setHeader("Access-Control-Allow-Origin", "http://localhost:8000");
        resp.setHeader("Access-Control-Allow-Headers", "Content-Type");   
        //OPTION请求就直接返回
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        if (req.getMethod().equals("OPTIONS")) {
            resp.setStatus(200);
            resp.flushBuffer();
        }else {
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }
    public void destroy() {}
}
复制代码

to sum up:

1, is provided in response to the POST request for the Content-Type header to some value, custom request class, the browser will first send a request to a preflight OPTIONS method, and set the appropriate request header.

2, or the server returns normally, but does not set the appropriate response header if the preflight request response header, the request is not passed preflight, will not issue a second request to obtain the data.

3, the server set the appropriate response header, the browser will send a second request, the data returned from the server and discharge, we can obtain the content of the response

With credential information request

There is a situation we often encounter. Browser cookie needs to be sent to the server when sending the request, the server to do some identity verification based on the information in the cookie.

By default, the browser sends ajax request different domains, it will not carry sends cookie information.

Client:

var containerElem = document.getElementById('container')
new Request().send('http://localhost:8080/server/testCookie',{
	success: function(data){
		containerElem.innerHTML = data
	}
});
复制代码

Server:

@RequestMapping(value="/testCookie", method= RequestMethod.GET)
@ResponseBody
public String testCookie(HttpServletRequest request,HttpServletResponse response) throws Exception{
    String str = "SUCCESS";
    Cookie[] cookies = request.getCookies();
    String school = getSchool(cookies);
    if(school == null || school.length() == 0){
        addCookie(response);
    }
    return str + buildText(cookies);
}
复制代码

The server receives the request, determine the cookie has no school, no to add cookie.

 

 

 

There you can see the response Set-Cookie header, when requested again, if it is homologous to the request, the value of the browser will be placed in Set-Cookie request header, but for cross-domain request, the default is not to send this Cookie's .

If you want the browser sends the cookie, you need to set withCredentials property of XMLHttpRequest in Client is true.

Client:

var containerElem = document.getElementById('container')
new Request().send('http://localhost:8080/server/testCookie',{
	withCredentials: true,
	success: function(data){
		containerElem.innerHTML = data
	}
});
复制代码

Now added to the browser cookie information in the request header

 

 

 

But the data returned from the server does not show in the page, and error:

 

 

 

Error message is very clear:  when the credential request includes the information required to set the response header Access-Control-Allow-Credentials, whether with the credential information is controlled by the property of XMLHttpRequest withCredentials.
**
So we join the Server response header at the end of the filter:

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse resp = (HttpServletResponse)servletResponse;
        resp.setHeader("Access-Control-Allow-Origin", "http://localhost:8000");
        resp.setHeader("Access-Control-Allow-Credentials","true");
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        if (req.getMethod().equals("OPTIONS")) {
            resp.setStatus(200);
            resp.flushBuffer();
        }else {
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }
复制代码

Now the browser knows the response header Access-Control-Allow-Credentials is true, will put the data to spit it out, we can get the content from the response of the.

 

 

 

 

 

 

If the information that came with credentials and have a preflight request it? If there is a preflight request with credentials (XMLHttpRequest is withCredentials set to true), the server needs to set up Access-Control-Allow-Credentials: true, otherwise the browser will not issue a second request with an error.

 

 

 

to sum up:

1, when the cross-domain requests, the browser does not send the default cookie, you need to set withCredentials property of XMLHttpRequest is true

2, browser settings withCredentials property of XMLHttpRequest is true, it indicates that the server would like to send credentials (here is a cookie). Then the server will need to add in the response header Access-Control-Allow-Credentials to true. Otherwise, there are two cases on the browser:

  • If it is a simple request, the server spit out the results, but to get the browser is not to spit it out, and an error.
  • If it is a preflight request, we can not get the same return results and error prompt preflight request is not passed, it does not send a second request.

other

cookie-origin policy

The other is set withCredentials property of XMLHttpRequest sent out to true, the browser, the server still can not get a cookie problem.

cookie also follow the same-origin policy, setting a cookie when you can find that in addition to key-value pairs, you can also set these cookie values:

cookie property value Explanation
path Can access the cookie path, the default path for the location of the current document
domain The domain part of the domain can access a cookie, the default position for the current document's path
max-age After the long failure, seconds time.
Negative: in the session valid; 0: delete the cookie; positive numbers: Valid for creating time + max-age
expires cookie expiration date. If not defined, cookie will expire at the end of the dialogue, that the session cookie
secure cookie is only transmitted through the https protocol

If you get less than cookie, cookie can check under the domain and path.

No cross-domain access rights on IE

When prompted to send a cross-domain ajax request no permissions. Because IE browser default there are restrictions on cross-domain access. The need to remove restrictions in your browser settings.
Methods:  Setting> Internet Options> Security> Custom Level> found in other settings - the [other] [will be] to access data sources across domains is enabled.

Source Demo

CORS Demo source code

END

Published 35 original articles · won praise 17 · views 30000 +

Guess you like

Origin blog.csdn.net/dev666/article/details/104200362