SpringBoot跨域请求解决方案详细分析

news2024/11/20 6:13:02

跨域的定义

  • 跨域是指不同域名之间的相互访问,这是由浏览器的同源策略决定的,是浏览器对JavaScript施加的安全措施,防止恶意文件破坏。
  • 同源策略:同源策略是一种约定,它是浏览器最核心的也是最基本的安全策略,如果缺少了同源策略,则浏览器的正常功能可能会受到影响。所谓同源就是说协议域名端口号完全一致,有一个不一致就会造成跨域问题。
  • **跨域原理:**跨域请求能正常发出去,服务端能接受到请求并正常返回结果,只是结果被拦截了。
    在这里插入图片描述

跨域测试

新建项目demo-producer,用于接收和处理跨域请求。

@RestController
@RequestMapping("/testCors")
public class CorsOriginController {

    //可以对方法运用该注解
    //@CrossOrigin(origins = "http://127.0.0.1:8093")
    @GetMapping("/getString")
    public String getString() {
        return "跨域成功!";
    }

}

新建项目demo-consumer,用于发送跨域请求。在resources 文件夹里面的static 新建一个index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>
<body>
<div id="test"></div>
<input type="button" value="测试数据" onclick="getString()"/>
</body>
<script>

    function getString() {
        $.ajax({
            url:'http://localhost:8092/testCors/getString',
            type:'get',
            data:{},
            success:function (msg) {
                $("#test").html(msg);
            }
        })
    }
</script>
</html>

访问http://127.0.0.1:8093/,点击测试数据按钮,当页面展示跨域成功!,则代表跨域请求可以成功访问。

解决方案

添加CrossOrigin注解

    //可以对方法运用该注解
    @CrossOrigin(origins = "http://127.0.0.1:8093")
    @GetMapping("/getString")
    public String getString() {
        return "跨域成功!";
    }

CrossOrigin原理分析

AbstractHandlerMethodMapping.MappingRegistry#register,加载每个独立的handlerMappingCorsConfiguration,存放到corsLookup

		public void register(T mapping, Object handler, Method method) {
			this.readWriteLock.writeLock().lock();
			try {
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				validateMethodMapping(handlerMethod, mapping);

				Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
				for (String path : directPaths) {
					this.pathLookup.add(path, mapping);
				}

				String name = null;
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}

				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					corsConfig.validateAllowCredentials();
					this.corsLookup.put(handlerMethod, corsConfig);
				}

				this.registry.put(mapping,
						new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}

RequestMappingHandlerMapping#initCorsConfiguration,处理类和方法上的注解。

	@Override
	protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
		HandlerMethod handlerMethod = createHandlerMethod(handler, method);
		Class<?> beanType = handlerMethod.getBeanType();
		CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(beanType, CrossOrigin.class);
		CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);

		if (typeAnnotation == null && methodAnnotation == null) {
			return null;
		}

		CorsConfiguration config = new CorsConfiguration();
		updateCorsConfig(config, typeAnnotation);
		updateCorsConfig(config, methodAnnotation);

		if (CollectionUtils.isEmpty(config.getAllowedMethods())) {
			for (RequestMethod allowedMethod : mappingInfo.getMethodsCondition().getMethods()) {
				config.addAllowedMethod(allowedMethod.name());
			}
		}
		return config.applyPermitDefaultValues();
	}

AbstractHandlerMapping#getHandler,判断该请求经过的方法是否在corsLookup中可以获取到。

	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		Object handler = getHandlerInternal(request);
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}

		// Ensure presence of cached lookupPath for interceptors and others
		if (!ServletRequestPathUtils.hasCachedPath(request)) {
			initLookupPath(request);
		}

		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

		if (logger.isTraceEnabled()) {
			logger.trace("Mapped to " + handler);
		}
		else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
			logger.debug("Mapped to " + executionChain.getHandler());
		}

		if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
			CorsConfiguration config = getCorsConfiguration(handler, request);
			if (getCorsConfigurationSource() != null) {
				CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
				config = (globalConfig != null ? globalConfig.combine(config) : config);
			}
			if (config != null) {
				config.validateAllowCredentials();
			}
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}

		return executionChain;
	}

