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.html
accessing:
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/cors
request.
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/cors
directly 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:8000
is 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:8000
this 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.