SpringBoot tool library: solve the cross-domain problem of SpringBoot2.* version

1. Solve the problem: When allowCredentials is true, xxxxxxx , using “allowedOriginPatterns“ instead

The cross-domain configuration of version 2.3 is as follows

/**
 * 跨域问题解决
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                .maxAge(3600)
                .allowCredentials(true);
    }
}

1.1.Solution:

        There are similar issues on the Spring official website: https://github.com/spring-projects/spring-framework/issues/26111

        The general meaning is: the allowedOriginPatterns method is provided for use. When the original allowCredentials is true, allowedOrigins cannot be matched using the * sign.

/**
 * 跨域问题解决
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                .maxAge(3600)
                .allowCredentials(true);
    }
}

In fact, there are two ways to solve cross-domain classification, one is to use annotations, and the other is to write a configuration class 

2. Several ways to solve cross-domain problems

2.1. CorsFilter method to set cross-domain

        CorsFilter filter: To handle cross-domain requests through Spring Boot's CORS filter, you need to define a CorsFilter Bean, for example:

@Configuration
public class CoreFilter {
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOriginPattern("*");  // 不是 addAllowedOrigin
        config.setAllowCredentials(true);
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);

        return new CorsFilter(source);
    }
}

2.2.Interceptor implements cross-domain

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.io.Serializable;

@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer, Serializable {
    private static final long serialVersionUID = 1L;

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                // 不是 addAllowedOrigin
                .allowedOriginPatterns("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                .maxAge(3600)
                .allowCredentials(true);
    }

    /**
     * 修改访问路径
     * @param configurer
     */
    public void configurePathMatch(PathMatchConfigurer configurer) {
        // 设置为true后,访问路径后加/ 也能正常访问  /user == /user/
         configurer.setUseTrailingSlashMatch(true);
    }

}

        In the same way as using CorsFilter, when allowCredentials is true, allowedOrigins cannot be set to *, and allowedOriginPatterns must be used. Otherwise, it will eventually go to the verification in the CorsConfiguration#validateAllowCredentials method.

2.3. Cross-domain implementation through @CrossOrigin

@CrossOrigin can annotate classes and individual methods. Use the @CrossOrigin annotation to annotate the Controller class or method to specify the allowed cross-domain source address, request method, header and other attributes.

Annotation class:

All methods under the Controller request path are allowed across domains.

@CrossOrigin(origins = "http://example.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

    @RequestMapping(method = RequestMethod.GET, path = "/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

        Added at class level  @CrossOrigin. Therefore, retrieve() and  remove() methods are enabled  @CrossOrigin. We can customize the configuration by specifying the values ​​of the following annotation properties: origins, methods, allowedHeaders, exposedHeaders, allowCredentials or  maxAge.

Labeling method:

Only annotated methods are allowed across domains.

@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin
    @RequestMapping(method = RequestMethod.GET, path = "/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

In the above example, we  retrieve() have enabled CORS only for the method. We can see that we did not  @CrossOrigin set any configuration for the annotation, so it uses the default value:

  • All origins are allowed.
  • The allowed HTTP methods are  those specified in @RequestMapping the annotation (in this case  ).GET
  • Preflight responses are cached for 30 minutes (maxAge).
on classes and methods
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin("http://example.com")
    @RequestMapping(method = RequestMethod.GET, "/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

Spring will merge the properties of the two annotations to create a merged CORS configuration.

Here, both methods  maxAge are  3600 seconds, remove() the method allows all origins, and  retrieve() the method only allows  http://example.com origins from.

2.4.nginx

Nginx reverse proxy: forward the request to Nginx, and Nginx handles cross-domain requests uniformly, for example:

location /api/ {
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
    }
    proxy_pass http://localhost:8080;
}

2.5.webFilter

Use the @WebFilter annotation: This annotation can be used directly on the filter class and specifies the filtered URL, similar to the filter configured in the xml file

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Component
@WebFilter(urlPatterns = "/*")
@Order(-99999)
public class CorsFilter extends HttpFilter {

	private static final long serialVersionUID = 2386571986045107652L;

	private static final String OPTIONS_METHOD = "OPTIONS";

	@Override
	protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {

		String origin = req.getHeader(HttpHeaders.ORIGIN);

		if (!StringUtils.isEmpty(origin)) {
			
			// 允许客户端的域
			res.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);

			// 允许客户端提交的Header
			String requestHeaders = req.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
			if (!StringUtils.isEmpty(requestHeaders)) {
				res.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders);
			}
			
			// 允许客户端访问的Header
			res.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
			
			// 允许客户端携带凭证信息
			res.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
			
			// 允许客户端请求方法
			res.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, OPTIONS, DELETE");

			if (OPTIONS_METHOD.equalsIgnoreCase(req.getMethod())) {
				res.setStatus(HttpServletResponse.SC_NO_CONTENT);
				res.setContentType(MediaType.TEXT_HTML_VALUE);
				res.setCharacterEncoding("utf-8");
				res.setContentLength(0);
				res.addHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "1800");
				return;
			}
		}

		super.doFilter(req, res, chain);
	}
}

Guess you like

Origin blog.csdn.net/qq_20957669/article/details/132616061