AbstractHandlerMapping#getCorsHandlerExecutionChain,增加拦截器,CorsInterceptor

protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
      HandlerExecutionChain chain, @Nullable CorsConfiguration config) {

   if (CorsUtils.isPreFlightRequest(request)) {
      HandlerInterceptor[] interceptors = chain.getInterceptors();
      return new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
   }
   else {
      chain.addInterceptor(0, new CorsInterceptor(config));
      return chain;
   }
}

AbstractHandlerMapping.CorsInterceptor#preHandle,拦截请求。

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   // Consistent with CorsFilter, ignore ASYNC dispatches
   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
   if (asyncManager.hasConcurrentResult()) {
      return true;
   }

   return corsProcessor.processRequest(this.config, request, response);
}

DefaultCorsProcessor#processRequest,核心的拦截逻辑,在response里面添加一系列的请求头。

    public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request, HttpServletResponse response) throws IOException {
        Collection<String> varyHeaders = response.getHeaders("Vary");
        if (!varyHeaders.contains("Origin")) {
            response.addHeader("Vary", "Origin");
        }

        if (!varyHeaders.contains("Access-Control-Request-Method")) {
            response.addHeader("Vary", "Access-Control-Request-Method");
        }

        if (!varyHeaders.contains("Access-Control-Request-Headers")) {
            response.addHeader("Vary", "Access-Control-Request-Headers");
        }

        if (!CorsUtils.isCorsRequest(request)) {
            return true;
        } else if (response.getHeader("Access-Control-Allow-Origin") != null) {
            logger.trace("Skip: response already contains \"Access-Control-Allow-Origin\"");
            return true;
        } else {
            boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
            if (config == null) {
                if (preFlightRequest) {
                    this.rejectRequest(new ServletServerHttpResponse(response));
                    return false;
                } else {
                    return true;
                }
            } else {
                return this.handleInternal(new ServletServerHttpRequest(request), new ServletServerHttpResponse(response), config, preFlightRequest);
            }
        }
    }

    protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response, CorsConfiguration config, boolean preFlightRequest) throws IOException {
        String requestOrigin = request.getHeaders().getOrigin();
        String allowOrigin = this.checkOrigin(config, requestOrigin);
        HttpHeaders responseHeaders = response.getHeaders();
        if (allowOrigin == null) {
            logger.debug("Reject: '" + requestOrigin + "' origin is not allowed");
            this.rejectRequest(response);
            return false;
        } else {
            HttpMethod requestMethod = this.getMethodToUse(request, preFlightRequest);
            List<HttpMethod> allowMethods = this.checkMethods(config, requestMethod);
            if (allowMethods == null) {
                logger.debug("Reject: HTTP '" + requestMethod + "' is not allowed");
                this.rejectRequest(response);
                return false;
            } else {
                List<String> requestHeaders = this.getHeadersToUse(request, preFlightRequest);
                List<String> allowHeaders = this.checkHeaders(config, requestHeaders);
                if (preFlightRequest && allowHeaders == null) {
                    logger.debug("Reject: headers '" + requestHeaders + "' are not allowed");
                    this.rejectRequest(response);
                    return false;
                } else {
                    responseHeaders.setAccessControlAllowOrigin(allowOrigin);
                    if (preFlightRequest) {
                        responseHeaders.setAccessControlAllowMethods(allowMethods);
                    }

                    if (preFlightRequest && !allowHeaders.isEmpty()) {
                        responseHeaders.setAccessControlAllowHeaders(allowHeaders);
                    }

                    if (!CollectionUtils.isEmpty(config.getExposedHeaders())) {
                        responseHeaders.setAccessControlExposeHeaders(config.getExposedHeaders());
                    }

                    if (Boolean.TRUE.equals(config.getAllowCredentials())) {
                        responseHeaders.setAccessControlAllowCredentials(true);
                    }

                    if (preFlightRequest && config.getMaxAge() != null) {
                        responseHeaders.setAccessControlMaxAge(config.getMaxAge());
                    }

                    response.flush();
                    return true;
                }
            }
        }
    }

实现WebMvcConfigurer

/**
 * @author 全栈学习笔记
 * @date 2020/4/21 12:04
 * @description
 */
