5.跨域处理

简介: 本文介绍Web开发中的跨域问题及解决方案。当协议、域名或端口不同时,浏览器因同源策略阻止资源访问。通过CORS(跨域资源共享)机制,使用`@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秒内,不需要再发送预检验请求,可以缓存该结果
         }
     };
    
    }
    }
    ● 测试,在允许访问的域名https://www.dustyblog.cn/控制台输入(注意,这里请求的是http://127.0.0.1:8080/demo3):
    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月前
|
人工智能 NoSQL 前端开发
Chap03. SpringAI
SpringAI整合多款主流大模型,支持对话、函数调用与RAG等架构,提供统一API简化开发。通过ChatClient封装交互,结合Prompt工程、工具调用与知识检索,可快速构建智能客服、哄哄模拟器、ChatPDF等应用,并支持多模态与持久化扩展,助力AI应用高效落地。
|
2月前
|
存储 算法 索引
二叉树基础及常见类型
二叉树是数据结构的核心,既是红黑树、堆、字典树等复杂结构的基础,也体现递归思维,贯穿回溯、动态规划等算法。掌握二叉树,等于掌握算法之魂。本文详解其类型、性质与实现,助你彻底精通。
|
2月前
|
安全 Java 数据安全/隐私保护
2.通用权限管理模型
本文介绍ACL、RBAC等常见权限模型。ACL通过对象与用户/角色的多对多授权实现控制;RBAC则基于角色分配权限,解耦用户与权限关系,提升管理效率。RBAC分为RBAC0至RBAC3,逐步引入角色继承与职责分离机制,增强安全性与灵活性。了解模型结构有助于构建清晰的权限体系。(238字)
|
2月前
|
数据采集 领域建模 数据库
领域模型图(数据架构/ER图)
数据架构核心输出为ER图,包含实体、关系与属性。通过四色原型法进行领域建模:红色MI表时序事件,绿色PPT为业务对象,黄色Role示参与角色,蓝色DESC供描述信息。以风控系统为例,从业务流程提炼MI,构建PPT实体,补充Role与DESC,最终提取含一对一、一对多等约束的ER图,实现从业务到数据模型的转化。(239字)
|
2月前
|
运维 Kubernetes Java
物理部署图
物理部署图从运维视角展示系统运行时的硬件配置与软件部署结构,用于理解分布式系统。包含节点、构件、物件、连接和框架五大元素,描述应用如何在硬件上部署及相互协作,是实现开发与运维协同的重要工具。
|
2月前
|
Java 应用服务中间件 网络安全
Eclipse运行SSM/SSH项目教程
本教程介绍如何在Eclipse中配置并运行Java Web项目。涵盖JDK、Tomcat环境搭建,项目导入与Maven配置,Eclipse中绑定Tomcat服务器并部署项目,最后通过本地地址访问应用,附常见问题如数据库连接修改等说明。
|
2月前
|
Java 大数据
ArrayList扩容机制
ArrayList 添加元素时,先调用 `ensureCapacityInternal` 检查容量,首次添加时默认扩容至 10。每次扩容为原容量的 1.5 倍(通过位运算 `oldCapacity >> 1` 实现),确保添加高效。`modCount` 记录结构修改次数,`size()` 返回元素个数,`length` 用于数组,`length()` 用于字符串。
|
2月前
|
Java
1.常见加载顺序
本示例展示了Java中各类代码块的执行顺序:静态代码块随类加载执行且仅一次;局部代码块在方法内按顺序执行;构造代码块每次创建对象前执行;构造器随后执行。输出结果体现其优先级与调用时机,清晰呈现初始化流程。
|
2月前
|
存储 Java 编译器
Java泛型类型擦除以及类型擦除带来的问题
Java泛型在编译时会进行类型擦除,所有泛型信息被移除,仅保留原始类型(如Object或限定类型)。例如,List<String>和List<Integer>在运行时均为List,导致无法通过instanceof判断泛型类型。类型检查在编译期完成,基于引用而非对象本身。擦除后的方法调用通过桥接方法和自动强转实现多态兼容。静态成员不能使用类的泛型参数,因泛型实例化依赖对象创建,而静态上下文无此支持。
|
2月前
|
缓存 算法 Java
线程池
本文深入剖析Java线程池实现原理,涵盖ThreadPoolExecutor与ScheduledThreadPoolExecutor核心机制,解析线程复用、任务队列、拒绝策略及周期性调度的底层逻辑,并对比Executors工具类各类线程池适用场景。

热门文章

最新文章