跨域处理

简介: 本文介绍了跨域问题的背景及CORS解决方案。通过实例讲解Spring Boot中使用@CrossOrigin注解和全局配置WebMvcConfigurer实现跨域访问,并提供拦截器方式的配置代码,帮助开发者解决前后端分离架构中的跨域请求难题。

一、跨域背景

1.1 何为跨域?

Url的一般格式:

协议 + 域名(子域名 + 主域名) + 端口号 + 资源地址

示例:

https://www.dustyblog.cn:8080/say/Hello 是由

https + www + dustyblog.cn + 8080 + say/Hello

组成。

只要协议,子域名,主域名,端口号这四项组成部分中有一项不同,就可以认为是不同的域,不同的域之间互相访问资源,就被称之为跨域。

1.2 一次正常的请求

  • Controller层代码:
@RequestMapping("/demo")
@RestController
public class CorsTestController {
    @GetMapping("/sayHello")
    public String sayHello() {
        return "hello world !";
    }
}
  • 启动项目,测试请求

浏览器打开localhost:8080/demo/sayHello

可以打印出“hello world”

1.3 跨域测试

以Chrome为例:

  • 打开任意网站,如:https://blog.csdn.net
  • 按F12,打开【开发者工具】,在里面的【Console】可以直接输入js代码测试;
var token= "LtSFVqKxvpS1nPARxS2lpUs2Q2IpGstidMrS8zMhNV3rT7RKnhLN6d2FFirkVEzVIeexgEHgI/PtnynGqjZlyGkJa4+zYIXxtDMoK/N+AB6wtsskYXereH3AR8kWErwIRvx+UOFveH3dgmdw1347SYjbL/ilGKX5xkoZCbfb1f0=,LZkg22zbNsUoHAgAUapeBn541X5OHUK7rLVNHsHWDM/BA4DCIP1f/3Bnu4GAElQU6cds/0fg9Li5cSPHe8pyhr1Ii/TNcUYxqHMf9bHyD6ugwOFTfvlmtp6RDopVrpG24RSjJbWy2kUOOjjk5uv6FUTmbrSTVoBEzAXYKZMM2m4=,R4QeD2psvrTr8tkBTjnnfUBw+YR4di+GToGjWYeR7qZk9hldUVLlZUsEEPWjtBpz+UURVmplIn5WM9Ge29ft5aS4oKDdPlIH8kWNIs9Y3r9TgH3MnSUTGrgayaNniY9Ji5wNZiZ9cE2CFzlxoyuZxOcSVfOxUw70ty0ukLVM/78=";
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8080/demo/sayHello');
xhr.setRequestHeader("x-access-token",token);
xhr.send(null);
xhr.onload = function(e) {
    var xhr = e.target;
    console.log(xhr.responseText);
}
  • 输入完后直接按回车键就可以返回结果:
Access to XMLHttpRequest at 'http://127.0.0.1:8080/demo/sayHello' 
from origin 'https://blog.csdn.net' has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

该结果表明:该请求在https://blog.csdn.net域名下请求失败!

二、解决方案 - Cors跨域

2.1 Cors是什么

CORS全称为Cross Origin Resource Sharing(跨域资源共享), 每一个页面需要返回一个名为Access-Control-Allow-Origin的http头来允许外域的站点访问,你可以仅仅暴露有限的资源和有限的外域站点访问。

我们可以理解为:如果一个请求需要允许跨域访问,则需要在http头中设置Access-Control-Allow-Origin来决定需要允许哪些站点来访问。如假设需要允许https://www.dustyblog.c这个站点的请求跨域,则可以设置:

Access-Control-Allow-Origin:https://www.dustyblog.cn。

2.2 方案一:使用@CrossOrigin注解

2.2.1 在Controller上使用@CrossOrigin注解

该类下的所有接口都可以通过跨域访问

@RequestMapping("/demo2")
@RestController
//@CrossOrigin //所有域名均可访问该类下所有接口
@CrossOrigin("https://blog.csdn.net") // 只有指定域名可以访问该类下所有接口
public class CorsTest2Controller {
    @GetMapping("/sayHello")
    public String sayHello() {
        return "hello world --- 2";
    }
}

这里指定当前的CorsTest2Controller中所有的方法可以处理https://csdn.net域上的请求,这里可以测试一下:

  • https://blog.csdn.net页面打开调试窗口,输入(注意:这里请求地址是/demo2,请区别于1.2 案例中的/demo)
