Now I am taking over a project in the school network center. Based on the actual situation and development needs of the team members, the teacher hopes to completely separate the front and back ends. The backend uses java to provide restful API as the core, and the frontend can share the same core regardless of PC or mobile terminal. In the early stage, problems such as oauth2 as an authorization mechanism were solved, and I thought it would be a great success. (Recently I plan to introduce in detail the solution of Spring sercurity to implement oauth2) As a result, another cross-domain problem occurred, which made us step into a big pit. We will record it here to avoid future troubles.
The error message is as follows:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403.
What is cross-domain?
Simply put, it means that the browser restricts access to the js code under site A and makes an ajax request to the url under site B. For example, if the front-end domain name is www.abc.com, then the js code running in the current environment is restricted from accessing resources under the www.xyz.com domain name for security reasons. Modern browsers will block cross-domain ajax requests by default for security reasons. This is a necessary feature in modern browsers, but it often brings inconvenience to development. Especially for a backend developer like me, this thing is simply magical.
But the need for cross-domain has always been there. In order to cross-domain, hard-working and brave programmers have come up with many methods, such as jsonP, proxy files, etc. However, these practices add a lot of unnecessary maintenance costs, and there are many limitations in application scenarios. For example, jsonP is not XHR, so jsonP can only use GET to pass parameters. More detailed information can be found here. Summary of cross-domain access solutions for web applications
CORS protocol
Nowadays, JS has a tendency to dominate the world, and the browser has become the best place for most applications. Even on the mobile side, there are various Hybird solutions. On the Web page of the local file system, there are also needs to obtain external data, and these needs must be cross-domain. When looking for a cross-domain solution, I found that the most elegant solution is the new feature of "Cross-Origin Resource Sharing" brought by HTML5, which gives developers the power to decide whether resources are allowed to be accessed across domains.
CORS is a W3C standard, its full name is "Cross-origin resource sharing".
It allows the browser to issue XMLHttpRequest requests to cross-origin servers, thereby overcoming the limitation that AJAX can only be used from the same origin.
Why do you say it is elegant?
The entire CORS communication process is automatically completed by the browser and does not require user participation. For developers, there is no difference between CORS communication and AJAX communication from the same source, and the code is exactly the same. Once the browser discovers that the AJAX request is cross-origin, it will automatically add some additional header information, and sometimes an additional request will be made, but the user will not feel it.
Therefore, the key to achieving CORS communication is the server. As long as the server implements the CORS interface, it can communicate across sources.
The key to solving this problem falls on me, the programmer responsible for the backend.
It’s not difficult to look at the documentation, but you need to set Access-Control-Allow-Origin in the http header to determine which sites need to be allowed to access. For more details about the CROS protocol, please refer to Cross-domain Resource Sharing CORS Detailed explanation
CROS common headers
CORS has the following common headers
Access-Control-Allow-Origin: http://kbiao.me Access-Control-Max-Age: 3628800 Access-Control-Allow-Methods: GET,PUT, DELETE Access-Control-Allow-Headers: content-type
"Access-Control-Allow-Origin" indicates that it allows "http://kbiao.me" Initiating a cross-domain request
"Access-Control-Max-Age" indicates that within 3628800 seconds, there is no need to send a pre-inspection request and the result can be cached (from the above information, we know that in the CROS protocol, an AJAX request is divided into The first step of OPTION pre-detection request and formal request)
"Access-Control-Allow-Methods" indicates that it allows GET, PUT, DELETE external domain requests
"Access-Control-Allow-Headers" indicates that it allows cross-domain requests The request contains the content-type header
Of course, there are many pitfalls in the front-end when dealing with this problem, especially the Chrome browser does not allow cross-domain requests from localhost. For detailed solutions, please see the partner responsible for the front-end solution in my project. Pretend there is a link
General solution
I know the cause of the problem and the supporting solution, let us solve it now. The idea is very simple. When the front end requests cross-domain resources, we just add the response header to it. Obviously it is easiest for us to define a filter ourselves.
/** * Created by kangb on 2016/5/10. */ @Component public class myCORSFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) servletResponse; String origin = (String) servletRequest.getRemoteHost()+":"+servletRequest.getRemotePort(); response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization"); response.setHeader("Access-Control-Allow-Credentials","true"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }
@Component 是Spring的注解,关键部分在doFilter中,添加了我们需要的头, option 是预检测需要所以需要允许, Authorization 是做了oauth2登录响应所必须的, Access-Control-Allow-Credentials 表示允许cookies。都是根据自己项目的实际需要配置。
再配置Web.xml使得过滤器生效
<filter> <filter-name>cors</filter-name> <filter-class>·CLASS_PATH·.myeCORSFilter</filter-class> </filter> <filter-mapping> <filter-name>cors</filter-name> <url-pattern>/api/*</url-pattern> </filter-mapping>
接下来前端就可以像往常一样使用AJAX请求获得资源了,完全不需要做出什么改变。
SPRING 4中更优雅的办法
SpringMVC4提供了非常方便的实现跨域的方法。在requestMapping中使用注解。
@CrossOrigin(origins = “http://kbiao.me”)
全局实现 .定义类继承WebMvcConfigurerAdapter,设置跨域相关的配置
public class CorsConfigurerAdapter extends WebMvcConfigurerAdapter{ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/*").allowedOrigins("*"); } }
将该类注入到容器中:
<bean class="com.tmall.wireless.angel.web.config.CorsConfigurerAdapter"></bean>