public class MyWebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/testCross/**")
                .allowedHeaders("*")
                .allowedMethods("*")
                .allowCredentials(true)
                .allowedOrigins("http://localhost:8093")
                .maxAge(2000);
    }
}

过滤器配置

@Configuration
public class Filter {
    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://localhost:8093");//*表示允许所有
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/191238.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据产品】缓存设计

背景&#xff1a;为什么需要做缓存&#xff1f; 我所做的产品的指标设计越来越复杂&#xff0c;查询性能也随之下降。因此需要增加缓存层&#xff0c; 以提高接口查询效率。 哪些层需要做缓存&#xff1f; 随着指标系统的应用&#xff0c;该产品的查询逻辑也越来越简单&…

二分查找核心思路--单调性--极值

在最初的二分查找中&#xff0c;我们将一组数据按大小排序&#xff0c;然后根据arr[mid]与要查找的k的大小比较&#xff0c;从而每次去掉一半的数字&#xff0c;使时间复杂度简化为O&#xff08;logN&#xff09;。 排序本质上是让数据的单调性统一&#xff0c;变为单增或单减…

spring中的JSR-303统一校验

1.在前后端的传输参数的过程中数据在何处校验? 在前后端都需要进行校验,只是分工不同. 2.各个层的校验内容: 1.Controller层主要负责校验残水的合法性,包括: 必填的参数字段,数据格式的校验 2.Service层的业务校验是审核业务中的规则的相关内容,比如:课程已经审核通过所以提…

vue3 为何比 vue2 快

vue3 为何比 vue2 快 测试环境&#xff1a;https://vue-next-template-explorer.netlify.app/ 1、proxy 响应式 vue3 优缺点&#xff1a; 深度监听性能更好可监听 新增 / 删除 属性可监听数组变化Proxy 能规避 Object.defineProxy 的问题Proxy 无法兼容所有浏览器&#xff…

OAuth2介绍

目录 一、什么是OAuth2 二、OAuth2中的角色 三、认证流程 四、令牌的特点 五、OAuth2授权方式 授权码 隐藏方式 密码方式 凭证方式 一、什么是OAuth2.0 概念&#xff1a;第三方授权解决方案 OAuth2.0是目前使用非常广泛的授权机制&#xff0c;用于授权第三方应用获取…

[NRF52] mesh DFU

mesh DFU升级过程&#xff1a;完整流程&#xff1a;以前nRF SDK DFU的实现是通过nRF51 Dongle配合主机nRF connect工具&#xff0c;且借助Secure DFU的后台式更新速率较快&#xff08;见另一篇笔记&#xff09;&#xff0c;现在的nRF mesh DFU分角色&#xff0c;全都由DK充当&a…

什么是单体应用?什么是微服务?

Monolith&#xff08;单体应用&#xff09;&#xff0c; 也称之为单体系统或者是 单体架构 。就是一种把系统中所有的功能、模块、组件等耦合在一个应用中应用最终打成一个(war,jar)包使用一个容器(Tomcat)进行部署&#xff0c;通常一个应用享用一个数据库。 也就是将所有的代码…

Java版数据结构与算法笔记

文章目录一、数据结构与算法概述及题目1、数据结构和算法的关系2、线性结构与非线性结构Ⅰ-线性结构Ⅱ-非线性结构3、经典面试题Ⅰ-字符串匹配问题&#xff1a;Ⅱ-汉诺塔游戏Ⅲ-八皇后问题:Ⅳ-马踏棋盘算法4、几个实际编程中遇到的问题Ⅰ-字符串替换问题Ⅱ-一个五子棋程序Ⅲ-约…

这家芯片企业,从创立之初就用 Authing 管理身份

在德州仪器和苹果的经验&#xff0c;让我深知统一身份管理要从 Day 1 做起。——Alpha Cen 联合创始人 & CEO 王璠 案例亮点&#xff1a; 打通 2000 主流应用、满足芯片初创企业统一身份需求 一周快速上线&#xff0c;产品开箱即用&#xff0c;后续无需费力运维 基于协同…

Pytorch安装及环境配置详细教程(CUDA版本)

文章目录前言一、查看GPU支持的CUDA版本二、安装CUDA三、确定torch、torchvision和python版本四、安装anaconda五、安装torch和torchvision前言 安装cuda版本的pytorch时踩了不少坑&#xff0c;网上安装pytorch的版本很多&#xff0c;一般的教程都是到pytorch的官网&#xff0…

相比传统专线网络,爱快、飞连等主流SD-WAN方案好在哪里?

当企业发展至一定的规模后&#xff0c;移动办公、异地办公会逐渐成为刚需&#xff0c;这就对企业的网络架构提出了新的挑战。 一般来说&#xff0c;企业在发展中所遇到的这些新增的网络需求&#xff0c;有两种比较常见的解决方案&#xff0c;其中之一是专线连接 。专线虽然简单…

【Unity3D插件】Build Report Tool插件,Build报告,优化包体,查看资源占用

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 本篇文章介绍一下Build Report Tool插件的使用。 Build Repor…

在Visual Studio中调试Linux控制台程序

最近&#xff0c;Visual Studio 2022 17.5 预览版3 面向广大 Linux 开发者提供了一项新特性&#xff1a;在 Visual Studio 集成的终端中调试 C Linux 控制台程序。 如需了解更多关于集成终端的功能以及它在 Visual Studio 中的工作原理&#xff0c;请参考我们之前的一篇文章: I…

Linux系统安装Hadoop步骤详解

一、 安装新的虚拟机。 配置根据电脑内存自由处理&#xff0c;一般来说&#xff0c;虚拟机内存设为电脑内存一半 二、打开虚拟机&#xff0c;设置语言&#xff0c;时区&#xff0c;硬盘分区&#xff0c;密码&#xff0c;用户等 三、修改ip地址&#xff0c;连接xshell 3.1关闭…

Acer传奇Go电脑开机几分钟就会蓝屏怎么重装系统?

Acer传奇Go电脑开机几分钟就会蓝屏怎么重装系统&#xff1f;有用户使用的Acer传奇Go电脑出现了一些系统故障&#xff0c;电脑正常开机使用几分钟之后&#xff0c;马上就会出现蓝屏的情况&#xff0c;导致无法正常的使用。那么这个情况如何去重装系统呢&#xff1f;来看看以下的…

在产业互联网时代,以生态和边界为代表的有限市场的瓜分业已完成

在这样一个过程中&#xff0c;阿里们更多地思考的是&#xff0c;如何与产业结合&#xff0c;而非独立于产业之外&#xff0c;仅仅只是做一个旁观者和第三方。无论是它们投身到物流、制造、能源化工等行业之中&#xff0c;还是它们对这些产业的传统玩家们深度赋能&#xff0c;几…

第三章 TCG 规范解读【嵌入式工作组】【工业工作组】

可信计算组织&#xff08;Ttrusted Computing Group,TCG&#xff09;是一个非盈利的工业标准组织&#xff0c;它的宗旨是加强在相异计算机平台上的计算环境的安全性。TCG于2003年春成立&#xff0c;并采纳了由可信计算平台联盟&#xff08;the Trusted Computing Platform Alli…

Elasticsearch:彻底理解 Elasticsearch 数据操作

Elasticsearch 中有四种基本的数据操作。 每个操作都有自己的资源需求。每个用例都使用这些操作&#xff0c;但它们会优先于某些操作。 Index&#xff1a;在这里被用做动词&#xff0c;而不是其名词索引。处理文档并将其存储在索引中以供将来检索。Delete&#xff1a;从索引中…

【攻防世界】easyphp

废话不多说&#xff0c;打开环境就是一大段的php代码&#xff0c;对于我一个小白来说&#xff0c;还是难阿&#xff01;只能一点点琢磨构造payload的思路&#xff0c;当然也是在网上参考了别人的wp。记录下来也为了加深自己的印象&#xff0c;有事没事都能翻一翻&#xff0c;温…

快速入门OAuth2

1. 什么是OAuth2.0OAuth2.0是目前使用非常广泛的授权机制&#xff0c;用于授权第三方应用获取用户的数据。举例说明&#xff1a;用户可以通过选择其他登录方式来使用gitee&#xff0c;这里就使用到了第三方认证。来自RFC 6749OAuth 引入了一个授权层&#xff0c;用来分离两种不…