Java请求跨域处理

Java请求跨域处理

参考

Access to XMLHttpRequest at ‘http://localhost:88/api/sys/login’ from origin ‘http://localhost:8001’ 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.

跨域:浏览器对JavaScript施加的安全限制,默认不让js获取远程网站的数据

XMLHttpRequest对象发送AJAX请求

同源策略:指协议、域名、端口都要相同,其中有一个不同都会产生跨域

限制:

  • 域名及其对应IP,发送跨域
  • 主域相同,子域不同,跨域
  • 同一域名,不同二级域名(有无www),跨域

跨域流程:

登录不属于简单请求,由于登录的content-type属于application/json所以数据非简单请求,此时先发送一个options 预检请求

服务器可对origin请求头做校验,仅允许部分请求源跨域访问数据

解决跨域:

  • 使用NGINX 部署为同一域 NGINX拦在gateway前方

在这里插入图片描述

前后端请求都走nginx 通过动静分离 代理到不同实际地址,但请求的origin(请求域:协议+域名+端口)就一致了都是nginx的地址

  • 跨域流程处理

在这里插入图片描述

1、添加响应头

• Access-Control-Allow-Origin:支持哪些来源的请求跨域

• Access-Control-Allow-Methods:支持哪些方法跨域

• Access-Control-Allow-Credentials:跨域请求默认不包含cookie,设置为true可以包含

cookie

• Access-Control-Expose-Headers:跨域请求暴露的字段

• CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:

Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如

果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

• Access-Control-Max-Age:表明该响应的有效时间为多少秒。在有效时间内,浏览器无

须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果

该首部字段的值超过了最大有效时间,将不会生效。

  • 在网关中添加跨域filter

    springboot 自带CorsWebFilter 来解决跨域问题,通过spring boot自带的跨域解决方式来处理,若不允许跨域返回403,若允许返回允许响应头信息

    package priv.yuzuki.gulimall.gateway.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.reactive.CorsWebFilter;
    import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
    
    /**
     * CorsConfiguration
     *
     * @author yuzuki
     * @date 2021/3/28 17:33
     * @since 1.0.0
     */
    @Configuration
    public class MyCorsConfiguration {
        @Bean
        public CorsWebFilter corsWebFilter(){
    //        CorsConfigurationSource corsConfigurationSource = new CorsConfigurationSource() {
    //            @Override
    //            public org.springframework.web.cors.CorsConfiguration getCorsConfiguration(javax.servlet.http.HttpServletRequest request) {
    //                return null;
    //            }
    //        };
            // 注意使用 reactive包下的类,因为spring cloud 都是响应式编程所以要都使用reactive包下的类
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            // 配置跨域
            corsConfiguration.addAllowedHeader("*");
            corsConfiguration.addAllowedMethod("*");
            // 测试效果,如果只允许GET请求,看POST请求会被如何拒绝
    //        corsConfiguration.addAllowedMethod(HttpMethod.GET);
            corsConfiguration.addAllowedOrigin("*");
            // 是否允许携带cookie跨域
            corsConfiguration.setAllowCredentials(true);
    
            source.registerCorsConfiguration("/**",corsConfiguration);
    
            return new CorsWebFilter(source);
        }
    }
    
    
  • 出错

    :8001/#/login:1 Access to XMLHttpRequest at ‘http://localhost:88/api/sys/login’ from origin ‘http://localhost:8001’ has been blocked by CORS policy: The ‘Access-Control-Allow-Origin’ header contains multiple values ‘http://localhost:8001, http://localhost:8001’, but only one is allowed.

    若多出配置跨域会返回多个相同响应头导致出错

spring boot 响应式编程跨域请求源码分析

核心是org.springframework.web.cors{.reactive}.CorsWebFilter

用户只需给出自定义的跨域配置,由spring boot自动扫描CorsWebFilter注入IOC容器并产生作用

