Sping Security (四): 跨域

132 阅读3分钟

前面已经基本介绍完了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文件

image.png 因为我习惯用json接收接口的请求数据,所以需要按图示的处理。

  • 设置 Working directory 点 + 设置为 ModuleFileDirModuleFileDir ,没有找到的话可以直接填入这个值即可

image.png

跨域问题演示

image.png

我们可以清晰地看到,从 localhost:8081 往 localhost:8080 发起请求时出错了。

image.png

image.png

解决跨域

设置 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就清楚了。

设置完成后即可跨越访问了。

image.png