Writing in the front

Cross-domain problems I believe most people have encountered, here I do a simple summary, generally a simple introduction to cross-domain problems, as well as for SpringBoot cross-domain solutions. If you think you’ve learned something from your writing, be sure to like it and follow it.

Introduction of cross-domain

Cross-domain has an English abbreviation, called CORS, its full name is cross-domain resource sharing (CORS), is a mechanism. The basic principle of cross-domain is to use additional HTTP headers to tell the browser that Web applications running on one Origin (domain) are allowed to access specified resources from different source servers. When a resource requests a resource from a “different domain, protocol, or port” than the server on which the resource itself resides, the resource makes a “cross-domain HTTP request.”

It’s important to emphasize that many people have a misconception about cross-domain, thinking that it’s about the front end and not the back end, but it’s not. When it comes to cross-domain, you have to talk about the same origin policy in the browser. The same origin policy is a famous security policy proposed by Netscape. It is the core and most basic security feature of the browser. Now all browsers that support JavaScript use this policy. The same protocol, domain name, and port must be the same. In other words, if the protocol, domain name, or port is different, the request between the application and the service is a cross-domain request, and special processing is required to pass the browser security policy.

The same origin policy is proposed based on security considerations, which is no problem in itself, but in actual development, due to various reasons, we often have cross-domain requirements. The traditional cross-domain solution is JSONP. Although JSONP can solve cross-domain, it has a great limitation, that is, it only supports GET requests. Other types of requests are not supported, which is almost useless in the RESTful era. CORS (Cross-domain Source Sharing) is a W3C standard, a specification of browser technology that provides a way for Web services to sandbox scripts from different domains to avoid the browser’s same-origin policy, a modern version of the JSONP pattern. In the Spring framework, there is also a corresponding solution for CORS. In Spring Boot, this solution has to be simplified, whether it is simply cross-domain, or cross-domain with Spring Security, it has become very easy.

preparation

Create two normal Spring Boot projects. The first one is named Provider to provide the service, the second one is named Consumer to consume the service, the first configuration port is 8080, and the second configuration configuration is 8081. The provider then provides two Hello interfaces, one get and one POST, as follows:

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(a) {
        return "hello";
    }
    @PostMapping("/hello")
    public String hello2(a) {
        return "post hello"; }}Copy the code

Create an HTML file in the Consumer’s Resources /static directory and send a simple Ajax request as follows:

<div id="app"></div>
<input type="button" onclick="btnClick()" value="get_button">
<input type="button" onclick="btnClick2()" value="post_button">
<script>
    function btnClick() {
        $.get('http://localhost:8080/hello', function (msg) {
            $("#app").html(msg);
        });
    }

    function btnClick2() {
        $.post('http://localhost:8080/hello', function (msg) {
            $("#app").html(msg);
        });
    }
</script>
Copy the code

Then launch the two projects separately, send the request button, and observe the browser console as follows:

Access to XMLHttpRequest at 'http://localhost:8080/hello' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Copy the code

As you can see, the request could not be sent due to the same origin policy. Using CORS allows cross-domain implementation without any changes to the front-end code, so let’s look at how to configure it in a provider. You can first configure a method to accept requests for a field using the @Crossorigin annotation as follows:

@RestController
public class HelloController {
    @CrossOrigin(value = "http://localhost:8081")
    @GetMapping("/hello")
    public String hello(a) {
        return "hello";
    }

    @CrossOrigin(value = "http://localhost:8081")
    @PostMapping("/hello")
    public String hello2(a) {
        return "post hello"; }}Copy the code

This annotation indicates that the two interfaces accept requests from http://localhost:8081, and when configured, restart the provider and send the request again, so that the browser console does not report an error and the consumer can get the data. If you look at the browser request network console, you can see the following information in the response header:

This indicates that the server is willing to accept the request from http://localhost:8081. With this information, the browser does not restrict the cross-domain of the request. In Spring Boot, you can also use global configuration to solve this problem at once. In Spring Boot, you can use global configuration to solve this problem. The global configuration simply requires overriding the addCorsMappings method in the SpringMVC configuration class, as follows:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/ * *")
        .allowedOrigins("http://localhost:8081")
        .allowedMethods("*")
        .allowedHeaders("*"); }}Copy the code

/** indicates that all methods of the application process cross-domain requests, allowedMethods indicates the number of requests that are allowed to pass, and allowedHeaders indicates the allowed request headers. After this configuration, it is not necessary to configure cross-domains separately on each method.

Cross domain issues are addressed in SpringSecurity

If Spring Security is used, the cross-domain configuration above is invalidated because the request is intercepted by Spring Security. When Spring Security was introduced, there were two ways to enable Spring Security’s cross-domain support.

Methods a

The first method is to add Spring Security’s support for CORS on the basis of the previous one. You only need to add the following configuration:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .permitAll() .and() .httpBasic() .and() .cors() .and() .csrf() .disable(); }}Copy the code

A.cors enables Spring Security’s support for CORS.

Way 2

The second method is to remove the previous cross-domain configuration and directly perform global configuration in Spring Security, as follows:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .permitAll()
                .and()
                .httpBasic()
                .and()
                .cors()
                .configurationSource(corsConfigurationSource())
                .and()
                .csrf()
                .disable();
    }
    @Bean
    CorsConfigurationSource corsConfigurationSource(a) {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowCredentials(true);
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setMaxAge(Duration.ofHours(1));
        source.registerCorsConfiguration("/ * *",configuration);
        returnsource; }}Copy the code

The CorsConfigurationSource instance provides detailed configuration of cross-domain information, such as allowed request sources, allowed request methods, allowed request headers, expiration dates for probe requests, paths to be processed, and so on. In this way, you can remove the previous cross-domain configuration.

OAuth2 cross-domain processing

If OAuth2 allows cross-domain access to an OAuth2 endpoint, such as /oauth/token, how to configure cross-domain access? This is a simple request, we only need to carry a Token in the request, mainly to configure a CorsFilter, I here listed the core configuration class:

@Configuration
public class GlobalCorsConfiguration {
    @Bean
    public CorsFilter corsFilter(a) {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/ * *", corsConfiguration);
        return newCorsFilter(urlBasedCorsConfigurationSource); }}Copy the code

Then enable cross-domain support in SecurityConfig:

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SecurityConfig extends WebSecurityConfigurerAdapter {... .@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .requestMatchers().antMatchers(HttpMethod.OPTIONS, "/oauth/**") .and() .csrf().disable().formLogin() .and() .cors(); }}Copy the code