跨域过滤的核心方法为CorsWebFilter#Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain)

	@Override
	public boolean process(@Nullable CorsConfiguration config, ServerWebExchange exchange) {

		ServerHttpRequest request = exchange.getRequest();
		ServerHttpResponse response = exchange.getResponse();
		response.getHeaders().addAll(HttpHeaders.VARY, VARY_HEADERS);

        // 根据请求头中是否携带origin请求头或origin代表的资源域与请求域是否同源(协议+域名+端口不一致)判断是否跨域,跨域就往下判断是否允许,否则请求不规范,未携带origin请求头或origin的内容与请求源地址不同
		if (!CorsUtils.isCorsRequest(request)) {
			return true;
		}

		if (response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) != null) {
			logger.trace("Skip: response already contains \"Access-Control-Allow-Origin\"");
			return true;
		}

		boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
		if (config == null) {
			if (preFlightRequest) {
				rejectRequest(response);
				return false;
			}
			else {
				return true;
			}
		}

		return handleInternal(exchange, config, preFlightRequest);
	}

最终处理方法:

protected boolean handleInternal(ServerWebExchange exchange,
      CorsConfiguration config, boolean preFlightRequest) {

   ServerHttpRequest request = exchange.getRequest();
   ServerHttpResponse response = exchange.getResponse();
   HttpHeaders responseHeaders = response.getHeaders();

   String requestOrigin = request.getHeaders().getOrigin();
    // 检测请求origin是否在允许范围内(由之前通过@Configuration+@Bean给出的自定义配置判断
   String allowOrigin = checkOrigin(config, requestOrigin);
   if (allowOrigin == null) {
      logger.debug("Reject: '" + requestOrigin + "' origin is not allowed");
      rejectRequest(response);
      return false;
   }

   HttpMethod requestMethod = getMethodToUse(request, preFlightRequest);
    // 检测请求方法是否在指定范围内
   List<HttpMethod> allowMethods = checkMethods(config, requestMethod);
   if (allowMethods == null) {
      logger.debug("Reject: HTTP '" + requestMethod + "' is not allowed");
      rejectRequest(response);
      return false;
   }

   List<String> requestHeaders = getHeadersToUse(request, preFlightRequest);
   // 检测请求头是否在指定范围内
   List<String> allowHeaders = checkHeaders(config, requestHeaders);
   if (preFlightRequest && allowHeaders == null) {
      logger.debug("Reject: headers '" + requestHeaders + "' are not allowed");
      rejectRequest(response);
      return false;
   }

   // 设置允许请求origin的响应头
   responseHeaders.setAccessControlAllowOrigin(allowOrigin);

   // 预检请求才设置允许请求方法的响应头
   if (preFlightRequest) {
      responseHeaders.setAccessControlAllowMethods(allowMethods);
   }

   // 预检请求且允许请求头非空才设置允许请求头的响应头
   if (preFlightRequest && !allowHeaders.isEmpty()) {
      responseHeaders.setAccessControlAllowHeaders(allowHeaders);
   }

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

   // 判断是否允许携带cookie
   if (Boolean.TRUE.equals(config.getAllowCredentials())) {
      responseHeaders.setAccessControlAllowCredentials(true);
   }

   // 判断是否设置允许响应时间
   if (preFlightRequest && config.getMaxAge() != null) {
      responseHeaders.setAccessControlMaxAge(config.getMaxAge());
   }

   return true;
}

小结

origin请求头是浏览器自带的,无需用户干预,如果不带,是否表明没有发生跨域

不带/带了但是请求同源都不会发生跨域处理

同源策略:

如果两个 URL 的 protocolport (如果有指定的话)和 host 都相同的话,则这两个 URL 是同源。这个方案也被称为“协议/主机/端口元组”,或者直接是 “元组”。(“元组” 是指一组项目构成的整体,双重/三重/四重/五重/等的通用形式)。

如何避免引导正常用户引发跨站请求伪造?(CSRF,Cross-site request forgery)(通过使用网站返回的cookie以非本意的方式发送请求,通过恶意网站B,引诱用户在B网站做任何操作从而B网站获取用户cookie并给给已登录的A网站发送下单、转账等请求)通过浏览器自带的origin同源策略可在一定程度上防止该操作

如果非正常用户去更改origin来避免浏览器的同源机制,除非获取正常用户的电脑,否则无法完成操作

浏览器通过同源策略在一定程度上阻止恶意请求(CSRF),但业务上产生跨域资源共享(CORS)场景则通过跨域策略来允许访问非同源请求获取资源

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值