CORS is a W3C standard, full name is “cross-origin Resource Sharing”; In detail before CORS simple introduce what is the same-origin policy, in order to understand the need for the origin of CORS |.
Browser Same-origin policy
The same origin policy is the cornerstone of browser security and is currently implemented by all browsers.
meaning
By “homology”, we mean the following three are identical:
- The agreement is the same
- Domain name is the same
- The same port
Here’s an example:
The current site | The requested page address | Whether to cross domains (different sources) | why |
---|---|---|---|
http://www.wu-yikun.top/page.html |
http://www.wu-yikun.top/main.html |
no | With the protocol (http ), same domain name (www.wu-yikun.top ), same port (80 ) |
http://www.wu-yikun.top/page.html |
https://www.wu-yikun.top/other.html |
is | Different protocols (http 与 https ) |
http://www.wu-yikun.top/page.html |
http://www.wu-yikun.com/page.html |
is | Different domain names (www.wu-yikun.top 与 www.wu-yikun.com ) |
http://www.wu-yikun.top/page.html |
http://www.wu-yikun.top:8090/other.html |
is | Different ports (80 vs. 8090) |
Now look at this picture, is it clear?
purpose
Excerpt from “Ruan Yifeng” teacher’s article
The purpose of the same origin policy is to ensure the security of user information and prevent malicious websites from stealing data.
Consider this situation: Site A is A bank, and the user logs in and then goes to another site. What happens if other websites can read the cookies of WEBSITE A?
Obviously, if a Cookie contains privacy (such as the amount of money deposited), this information will be disclosed. What’s more, cookies are often used to save the user’s login status. If the user does not log out, other websites can pretend to be the user and do whatever they want. Because browsers also specify that submitting forms is not subject to the same origin policy.
Therefore, the same origin policy is necessary, otherwise cookies can be shared and the Internet is not safe at all.
limit
With the development of the Internet, the same origin policy has become more and more strict. If not homologous, the following behaviors are limited.
Cookie
,LocalStorage
和IndexDB
Unable to readDOM
Unable to getAJAX
The request could not be sent
A single AJAX request that can’t be sent can inhibit many legitimate uses.
Next, you’ll see how to implement cross-domain resource sharing to avoid the same-origin policy.
CORS,
Introduction to the
CORS requires both browser and server support. At present, all browsers support this function. In the process of CORS communication, the browser will automatically complete without user participation. Therefore, the key to achieve CORS communication is to enable the server to support THE CORS interface, so that cross-source communication can be achieved.
CORS has two types of requests
Browsers classify CORS requests into two categories: simple and non-simple.
A simple request
It is a simple request as long as the following two conditions are met:
- The request method is one of three types
HEAD
GET
POST
HTTP
The header information does not exceed the following fieldsAccept
Accept-Language
Content-Language
Content-Type
: onlyapplication/x-www-form-urlencoded
,multipart/form-data
,text/plain
One of the three
Anything that does not meet both conditions is a non-simple request.
Here is a simple CORS request:
The following is the request packet sent by the browser to the server:
GET /resources/public-data/ HTTP / 1.1
Host: bar.other
User-Agent: Mozilla / 5.0 (Macintosh; Intel Mac OS X 10.14; The rv: 71.0) Gecko / 20100101 Firefox 71.0Accept: text/html,application/xhtml+xml,application/xml; Q = 0.9 * / *; Q = 0.8Accept-Language: en-us,en; Q = 0.5Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
Copy the code
The Origin field in the request header indicates that the request is from http://foo.example.
HTTP / 1.1 200 OK
Date: Mon, 01 Dec 2021 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[XML Data]
Copy the code
In this case, the access-Control-allow-Origin: * returned by the server indicates that the resource can be accessed by any external domain.
Access-Control-Allow-Origin: *
Copy the code
Use Origin and access-Control-Allow-Origin to do the simplest Access Control. If the server only allows access from https://foo.example, the header field would look like this:
Access-Control-Allow-Origin: https://foo.example
Copy the code
Non-simple request
Non-simple requests are requests that have special requirements on the server, such as the request method being PUT or DELETE, or the content-Type field Type being Application/JSON.
A major feature of CORS requests that are not simple requests is the addition of an HTTP query request, called a “Preflight request,” before formal communication. The method of “prechecking” request is OPTIONS. The use of “prechecking” request can avoid unexpected impact of cross-domain request on user data of the server.
Here is an HTTP request that needs to perform a precheck request:
As described below, actual POST requests do not carry the Access-Control-request -* header; they only apply to OPTIONS requests.
The following is the complete information interaction between the server and client.
The first is the precheck request:
OPTIONS /doc HTTP / 1.1
Host: bar.other
User-Agent: Mozilla / 5.0 (Macintosh; Intel Mac OS X 10.14; The rv: 71.0) Gecko / 20100101 Firefox 71.0Accept: text/html,application/xhtml+xml,application/xml; Q = 0.9 * / *; Q = 0.8Accept-Language: en-us,en; Q = 0.5Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
Copy the code
The browser detects that requests made from JavaScript need to be prechecked. From the above message, we can see that lines 1 to 10 send a “precheck request” using the OPTIONS method. OPTIONS are methods defined in HTTP/1.1 to get more information from the server. This method has no impact on server resources. The precheck request carries both of the following header fields:
- Access-Control-Request-Method: POST
- Access-Control-Request-Headers: X-PINGOTHER, Content-Type
The header field access-Control-request-method tells the server that the actual Request will use the POST Method.
The header field access-Control-request-HEADERS tells the server that the actual Request will carry two custom header fields: X-Pingother and Content-Type.
The server then decides whether the actual request is allowed.
Precheck response:
HTTP / 1.1 204 No Content
Date: Mon, 01 Dec 2021 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Copy the code
(1) The server’s response carries access-Control-allow-Origin: https://foo.example, thus limiting the source domain of the request.
(2) Access-Control-allow-methods indicates that the server allows clients to initiate requests using POST and GET Methods (similar to the Allow header, but with strict Access Control).
Access-control-allow-headers indicates that the server allows x-Pingother and Content-Type to be carried in the request. The access-Control-allow-HEADERS field is required if the browser Request includes the access-Control-request-HEADERS field. It is also a comma-separated string indicating all header information fields supported by the server, not limited to those requested by the browser in precheck.
(4) Finally, the header field access-Control-max-age indicates that the valid time of the response is 86400 seconds, that is, 24 hours. The browser does not have to make another precheck request for the same request during the valid time. Note that the browser itself maintains a maximum validity time, and if the value of this header field exceeds the maximum validity time, it will not take effect.
After the precheck request is complete, send the actual request:
POST /doc HTTP / 1.1
Host: bar.other
User-Agent: Mozilla / 5.0 (Macintosh; Intel Mac OS X 10.14; The rv: 71.0) Gecko / 20100101 Firefox 71.0Accept: text/html,application/xhtml+xml,application/xml; Q = 0.9 * / *; Q = 0.8Accept-Language: en-us,en; Q = 0.5Accept-Encoding: gzip,deflate
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: https://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: https://foo.example
Pragma: no-cache
Cache-Control: no-cache
Copy the code
Actual response packet:
HTTP / 1.1 200 OK
Date: Mon, 01 Dec 2021 01:15:40 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain
Copy the code
In the header above, the access-Control-Allow-Origin field is required for each response.
Spring Boot solves cross-domain problems
With all that said, let’s get a feel for the actual scene.
Do you feel a certain “kinship”? Here’s how to deal with this feeling of powerlessness.
Manually set the response header through the Filter Filter
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
@Slf4j
@WebFilter(urlPatterns = {"/*"}, filterName = "corsFilter")
public class CorsFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("Enable cross-domain filters");
}
@Override
public void doFilter(ServletRequest request, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) resp;
// Manually set the response header to resolve cross-domain access
response.setHeader("Access-Control-Allow-Origin"."*");
response.setHeader("Access-Control-Allow-Methods"."POST, PUT, GET, OPTIONS, DELETE");
// Set the expiration time
response.setHeader("Access-Control-Max-Age"."86400");
response.setHeader("Access-Control-Allow-Headers"."Origin, X-Requested-With, Content-Type, Accept, Authorization, uuid");
// Support HTTP 1.1
response.setHeader("Cache-Control"."no-cache, no-store, must-revalidate");
// HTTP 1.0.response. setHeader("Expires", "0") is supported;
response.setHeader("Pragma"."no-cache");
/ / code
response.setCharacterEncoding("UTF-8");
chain.doFilter(request, resp);
}
@Override
public void destroy(a) {
log.info("Destroy cross-domain filters"); }}Copy the code
Using @Crossorigin annotations (local cross domain)
@crossorigin
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
@AliasFor("origins")
String[] value() default {};
@AliasFor("value")
String[] origins() default {};
String[] allowedHeaders() default {};
String[] exposedHeaders() default {};
RequestMethod[] methods() default {};
String allowCredentials(a) default "";
long maxAge(a) default -1L;
}
Copy the code
Annotated with @Crossorigin:
@CrossOrigin(origins = "*", allowedHeaders = "*", maxAge = 86400)
@PostMapping("/login")
public String login(@RequestBody User user) {
TODO..
}
Copy the code
However, the @Crossorigin annotated source code means that it can only be configured cross-domain for a single interface, that is, locally cross-domain. Although it is easier to use such as the Filter Filter, this is clearly not what we want and is rarely used in actual development.
Implement the WebMvcConfigurer interface
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/ * *")
// Indicate which domains are allowed to access. A simpler option is *
.allowedOrigins("http://localhost:3000")
.allowedHeaders("*")
.allowedMethods("*")
AllowCredentials (true): indicates the credentials
// Once the allowCredentials(true) method is used, allowedOrigins("*") needs to specify a specific domain, not *
.allowCredentials(true)
.maxAge(86400); }}Copy the code
This works fine when you don’t have an Interceptor defined, but if you have a global Interceptor, such as one that detects user logins:
public class AuthenticationInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
Fetch the token from the HTTP request header
String token = httpServletRequest.getHeader("token");
// Check whether you are logged in
if (token == null) {
throw new InvalidTokenException(ResultCode.INVALID_TOKEN.getCode(), "Login information has expired. Please log in again.");
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}}Copy the code
When the custom interceptor returns true, everything is fine, but when the interceptor throws an exception (or returns false), subsequent CORS configurations will not take effect.
Why does the interceptor throw exception CORS not take effect? Check out this proposed issue on GitHub:
when interceptor preHandler throw exception, the cors is broken #9595
The general content is as follows:
Someone submitted an issue stating that the global CORS configuration set up through CorsRegistry is invalidated if an exception is thrown in the preHandler method of a custom interceptor, but Spring Boot members do not consider this a BUG🐛.
The submitter then gave a specific example:
He first defines CorsRegistry and adds a custom interceptor that throws an exception:
Then find AbstractHandlerMapping when adding CorsInterceptor is add the interceptor of Cors at the end of the interceptor chain:
That can cause the problem described above: After a custom interceptor throws an exception, the CorsInterceptor interceptor doesn’t have a chance to set the CORS related response header to the Response.
Issuer also suggests that the CorsInterceptor, the interceptor used to handle CORS, should be placed at the top of the interceptor chain:
This way, once the request comes in, the first interceptor sets the corresponding CORS response header for the response (for example: Access-control-allow-origin, access-Control-allow-methods, access-Control-allow-headers, Headers, Headers, Headers, access-Control-allow-headers
It felt like a viable solution, but the Spring Boot members decided that it was not a Spring Boot Bug, but a Spring Framework Bug, and closed the issue.
Inject the CorsFilter
The problem of interceptors colliding with global cross-domain configurations can be avoided by using filters. The code is as follows:
@Configuration
public class CorsFilterConfiguration {
@Bean
public CorsFilter corsFilter(a) {
// Add the configuration after creating the CorsConfiguration object
CorsConfiguration corsConfiguration = new CorsConfiguration();
// Sets which raw fields to allow
corsConfiguration.addAllowedOrigin("*");
// Which original request headers are allowed
corsConfiguration.addAllowedHeader("*");
// Which request methods are allowed
corsConfiguration.addAllowedMethod("*");
// Add a mapping path
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/ * *", corsConfiguration);
return newCorsFilter(source); }}Copy the code
⭐ Why do filters avoid collisions but interceptors don’t?
Because the filter relies on the Servlet container, it can filter almost any request based on function callbacks. Interceptors rely on Web frameworks (such as the Spring MVC framework) and are implemented via AOP based on reflection.
The triggering sequence is as follows:
Filters are triggered before interceptors, but if there are multiple filters, CorsFilter needs to be set to the first filter.
Solve the CORS cross-domain other Angle | train of thought
The server supports CORS
🤨 What are you expecting?
Spring Boot solves cross-domain problems (#Spring Boot Solves cross-domain problems) is how to implement CORS cross-domain resource sharing on the server side.
The above four methods are effective! If you have any questions, see the comments section.
😉 If you are using the Spring Framework instead of Spring Boot, I also found an official document for your reference: CORS Support in Spring Framework
JSONP
Web pages cross domains to get JSON data dynamically generated from other sources, taking advantage of the
Note: There are three tags that themselves allow resources to be loaded across domains.
<img src="xxx">
<link href="xxx">
<script src="xxx">
JSONP, like AJAX, is a way for a client to send a request to the server to get data from the server. But AJAX is a same-origin policy, and JSONP is a non-same-origin policy (supporting cross-domain requests).
Advantages: Simple and compatible, it can be used to solve the problem of cross-domain data access in mainstream browsers.
Disadvantages: Only support GET method, limitations, insecure may be exposed to XSS attacks.
Reverse proxy server
The same origin policy is the standard that browsers need to follow, but not if the server is making requests to the server. Therefore, the reverse proxy server can effectively solve the cross-domain problem. The proxy server needs to do the following:
- Accept client requests
- Forwards the request to the actual server
- Returns the server’s response to the client
Nginx is a similar reverse proxy server, cross-domain by configuring the Nginx proxy to solve cross-domain problems.
Reference
- Developer.mozilla.org/zh-CN/docs/…
- www.cnblogs.com/lenve/p/105…
- www.ruanyifeng.com/blog/2016/0…
- Juejin. Cn/post / 700057…
Don’t limit your imagination to what you can do now!