var token= "LtSFVqKxvpS1nPARxS2lpUs2Q2IpGstidMrS8zMhNV3rT7RKnhLN6d2FFirkVEzVIeexgEHgI/PtnynGqjZlyGkJa4+zYIXxtDMoK/N+AB6wtsskYXereH3AR8kWErwIRvx+UOFveH3dgmdw1347SYjbL/ilGKX5xkoZCbfb1f0=,LZkg22zbNsUoHAgAUapeBn541X5OHUK7rLVNHsHWDM/BA4DCIP1f/3Bnu4GAElQU6cds/0fg9Li5cSPHe8pyhr1Ii/TNcUYxqHMf9bHyD6ugwOFTfvlmtp6RDopVrpG24RSjJbWy2kUOOjjk5uv6FUTmbrSTVoBEzAXYKZMM2m4=,R4QeD2psvrTr8tkBTjnnfUBw+YR4di+GToGjWYeR7qZk9hldUVLlZUsEEPWjtBpz+UURVmplIn5WM9Ge29ft5aS4oKDdPlIH8kWNIs9Y3r9TgH3MnSUTGrgayaNniY9Ji5wNZiZ9cE2CFzlxoyuZxOcSVfOxUw70ty0ukLVM/78=";
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8080/demo2/sayHello');
xhr.setRequestHeader("x-access-token",token);
xhr.send(null);
xhr.onload = function(e) {
    var xhr = e.target;
    console.log(xhr.responseText);
}

返回结果:

ƒ (e) {
    var xhr = e.target;
    console.log(xhr.responseText);
}
VM156:8 hello world --- 2

说明跨域成功!

  • 换个域名测试一下看跨域是否还有效,在https://www.baidu.com按照上述方法测试一下,返回结果:
OPTIONS http://127.0.0.1:8080/demo2/sayHello 403
(anonymous)
Access to XMLHttpRequest at 'http://127.0.0.1:8080/demo2/sayHello' 
from origin 'http://www.cnblogs.com' has been blocked by CORS policy: 
Response to preflight request doesn't pass access control check: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

说明跨域失败!证明该方案成功指定了部分域名能跨域!

2.3 方案二:CORS全局配置-实现WebMvcConfigurer

  • 新建跨域配置类:CorsConfig.java:
