名词解释:跨域资源共享(Cross-Origin Resource Sharing)

简单说就是只要协议、IP、http方法任意一个不同就是跨域。
spring MVC自4.2开始添加了跨域的支持。
跨域具体的定义请移步mozilla查看
使用案例
spring mvc中跨域使用有3种方式:
在web.xml中配置CorsFilter
<filter> <filter-name>cors</filter-name> <filter-class>org.springframework.web.filter.CorsFilter</filter-class> </filter> <filter-mapping> <filter-name>cors</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
在xml中配置
// 简单配置,未配置的均使用默认值,就是全面放开 <mvc:cors> <mvc:mapping path="/**" /> </mvc:cors> // 这是一个全量配置 <mvc:cors> <mvc:mapping path="/api/**" allowed-origins="http://domain1.com, http://domain2.com" allowed-methods="GET, PUT" allowed-headers="header1, header2, header3" exposed-headers="header1, header2" allow-credentials="false" max-age="123" /> <mvc:mapping path="/resources/**" allowed-origins="http://domain1.com" /> </mvc:cors>
使用注解
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("http://domain2.com")
@RequestMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
}
涉及概念
涉及的java类:
封装信息的pojo
CorsConfiguration
存储request与跨域配置信息的容器
CorsConfigurationSource、UrlBasedCorsConfigurationSource
具体处理类
CorsProcessor、DefaultCorsProcessor
CorsUtils
实现OncePerRequestFilter接口的Adapter
CorsFilter
校验request是否cors,并封装对应的Adapter
AbstractHandlerMapping、包括内部类PreFlightHandler、CorsInterceptor
读取CrossOrigin注解信息
AbstractHandlerMethodMapping、RequestMappingHandlerMapping
从xml文件中读取跨域配置信息
CorsBeanDefinitionParser
跨域注册辅助类
MvcNamespaceUtils
debug分析
要看懂代码我们需要先了解下封装跨域信息的pojo--CorsConfiguration
这边是一个非常简单的pojo,除了跨域对应的几个属性,就只有combine、checkOrigin、checkHttpMethod、checkHeaders。
属性都是多值组合使用的。
// CorsConfiguration public static final String ALL = "*"; // 允许的请求源 private List<String> allowedOrigins; // 允许的http方法 private List<String> allowedMethods; // 允许的请求头 private List<String> allowedHeaders; // 返回的响应头 private List<String> exposedHeaders; // 是否允许携带cookies private Boolean allowCredentials; // 预请求的存活有效期 private Long maxAge;
combine是将跨域信息进行合并
3个check方法分别是核对request中的信息是否包含在允许范围内
配置初始化
在系统启动时通过CorsBeanDefinitionParser解析配置文件;
加载RequestMappingHandlerMapping时,通过InitializingBean的afterProperties的钩子调用initCorsConfiguration初始化注解信息;
配置文件初始化
在CorsBeanDefinitionParser类的parse方法中打一个断点。
CorsBeanDefinitionParser的调用栈
通过代码可以看到这边解析
跨域信息的配置可以以path为单位定义多个映射关系。
解析时如果没有定义则使用默认设置
// CorsBeanDefinitionParser
if (mappings.isEmpty()) {
// 最简配置时的默认设置
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(DEFAULT_ALLOWED_ORIGINS);
config.setAllowedMethods(DEFAULT_ALLOWED_METHODS);
config.setAllowedHeaders(DEFAULT_ALLOWED_HEADERS);
config.setAllowCredentials(DEFAULT_ALLOW_CREDENTIALS);
config.setMaxAge(DEFAULT_MAX_AGE);
corsConfigurations.put("/**", config);
}else {
// 单个mapping的处理
for (Element mapping : mappings) {
CorsConfiguration config = new CorsConfiguration();
if (mapping.hasAttribute("allowed-origins")) {
String[] allowedOrigins = StringUtils.tokenizeToStringArray(mapping.getAttribute("allowed-origins"), ",");
config.setAllowedOrigins(Arrays.asList(allowedOrigins));
}
// ...
}
解析完成后,通过MvcNamespaceUtils.registerCorsConfiguratoions注册
这边走的是spring bean容器管理的统一流程,现在转化为BeanDefinition然后再实例化。
// MvcNamespaceUtils
public static RuntimeBeanReference registerCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations, ParserContext parserContext, Object source) {
if (!parserContext.getRegistry().containsBeanDefinition(CORS_CONFIGURATION_BEAN_NAME)) {
RootBeanDefinition corsConfigurationsDef = new RootBeanDefinition(LinkedHashMap.class);
corsConfigurationsDef.setSource(source);
corsConfigurationsDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
if (corsConfigurations != null) {
corsConfigurationsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations);
}
parserContext.getReaderContext().getRegistry().registerBeanDefinition(CORS_CONFIGURATION_BEAN_NAME, corsConfigurationsDef);
parserContext.registerComponent(new BeanComponentDefinition(corsConfigurationsDef, CORS_CONFIGURATION_BEAN_NAME));
}
else if (corsConfigurations != null) {
BeanDefinition corsConfigurationsDef = parserContext.getRegistry().getBeanDefinition(CORS_CONFIGURATION_BEAN_NAME); corsConfigurationsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations);
}
return new RuntimeBeanReference(CORS_CONFIGURATION_BEAN_NAME);
}
注解初始化
在RequestMappingHandlerMapping的initCorsConfiguration中扫描使用CrossOrigin注解的方法,并提取信息。
RequestMappingHandlerMapping_initCorsConfiguration
// RequestMappingHandlerMapping
@Override
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), 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);
// ... 设置默认值
return config;
}
跨域请求处理
HandlerMapping在正常处理完查找处理器后,在AbstractHandlerMapping.getHandler中校验是否是跨域请求,如果是分两种进行处理:
拿到处理器后,通过请求头是否包含Origin判断是否跨域,如果是跨域,通过UrlBasedCorsConfigurationSource获取跨域配置信息,并委托getCorsHandlerExecutionChain处理
UrlBasedCorsConfigurationSource是CorsConfigurationSource的实现,从类名就可以猜出这边request与CorsConfiguration的映射是基于url的。getCorsConfiguration中提取request中的url后,逐一验证配置是否匹配url。
// UrlBasedCorsConfigurationSource
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for(Map.Entry<String, CorsConfiguration> entry : this.corsConfigurations.entrySet()) {
if (this.pathMatcher.match(entry.getKey(), lookupPath)) {
return entry.getValue();
}
}
return null;
}
// AbstractHandlerMapping
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
// ...
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
// HttpHeaders
public static final String ORIGIN = "Origin";
// CorsUtils
public static boolean isCorsRequest(HttpServletRequest request) {
return (request.getHeader(HttpHeaders.ORIGIN) != null);
}
通过请求头的http方法是否options判断是否预请求,如果是使用PreFlightRequest替换处理器;如果是普通请求,添加一个拦截器CorsInterceptor。
PreFlightRequest是CorsProcessor对于HttpRequestHandler的一个适配器。这样HandlerAdapter直接使用HttpRequestHandlerAdapter处理。
CorsInterceptor 是CorsProcessor对于HnalderInterceptorAdapter的适配器。
// AbstractHandlerMapping
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
HandlerExecutionChain chain, CorsConfiguration config) {
if (CorsUtils.isPreFlightRequest(request)) {
HandlerInterceptor[] interceptors = chain.getInterceptors();
chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
}
else {
chain.addInterceptor(new CorsInterceptor(config));
}
return chain;
}
private class PreFlightHandler implements HttpRequestHandler {
private final CorsConfiguration config;
public PreFlightHandler(CorsConfiguration config) {
this.config = config;
}
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws IOException {
corsProcessor.processRequest(this.config, request, response);
}
}
private class CorsInterceptor extends HandlerInterceptorAdapter {
private final CorsConfiguration config;
public CorsInterceptor(CorsConfiguration config) {
this.config = config;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
return corsProcessor.processRequest(this.config, request, response);
}
}
// CorsUtils
public static boolean isPreFlightRequest(HttpServletRequest request) {
return (isCorsRequest(request) && request.getMethod().equals(HttpMethod.OPTIONS.name()) &&
request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD) != null);
}
可以去github查看: https://github.com/haplone/spring_doc/blob/master/mvc/cors.md
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!
# spring
# cors
# 跨域
# mvc
# Spring+SpringMVC+JDBC实现登录的示例(附源码)
# Spring MVC 启动过程源码分析详解
# SpringMVC源码解析之消息转换器HttpMessageConverter
# Spring SpringMVC在启动完成后执行方法源码解析
# SpringMVC的源码解析
# 从源码角度看spring mvc的请求处理过程
# 配置文件
# 默认设置
# 默认值
# 的是
# 都是
# 判断是否
# 几个
# 拦截器
# 多个
# 两种
# 这是一个
# 要看
# 如果没有
# 可以看到
# 然后再
# 转化为
# 是一个非常
# 资源共享
# 启动时
# 猜出
相关文章:
济南网站建设制作公司,室内设计网站一般都有哪些功能?
如何零基础开发自助建站系统?完整教程解析
建站之星后台密码遗忘?如何快速找回?
长春网站建设制作公司,长春的网络公司怎么样主要是能做网站的?
建站之星如何通过成品分离优化网站效率?
如何高效利用亚马逊云主机搭建企业网站?
网站建设制作、微信公众号,公明人民医院怎么在网上预约?
代刷网站制作软件,别人代刷火车票靠谱吗?
微信小程序 五星评分(包括半颗星评分)实例代码
金*站制作公司有哪些,金华教育集团官网?
如何通过WDCP绑定主域名及创建子域名站点?
如何在橙子建站中快速调整背景颜色?
SQL查询语句优化的实用方法总结
如何快速搭建高效WAP手机网站?
GML (Geography Markup Language)是什么,它如何用XML来表示地理空间信息?
如何在万网主机上快速搭建网站?
定制建站方案优化指南:企业官网开发与建站费用解析
小说建站VPS选用指南:性能对比、配置优化与建站方案解析
建站之星如何快速更换网站模板?
如何配置WinSCP新建站点的密钥验证步骤?
网站视频怎么制作,哪个网站可以免费收看好莱坞经典大片?
如何选择CMS系统实现快速建站与SEO优化?
青岛网站建设如何选择本地服务器?
中山网站制作网页,中山新生登记系统登记流程?
广州美橙建站如何快速搭建多端合一网站?
建站之星安装后如何配置SEO及设计样式?
企业网站制作费用多少,企业网站空间一般需要多大,费用是多少?
平台云上自助建站如何快速打造专业网站?
专业的网站制作设计是什么,如何制作一个企业网站,建设网站的基本步骤有哪些?
小程序网站制作需要准备什么资料,如何制作小程序?
娃派WAP自助建站:免费模板+移动优化,快速打造专业网站
北京制作网站的公司,北京铁路集团官方网站?
常州企业网站制作公司,全国继续教育网怎么登录?
建站VPS能否同时实现高效与安全翻墙?
建站之星安装步骤有哪些常见问题?
如何通过商城自助建站源码实现零基础高效建站?
如何用VPS主机快速搭建个人网站?
Android使用GridView实现日历的简单功能
建站主机选购指南与交易推荐:核心配置解析
网站企业制作流程,用什么语言做企业网站比较好?
如何通过建站之星自助学习解决操作问题?
孙琪峥织梦建站教程如何优化数据库安全?
番禺网站制作公司哪家值得合作,番禺图书馆新馆开放了吗?
建站之星2.7模板:企业网站建设与h5定制设计专题
兔展官网 在线制作,怎样制作微信请帖?
重庆市网站制作公司,重庆招聘网站哪个好?
Python多线程使用规范_线程安全解析【教程】
Android自定义控件实现温度旋转按钮效果
Python lxml的etree和ElementTree有什么区别
网站制作软件免费下载安装,有哪些免费下载的软件网站?
*请认真填写需求信息,我们会在24小时内与您取得联系。