前面已经基本介绍完了Spring Security的认证、授权流程,这一篇主要讲一下关于跨域的问题。
说明: 本系列的文章使用的是 springBoot 2.7.11 (springboot 2x 最后一个稳定版本),对应的 Spring Security 是 5.7.8
什么是跨域
常见的解释:
浏览器处于安全的考虑,使用 XMLHttpRequest 对象发起HTTP请求时必须遵守同源策略,否则就是跨域的HTTP请求,默认情况下是禁止的。
同源策略要求源相同才能正常通信,即协议、域名、端口号完全一致。
Form表单提交会跨域吗
答案是不会。
因为上面说的很清楚,XMLHttpRequest 对象发起HTTP请求时要遵循同源策略。
为什么form表单发起请求就不会有跨域问题呢?
这是因为使用XMLHttpRequest发起请求,也就是 ajax请求,请求结束之后,发起请求的页面是可以处理ajax返回的数据。而form表单提交请求时,页面会调整到目标域。
也就是说form提交请求后,原始页面是无法处理form表单的响应数据的,跟你发起页面都无关了
但是 ajax请求不同,当前页面是可以拿到ajax请求的响应数据并做相应处理。这就会有风险了。相当于你在自家院子开了个窗口往外拿苹果,因为院子里都是自家人,你本意是给院子里的自家人拿苹果。可如果不是自家院子的人也通过这个窗子跟你拿苹果。对你而言是不是有风险?
如何模拟跨域问题?
我将启动应用A ,然后访问应用A的jsp页面,在页面中使用ajax访问应用B的接口。
springBoot项目访问jsp页面
- 添加JSP模版引擎
<!--JSP模板引擎-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
- 添加url,返回指定视图
@Controller
@RequestMapping("/admin")
public class AdminController {
@RequestMapping("/test")
public ModelAndView test() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("index");
modelAndView.addObject("message", "world");
return modelAndView;
}
}
- 配置视图解析规则
spring:
mvc:
view:
suffix: .jsp
prefix: /
log-resolved-exception: true
server:
port: 8081
-
新建webapp
新建目录 src/main/webapp
-
新建jsp页面 在 src/main/webapp 下新增index.jsp文件
因为我习惯用json接收接口的请求数据,所以需要按图示的处理。
- 设置 Working directory 点 + 设置为 ,没有找到的话可以直接填入这个值即可
跨域问题演示
我们可以清晰地看到,从 localhost:8081 往 localhost:8080 发起请求时出错了。
解决跨域
设置 Cors 即可。
@Configuration
public class MvcSupportConfig extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/html/**")
//不需要再带 resources 这一层了,因为 resources 这个路径 == classpath
.addResourceLocations( "classpath:/html/");
}
// 配置跨域
@Override
protected void addCorsMappings(CorsRegistry registry) {
// 允许跨域的路径
registry.addMapping("/**")
// 允许跨域请求的域名
.allowedOriginPatterns("*")
// 是否允许cookie
.allowCredentials(true)
// 允许的请求方法
.allowedMethods("GET", "POST", "DELETE", "PUT")
// 允许的header属性
.allowedHeaders("*")
// 跨域允许时间
.maxAge(3600);
}
}
有些文档提到,说要去给 httpSecurity.cors();
@Bean
public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception {
//使用由httpSecurityConfiguration 装配的 HttpSecurity 则会紧密的保持原有的过滤器
http.
...
//允许跨域
// http.cors();
return http.build();
}
实际上是这个不是必要的了,具体原因大家看一下http.cors() 和 registry.addMapping就清楚了。
设置完成后即可跨越访问了。