/**
 * 跨域配置
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Bean
    public WebMvcConfigurer corsConfigurer()
    {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").
                        allowedOrigins("https://www.dustyblog.cn"). //允许跨域的域名,可以用*表示允许任何域名使用
                        allowedMethods("*"). //允许任何方法(post、get等)
                    allowedHeaders("*"). //允许任何请求头
                        allowCredentials(true). //带上cookie信息
                        exposedHeaders(HttpHeaders.SET_COOKIE).maxAge(3600L); //maxAge(3600)表明在3600秒内,不需要再发送预检验请求,可以缓存该结果
            }
        };
    }
}
var token= "LtSFVqKxvpS1nPARxS2lpUs2Q2IpGstidMrS8zMhNV3rT7RKnhLN6d2FFirkVEzVIeexgEHgI/PtnynGqjZlyGkJa4+zYIXxtDMoK/N+AB6wtsskYXereH3AR8kWErwIRvx+UOFveH3dgmdw1347SYjbL/ilGKX5xkoZCbfb1f0=,LZkg22zbNsUoHAgAUapeBn541X5OHUK7rLVNHsHWDM/BA4DCIP1f/3Bnu4GAElQU6cds/0fg9Li5cSPHe8pyhr1Ii/TNcUYxqHMf9bHyD6ugwOFTfvlmtp6RDopVrpG24RSjJbWy2kUOOjjk5uv6FUTmbrSTVoBEzAXYKZMM2m4=,R4QeD2psvrTr8tkBTjnnfUBw+YR4di+GToGjWYeR7qZk9hldUVLlZUsEEPWjtBpz+UURVmplIn5WM9Ge29ft5aS4oKDdPlIH8kWNIs9Y3r9TgH3MnSUTGrgayaNniY9Ji5wNZiZ9cE2CFzlxoyuZxOcSVfOxUw70ty0ukLVM/78=";
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8080/demo3/sayHello');
xhr.setRequestHeader("x-access-token",token);
xhr.send(null);
xhr.onload = function(e) {
    var xhr = e.target;
    console.log(xhr.responseText);
}

输出结果

ƒ (e) {
    var xhr = e.target;
    console.log(xhr.responseText);
}
VM433:8 hello world --- 3

说明跨域成功,换个网址如https://www.baidu.com测试依旧出现需要跨域的错误提示,证明该配置正确,该方案测试通过。

2.3 拦截器实现

通过实现Fiter接口在请求中添加一些Header来解决跨域的问题

@Component
public class CorsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.addHeader("Access-Control-Allow-Credentials", "true");
        res.addHeader("Access-Control-Allow-Origin", "*");
        res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
        res.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN");
        if (((HttpServletRequest) request).getMethod().equals("OPTIONS")) {
            response.getWriter().println("ok");
            return;
        }
        chain.doFilter(request, response);
    }
    @Override
    public void destroy() {
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
}

三、更多

3.1 源码地址

Github 示例代码


相关文章
|
2月前
|
存储 Dubbo API
SpringCloud工程部署启
本文介绍SpringCloud微服务工程的搭建与部署,涵盖项目创建、数据库配置、服务启动及远程调用实现,通过RestTemplate完成服务间通信,帮助理解微服务拆分与协作机制。
SpringCloud工程部署启
|
2月前
|
前端开发 程序员
常见注解及使用说明
本文介绍SpringMVC中@RequestMapping注解的作用及原理,讲解如何通过注解将HTTP请求映射到控制器方法,并列举@GetMapping等派生注解,帮助理解前后端接口的对应关系与实现方式。
 常见注解及使用说明
|
2月前
|
存储 运维 Java
微服务概述
本文对比单体与微服务架构,解析微服务定义、优缺点及技术实现。微服务通过拆分业务、独立部署、轻量通信提升系统扩展性与维护性,虽带来运维与分布式事务挑战,但仍是现代应用架构演进方向。
 微服务概述
|
2月前
|
SQL Java 数据库连接
持久层框架MyBatisPlus
MyBatisPlus是MyBatis的增强工具,简化单表CRUD操作,通过继承BaseMapper即可实现增删改查。支持条件构造器、分页插件、代码生成等功能,提升开发效率,广泛应用于企业级项目中。
 持久层框架MyBatisPlus
|
2月前
|
敏捷开发 Dubbo Java
需求开发人日评估
本文介绍了敏捷开发中工时评估的关键方法,重点讲解“人日”概念及开发、自测、联调、测试、发布各阶段的参考周期。结合常见需求如增删改查、Excel导入导出、远程调用等,提供实用的人日估算标准,并附详细拆分模板,助力团队科学排期。
需求开发人日评估
|
2月前
|
Java Shell 测试技术
Jmeter快速入门
本教程介绍JMeter的安装与快速入门。首先需安装JDK并配置环境变量,再下载解压JMeter,通过bin目录下的脚本启动。可设置中文界面并创建线程组、HTTP取样器及监听器,进行简单性能测试,掌握基本使用流程。
|
2月前
|
存储 负载均衡 算法
负载均衡算法
本文介绍了五种负载均衡算法:随机、轮询、最小活跃数、源地址哈希与一致性哈希。涵盖其原理、适用场景及实现代码,重点解析加权随机、加权轮询的调度策略,强调根据服务状态动态分配请求,保障系统负载均衡与稳定性。
 负载均衡算法
|
2月前
|
SQL 安全 网络协议
常见的网络攻击
恶意软件、网络钓鱼、中间人攻击、DDoS攻击、SQL注入、零日漏洞及DNS隧道是常见网络安全威胁。恶意软件通过漏洞入侵,窃取数据或破坏系统;网络钓鱼伪装可信来源骗取敏感信息;中间人攻击窃听通信;DDoS以海量流量瘫痪服务;SQL注入操控数据库;零日攻击利用未修复漏洞;DNS隧道则隐藏恶意流量。防范需多层安全策略。
 常见的网络攻击
|
2月前
|
安全 Java 数据库连接
OAuth2.0实战案例
本文介绍基于Spring Boot与Spring Cloud的OAuth2安全授权实现,涵盖父工程搭建、资源服务与授权服务配置,支持授权码、简化、密码及客户端四种模式测试,结合Security与MyBatis完成认证授权全流程。
OAuth2.0实战案例
|
2月前
|
存储 数据库
数据库设计三范式
本文介绍了数据库设计中的三大范式:第一范式(1NF)要求字段原子性、不可再分;第二范式(2NF)要求消除部分依赖,即主键能唯一确定所有非主键字段;第三范式(3NF)要求消除传递依赖。通过实例解析,说明范式旨在减少数据冗余与操作异常,但实际设计中应结合业务需求灵活应用,而非机械遵循。