A normal request
Recently, others need to call a function of our system, and the other party hopes to provide an API so that it can update data. Since this classmate is a client developer, he has a code similar to the following.
@RequestMapping(method = RequestMethod.POST, value = "/update.json", produces = MediaType.APPLICATION_JSON_VALUE)public @ResponseBody Contacter update(@RequestBody Contacter contacterRO) {logger.debug("get update request {}", contacterRO.toString());if (contacterRO.getUserId() == 123) {contacterRO.setUserName("adminUpdate-wangdachui");}return contacterRO;}The client initiates an http request through code to call it. Then, the student asked: he hoped to use js calls through the browser, so there was a cross-domain problem.
Why cross-domain
Simply put, the browser restricts access to the js code under site A to make ajax request to the url under site B. If the current domain name is www.abc.com, then the js code running in the current environment cannot access the resources under the www.zzz.com domain name for security reasons.
For example: The following code can be used to call the interface normally through js code under this domain name
(function() {var url = "http://localhost:8080/api/Home/update.json";var data = { "userId": 123, "userName": "wangdachui" };$.ajax({url: url, type: 'POST', dataType: 'json', data: $.toJSON(data), contentType: 'application/json'}).done(function(result) {console.log("success");console.log(result);}).fail(function() {console.log("error");})})()The output is:
Object {userId: 123, userName: "adminUpdate-wangdachui"}However, accessing under other domain names will cause an error:
OPTIONS http://localhost:8080/api/Home/update.jsonXMLHttpRequest cannot load http://localhost:8080/api/Home/update.json. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403.
Solution
JSONP
Using jsonp to cross-domain is a relatively common way, but when the interface has been written, both the server and the call side need to be transformed and compatible with the original interface, which is a bit too much work, so we consider other methods.
CORS protocol
According to references: Each page needs to return an HTTP header named 'Access-Control-Allow-Origin' to allow access to the site in the foreign domain. You can just expose limited resources and limited out-of-domain site access. In COR mode, the responsibility of access control can be placed in the hands of the page developer, not the server administrator. Of course, page developers need to write special processing code to allow access to the outside world. We can understand it as: if a request needs to allow cross-domain access, you need to set Access-Control-Allow-Origin in the http header to decide which sites to allow to access. If you need to allow requests from www.foo.com to cross-domain, you can set: Access-Control-Allow-Origin: http://www.foo.com. Or Access-Control-Allow-Origin: * . CORS is supported in most modern browsers as part of HTML5.
CORS has the following common headers
Access-Control-Allow-Origin: http://foo.orgAccess-Control-Max-Age: 3628800Access-Control-Allow-Methods: GET, PUT, DELETEAccess-Control-Allow-Headers: content-type "Access-Control-Allow-Origin" indicates that it allows "http://foo.org" to initiate cross-domain requests. "Access-Control-Max-Age" indicates that within 3628800 seconds, no pre-check requests are required. The result "Access-Control-Allow-Methods" indicates that it allows GET, PUT, DELETE out-domain requests "Access-Control-Allow-Headers" indicates that it allows cross-domain requests to include content-type headers
Basic CORS process
First, a preflight request is issued, which first issues an OPTIONS method and a request containing the "Origin" header to the resource server. This reply can control the COR request method, HTTP header, and verification information. A real foreign domain request will only be initiated after the request is allowed.
Spring MVC supports CORS
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403.
From the above error message, we can see that the direct reason is that there is no Access-Control-Allow-Origin header in the request header. So our direct idea is to add this header to the request header. The server can return 403, indicating that the server has indeed processed the request.
MVC Interceptor
First, we configure an interceptor to intercept the request and log the request's header information.
DEBUG requestURL:/api/Home/update.json DEBUG method:OPTIONS DEBUG header host:localhost:8080 DEBUG header connection: keep-alive DEBUG header cache-control:max-age=0 DEBUG header access-control-request-method:POST DEBUG header origin:null DEBUG header user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36 DEBUG header access-control-request-headers:accept, content-type DEBUG header accept:*/* DEBUG header accept-encoding:gzip, deflate, sdch DEBUG header accept-language:zh-CN,zh;q=0.8,en;q=0.6
Printing the log in postHandle found that the status of the response is 403 at this time. Tracking SpringMVC codes found that in org.springframework.web.servlet.DispatcherServlet.doDispatch, HandlerExecutionChain will be obtained based on request. After SpringMVC acquires a regular processor, it will check whether it is a cross-domain request. If so, it will replace the original instance.
@Overridepublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {Object handler = getHandlerInternal(request);if (handler == null) {handler = getDefaultHandler();}if (handler == null) {return null;}// Bean name or resolved handler?if (handler instanceof String) {String handlerName = (String) handler;handler = getApplicationContext().getBean(handlerName);}HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);if (CorsUtils.isCorsRequest(request)) {CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);executionChain = getCorsHandlerExecutionChain(request, executionChain, config);}return executionChain;}The checking method is also very simple, that is, check whether there is an origin field in the request header.
public static boolean isCorsRequest(HttpServletRequest request) {return (request.getHeader(HttpHeaders.ORIGIN) != null);}The request will then be handed over to HttpRequestHandlerAdapter.handle for processing, and different logics will be processed according to the handle. The previous judgment is based on the request header as a cross-domain request. The obtained Handler is PreFlightHandler, which is implemented as:
@Overridepublic void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {corsProcessor.processRequest(this.config, request, response);}Continue to follow up
@Overridepublic Boolean processRequest(CorsConfiguration config, HttpServletRequest request, HttpServletResponse response)throws IOException {if (!CorsUtils.isCorsRequest(request)) {return true;}ServletServerHttpResponse serverResponse = new ServletServerHttpResponse(response);ServletServerHttpRequest serverRequest = new ServletServerHttpRequest(request); if (WebUtils.isSameOrigin(serverRequest)) {logger.debug("Skip CORS processing, request is a same-origin one");return true;}if (responseHasCors(serverResponse)) {logger.debug("Skip CORS processing, response already contains /"Access-Control-Allow-Origin/" header");return true;}Boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);if (config == null) {if (preFlightRequest) {rejectRequest(serverResponse);return false;} else {return true;}}return handleInternal(serverRequest, serverResponse, config, preFlightRequest);}This method first checks whether it is a cross-domain request, and if it is not, it will return directly. Then checks whether it is under the same domain, or whether it has the Access-Control-Allow-Origin field in the response header or whether it has the Access-Control-Request-Method in the request. If the judgment condition is met, the request is rejected.
From this we know that the check can be passed by setting the Access-Control-Allow-Origin header of the response before the check. We handle preHandle in the interceptor. Add the following code:
response.setHeader("Access-Control-Allow-Origin", "*");At this time, the OPTIONS request in the browser returns 200. But still an error:
Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
We noticed that there is Access-Control-Request-Headers:accept, content-type in the request header, but this request header does not. At this time, the browser does not send the request as required. Try adding it to the response:
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");Execution successful: Object {userId: 123, userName: "adminUpdate-wangdachui"}.
So far: we use the analysis principle to enable SpringMVC to achieve cross-domain, without any changes to the original implementation and client code.
SpringMVC 4
Furthermore, in Reference 2, SpringMVC4 provides a very convenient method to implement cross-domain.
Use annotations in requestMapping. @CrossOrigin(origins = "http://localhost:9000")
Global implementation. Definition class inheritance WebMvcConfigurerAdapter
public class CorsConfigurerAdapter extends WebMvcConfigurerAdapter{@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/api/*").allowedOrigins("*");}}Inject the class into the container:
<bean></bean>
Summarize
The above is all the detailed explanation of Spring implementation and processing cross-domain request code. I hope it will be helpful to everyone. Interested friends can continue to refer to other related topics on this site. If there are any shortcomings, please leave a message to point it out. Thank you friends for your support for this site!