Article Directory
Sonco has written about this cross-domain issue before, but I recently received some questions from my little buddies that made me realize that the previous summary was not comprehensive enough, so I plan to write another article to share the cross-domain issue in Spring Boot with you.
This time I've divided the cross-domain issues in Spring Boot into three scenarios:
- Common cross-domain
- Spring Security Cross Domain
- OAuth2 Cross Domain
It's not too much to divide into three, mainly because the three scenes are not configured in the same way, and these three scenes are very common, so here's a special one to share with you.
1. What is cross-domain
Many people have a misconception about cross-domain, thinking that this is a front-end thing and has nothing to do with the back-end. In fact, this is not the case. When it comes to cross-domain, you have to talk about the browser's homology policy.
Homology policy, a well-known security policy proposed by Netscape, is the most core and basic security feature of browsers, and is now used by all JavaScript-enabled browsers.Homology means that protocols, domain names and ports must be identical.
Homology policy is based on security considerations. This policy is not a problem, but we often have cross-domain requirements in actual development for various reasons. The traditional cross-domain scheme is JSONP. Although JSONP can solve cross-domain problems, it has a big limitation: it only supports GET requests, does not support other types of requests, and in RESTful This is hardly useful in the times.
Today, CORS (Cross-origin resource sharing) is a W3C standard. It is a specification of browser technology and provides a way for Web services to pass sandbox scripts from different domains to avoid browser's homology policy. This is a modern version of JSONP mode.
Within the Spring framework, there is also a solution for CORS, which is simplified in the Spring Boot and becomes very easy whether it is a simple cross-domain or a cross-domain after combining Spring Security.
2. Solutions
First, create two common SpringBoot projects. Needless to mention more, the first one is named provider Provider, the second consumer consumer service, the first configuration port is 8080, the second configuration is 8081, and then provide two hello interfaces, one get and one post on the provider, as follows:
@RestController public class HelloController { @GetMapping("/hello") public String hello() { return "hello"; } @PostMapping("/hello") public String hello2() { return "post hello"; } }
Create an html file in 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>
Then start two projects, send a 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.
You can see that the request could not be sent successfully due to the restrictions of the homology policy.
Using CORS enables cross-domain implementation without any modifications to the front-end code, so let's see how to configure it in the provider.You can first configure a method to accept requests from a domain through the @CrossOrigin annotation, as follows:
@RestController public class HelloController { @CrossOrigin(value = "http://localhost:8081") @GetMapping("/hello") public String hello() { return "hello"; } @CrossOrigin(value = "http://localhost:8081") @PostMapping("/hello") public String hello2() { return "post hello"; } }
This comment indicates that both interfaces accept fromHttp://localhost: 8081 address request, after configuration is complete, restart the provider, send the request again, the browser console will not error, consumer can also get the data.
Looking at the browser request network console at this point, you can see that there is more information in the response header as follows:
This indicates that the server is willing to receive fromHttp://localhost: 8081 request, get this information, browsers will no longer limit the cross-domain of this request.
On the provider, it's a hassle to annotate every method. Some buddies think they can add annotations directly to the Controller, but it's still a hassle to add each Controller. In Spring Boot, global configuration can also solve this problem one time by one. Global configuration only needs to override addCorsMappings in the configuration class of Spring MVC.The method is as follows:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8081") .allowedMethods("*") .allowedHeaders("*"); } }
/** means that all methods applied in this application will process cross-domain requests, allowedMethods means the number of requests allowed to pass, and allowedHeaders means the allowed request headers.With this configuration, you do not have to configure cross-domain individually on each method.
2.1 Problems
Understanding the entire CORS process, we send cross-domain requests through Ajax. Although the user experience has improved, there are potential threats, such as CSRF (Cross-site request forgery) cross-site request forgery.Cross-site request forgery, also known as one-click attack s or session riding, is often abbreviated as CSRF or XSRF and is an attack method to coerce users from performing unintentional operations on currently logged-in Web applications.
For specific descriptions and defenses against CSRF attacks, you can refer to the previous Songo articles, which do not repeat here:
- Songgo teaches you how to defend against CSRF attacks in SpringBoot!so easy!
- Learn thoroughly!CSRF Defense Source Parsing in Spring Security
3.SpringSecurity
If Spring Security is used, the cross-domain configuration above will fail because the request is intercepted by Spring Security.
When Spring Security was introduced, there were two ways to turn on Spring Security support across domains.
3.1 Mode 1
One way is to add Spring Security support for CORS on the basis of the previous section by simply adding 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(); } }
A.CORS opens Spring Security support for CORS.
3.2 Mode 2
Mode two removes the cross-domain configuration from the second section and configures it globally directly 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() { 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); return source; } }
Cross-domain information is configured in detail through the CorsConfiguration Source instance, such as the allowed request source, the allowed request method, the allowed request header, the validity period of the probe request, the path to be processed, and so on.
This way you can remove the cross-domain configuration from the second section.
4.OAuth2
Another scenario is that OAuth2 allows cross-domain configuration. If a user wants to access an OAuth2 endpoint, such as/oauth/token, what about cross-domain configuration?
This solution Songo used to Using Swagger to test the interface, how do I carry Token in the request header? The article has already been introduced, mainly configuring a CorsFilter, which you can refer to. Here I will list the core configuration classes:
@Configuration public class GlobalCorsConfiguration { @Bean public CorsFilter corsFilter() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowCredentials(true); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); return new CorsFilter(urlBasedCorsConfigurationSource); } }
Then turn on 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(); } }
5. Summary
Well, today the main and small partners summarize three cross-domain scenarios in Spring Boot. Do you know if you have GET?If you feel there is something to gain, remember to take a look at the encouragement of Panasonic Oh~