SpringBoot解决跨域的超实用方案分享
作者:天罡gg
一、验证跨域
模拟前端地址:http://localhost:8080/index.html
后端接口地址:http://localhost:8081/auth/test/cors
思路:启动两个springboot项目:8080端口和8081端口,在8080端口的静态资源目录index.html中,对8081端口的接口发起请求,因为端口不同,所以属于跨域,从而就可以验证不同请求(
POST或OPTIONS等方法
)是否报CORS error !
1. 添加index.html
在 tg-book-web的resources下,新建static\index.html
逻辑:使用ajax发送请求,调用8081的/auth/test/cors接口,如果执行成功,将结果输出到id="result"的div下,如下代码是post
请求,后面还会改成options
请求测试.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js"></script> </head> <body> <div id="result"> </div> </body> <script type="text/javascript"> $.ajax({ url: 'http://localhost:8081/auth/test/cors', method: 'post', success: function (res) { console.log(res) $('#result').append('<p>' + res['message'] + '</p>') } }) </script> </html>
对index.html不进行接口身份认证
因为我们增加了身份认证AuthInterceptor,所以需要在AuthInterceptor的preHandle过滤掉index.html,如下:
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("AuthInterceptor.preHandle:" + request.getServletPath()); if (request.getServletPath().startsWith("/index.html")) { return true; } ...其它代码 }
2. 增加/auth/test/cors接口
AuthController下增加接口
@PostMapping("/test/cors") public TgResult<String> testCors() { return TgResult.ok(); }
同样,将/auth/test/cors也不进行接口身份认证.
排除身份认证是为了更好的测试跨域,所以同样写在排除"/auth/login"的位置.
registry.addInterceptor(authInterceptor) .addPathPatterns("/**") // 排除的请求路径 .excludePathPatterns("/auth/login", "/auth/test/cors");
3. IDEA启动多个springboot项目
关于如何使用IDEA启动多个springboot项目,我就不演示了,我启动了8080和8081两个端口:
4. 验证POST方法
在浏览器打开 http://localhost:8080/index.html,如下图:
可以看到,8080/index.html通过ajax请求了8081的接口,返回200,说明支持POST请求跨域!
5. 验证OPTIONS方法
OK,正如我开头所说,@CrossOrigin注解对options方法并没有支持跨域
,我们来试一试!
只需将index.html中的 【 method: ‘post’】改为【 method: ‘options’】,重启8080服务,然后用同样的方法测试,如下,报CORS error了 🤣
详细报错:
二、拦截器方案
SpringBoot中使用拦截器需要2步:
- 定义拦截器
- 注册拦截器,并指定拦截规则
定义拦截器
新增一个拦截器类 CorsInterceptor
实现 HandlerInterceptor 接口
package org.tg.book.web.interceptor;
@Component public class CorsInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 允许跨域源,全部允许为*,否则写允许请求的源ip if (StringUtils.isEmpty(request.getHeader("Origin"))) { response.setHeader("Access-Control-Allow-Origin", "*"); } else { response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); } response.setHeader("Access-Control-Allow-Credentials", "true"); // 允许跨域方法:全部允许为*,否则写具体的方法,例如:GET, POST, PUT, DELETE, OPTIONS response.setHeader("Access-Control-Allow-Methods", "*"); response.setHeader("Access-Control-Max-Age", "86400"); // 允许跨域请求头 response.setHeader("Access-Control-Allow-Headers", "*"); // 如果是OPTIONS,不走后续操作,直接返回 if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); return false; } return true; } }
- Access-Control-Allow-Origin: 允许跨域源,全部允许为*,否则写允许请求的源ip。重点:通常会使用白名单机制,只有request.getHeader(“Origin”) 在白名单中才允许跨域。
- Access-Control-Allow-Methods:允许跨域方法:全部允许为*,否则写具体的方法,例如:GET, POST, PUT, DELETE, OPTIONS
注册拦截器,并指定拦截规则
在原有的InterceptorConfig
中增加注入CorsInterceptor ,并将【跨域拦截器】 放在 【身份认证拦截器】之前,如下:
@Configuration public class InterceptorConfig implements WebMvcConfigurer { @Autowired private CorsInterceptor corsInterceptor; @Autowired private AuthInterceptor authInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { // 新增的跨域拦截器 registry.addInterceptor(corsInterceptor) .addPathPatterns("/**"); // 身份认证拦截器 registry.addInterceptor(authInterceptor) .addPathPatterns("/**") // 排除的请求路径 .excludePathPatterns("/auth/login"); } }
去除原来的 @CrossOrigin注解
,然后验证,同样,完全支持POST和OPTIONS方法!
三、过滤器方案
新增 CorsFilter
实现Filter
接口
public class CorsFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; if (StringUtils.isEmpty(request.getHeader("Origin"))) { response.setHeader("Access-Control-Allow-Origin", "*"); } else { response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); } response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "*"); response.setHeader("Access-Control-Max-Age", "86400"); response.setHeader("Access-Control-Allow-Headers", "*"); // 如果是OPTIONS,不走后续操作,直接返回 if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); return; } chain.doFilter(request, response); } @Override public void destroy() { } }
新增配置类CorsConfig
注入 CorsFilter
@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { return new CorsFilter(); } }
注意:测试时先注释上面的 跨域拦截器
,然后验证,完全支持POST和OPTIONS方法!
总结
今天我使用了两种方案解决跨域:拦截器和过滤器方案,都是超实用的方案。另外,给你留个作业吧,都做成配置扩展,而不是hard code,例如上面说到的白名单机制,实际就是配置一堆ip,其它也是同理,你可以把你的配置扩展写在留言区里,或者遇到的问题在留言区和我一起讨论,感谢你的阅读,也欢迎你把这篇文章分享给更多的朋友一起阅读。
以上就是SpringBoot解决跨域的超实用方案分享的详细内容,更多关于SpringBoot解决跨域的资料请关注脚本之家其它相关文章!