Original address: xeblog.cn/articles/12

The introduction

Although the project with separated front and back ends reduces the coupling degree, it also causes various problems. The back-end project is deployed by Tomcat (listening on port 8080), and the front-end project is deployed on Nginx (listening on port 80, 443 and other non-8080 ports). The front-end page loading speed is greatly improved, but an error is reported when ajax requests the back-end interface.

The same-origin policy

The same origin policy, which is a famous security policy proposed by Netscape. This strategy is now used by all browsers that support JavaScript. Same name means same domain name, same protocol, same port.

Front-end address: http://127.0.0.1:63344

The back-end address is http://127.0.0.1:8080

The IP addresses and protocols of the two IP addresses are the same, but the ports of the two IP addresses are different. Therefore, the IP addresses of the two IP addresses do not meet the same source. This causes cross-domain problems.

The solution

Configuration addCorsMappings

Add a class that implements the WebMvcConfigurer interface, then add the @Configuration annotation to the class, and finally implement the addCorsMappings methods.

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
	@Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/ * *")
                .allowedOrigins("*")
                .allowedMethods("POST"."GET"."PUT"."OPTIONS"."DELETE")
                .maxAge(3600)
                .allowCredentials(true); }}Copy the code

Request to normal

And that’s it? Is it that simple?

Login backstage management to see

wtf?

Why OPTIONS request?

According to my logic, the above error interface should be a GET request, but why send OPTIONS request? Read all kinds of information, all kinds of Baidu Google, all kinds of inverted, all kinds of headaches, finally… I pulled back the covers and was surprised to find… What a warm bed! (Sleep on it)

why

Are not all requests sent but are known as Preflighted requests but surrounding requests are used to verify that this request is safe. But not all requests are sent, but one of the following conditions is known:

  • The request method is not GET/HEAD/POST
  • The content-type of a POST request is not Application/X-www-form-urlencoded, multipart/form-data, or Text /plain
  • The request sets a custom header field

For the interface on the management side, I verify the permission of the interface. Each request needs to carry a user-defined field (token) in the header, so the browser will send an additional OPTIONS request.

Then why did the OPTIONS request get an error…

The debugging finds that the OPTIONS request carries only user-defined fields but does not insert the corresponding values. However, the token field is NULL during background verification. Therefore, the verification fails and an exception is thrown.

Release the OPTIONS

I took a different approach to cross-domain resolution, using interceptors to solve cross-domain problems and permitting OPTIONS requests.

A new interceptor class CorsInterceptor implements the HandlerInterceptor interface

@Component
public class CorsInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        response.setHeader("Access-Control-Allow-Origin"."*");
        response.setHeader("Access-Control-Allow-Credentials"."true");
        response.setHeader("Access-Control-Allow-Methods"."GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS");
        response.setHeader("Access-Control-Max-Age"."86400");
        response.setHeader("Access-Control-Allow-Headers"."*"); // End the request if it is OPTIONSif (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            response.setStatus(HttpStatus.NO_CONTENT.value());
            return false;
        }

        return true; }}Copy the code

Cross-domain interceptors need to be placed on top of checksum interceptors

I deleted the original cross-domain configuration addCorsMappings

@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Resource private LoginInterceptor loginInterceptor; @Resource private CorsInterceptor corsInterceptor; @override public void addInterceptors(InterceptorRegistry registry) {// Cross-domain interceptors need to be placed at the top registry.addInterceptor(corsInterceptor).addPathPatterns("/ * *"); Registry. AddInterceptor (loginInterceptor). AddPathPatterns ()"/admin/**"); }}Copy the code

OPTIOINS request is clear

GET requests are also sent

conclusion

Good night!

added

The access-Control-allow-credentials response header indicates whether the response to the request can be exposed to the page. Returns true, and all other values do not. Credentials can be cookies, Authorization headers, or TLS Client certificates. If the value of access-Control-allow-origin is set to true, the server must not set the access-Control-allow-origin value to *. The domain name must be specified. Otherwise, the browser will report an error when sending cookies to the server.

test

Ajax requests enable the withCredentials attribute

xhrFields: {
	withCredentials: true
}
Copy the code

If the back-end cross-domain configuration remains unchanged, an error message is displayed

If access-control-allow-origin is set to http://127.0.0.1:63344, the request will work again

The final configuration

@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, {response.setheader () {response.setheader () {response.setheader ("Access-Control-Allow-Origin", request.getHeader("origin"));
        response.setHeader("Access-Control-Allow-Credentials"."true");
        response.setHeader("Access-Control-Allow-Methods"."GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS");
        response.setHeader("Access-Control-Max-Age"."86400");
        response.setHeader("Access-Control-Allow-Headers"."*"); // End the request if it is OPTIONSif (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            response.setStatus(HttpStatus.NO_CONTENT.value());
            return false;
        }

        return true;
    }
Copy the code

reference

  • Levy. Work / 2016-09-01 -…

  • Developer.mozilla.org/en-US/docs/…

  • www.ruanyifeng.com/blog/2016/0…