• 跨域问题的解决方案


    作者:java高并发框架
    链接:https://zhuanlan.zhihu.com/p/145837536
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    1.通过JSONP

    利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。

    JSONP 和 AJAX 相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但 AJAX 属于同源策略,JSONP 属于非同源策略(支持跨域请求)。JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持 GET 方法具有局限性,不安全可能会遭受XSS攻击。

    2.利用反向代理服务器

    同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略

    所以通过反向代理服务器可以有效的解决跨域问题,代理服务器需要做以下几个步骤:

    1.接受客户端的请求

    2.将请求转发给实际的服务器

    3.将服务器的响应结果返回给客户端

    Nginx就是类似的反向代理服务器,可以通过配置Nginx代理来解决跨域问题。

    3.服务端支持CORS

    最安全的还是服务端来设置允许哪些来源的请求,即服务端在接收到请求之后,对允许的请求源设置
    Access-Control-Allow-Origin 的响应头。

    通过@CrossOrigin注解

    这里以 Spring Boot 为例,可以通过 @CrossOrigin 注解来指定哪些类或者方法支持跨越,如下列代码所示:

    /**
     * 在类上加注解
     */
    @CrossOrigin({"http://127.0.0.1:9528", "http://localhost:9528"})
    @RestController
    public class UserController {
        
    }
    
    @RestController
    public class UserController {
        @Resource
        private UserFacade userFacade;
        /**
         * 在方法上加注解
         */
        @GetMapping(ApiConstant.Urls.GET_USER_INFO)
        @CrossOrigin({"http://127.0.0.1:9528", "http://localhost:9528"})
        public PojoResult<UserDTO> getUserInfo() {
            return userFacade.getUserInfo();
        }
    }
    

    通过CorsRegistry设置全局跨域配置

    @Configuration
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurerAdapter {
    	@Override
    	public void addCorsMappings(CorsRegistry registry) {
    		registry.addMapping("/**")
                .allowedOrigins("http://127.0.0.1:9528", "http://localhost:9528");
    	}
    }
    

    如果你使用的是 Spring Boot,推荐的做法是只定义一个 WebMvcConfigurer 的Bean:

    @Configuration
    public class MyConfiguration {
        @Bean
        public WebMvcConfigurer corsConfigurer() {
            return new WebMvcConfigurerAdapter() {
                @Override
                public void addCorsMappings(CorsRegistry registry) {
                    registry.addMapping("/**")
                        .allowedOrigins("http://127.0.0.1:9528", "http://localhost:9528");
                }
            };
        }
    }
    

    以上两种方式在没有定义拦截器(Interceptor)的时候,使用一切正常,但是如果你有一个全局的拦截器用来检测用户的登录态,例如下面的简易代码:

    public class AuthenticationInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
            // 从 http 请求头中取出 token
            String token = httpServletRequest.getHeader("token");
            // 检查是否登录
            if (token == null) {
                throw new InvalidTokenException(ResultCode.INVALID_TOKEN.getCode(), "登录态失效,请重新登录");
            }
            return true;
        }
    }
    

    当自定义拦截器返回true时,一切正常,但是当拦截器抛出异常(或者返回false)时,后续的CORS设置将不会生效。

    为什么拦截器抛出异常时,CORS不生效呢?可以看下这个issue:

    when interceptor preHandler throw exception, the cors is broken

    有个人提交了一个issue,说明如果在自定义拦截器的preHandler方法中抛出异常的话,通过 CorsRegistry 设置的全局 CORS 配置就失效了,但是Spring Boot 的成员不认为这是一个Bug。

    然后提交者举了个具体的例子:

    他先定义了CorsRegistry,并添加了一个自定义的拦截器,拦截器中抛出了异常

     

     

    然后他发现AbstractHandlerMapping在添加CorsInterceptor的时候,是将 Cors 的拦截器加在拦截器链的最后:

     

     

    那就会造成上面说的问题,在自定义拦截器中抛出异常之后,CorsInterceptor 拦截器就没有机会执行向 response 中设置 CORS 相关响应头了。

    issue的提交者也给出了解决的方案,就是将用来处理 Cors 的拦截器 CorsInterceptor 加在拦截器链的第一个位置:

     

     

    这样的话请求来了之后,第一个就会为 response 设置相应的 CORS 响应头,后续如果其他自定义拦截器抛出异常,也不会有影响了。

    感觉是一个可行的解决方案,但是 Spring Boot 的成员认为这不是 Spring Boot 的Bug,而是 Spring Framework 的 Bug,所以将这个issue关闭了。

    通过CorsFilter设置全局跨域配置

    既然通过拦截器设置全局跨域配置会有问题,那我们还有另外一种方案,通过过滤器 CorsFilter 的方式来设置,代码如下:

    @Configuration
    public class MyConfiguration {
    	@Bean
    	public FilterRegistrationBean corsFilter() {
    		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    		CorsConfiguration config = new CorsConfiguration();
    		config.setAllowCredentials(true);
    		config.addAllowedOrigin("http://127.0.0.1:9528");
            config.addAllowedOrigin("http://localhost:9528");
    		config.addAllowedHeader("*");
    		config.addAllowedMethod("*");
    		source.registerCorsConfiguration("/**", config);
    		FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
    		bean.setOrder(0);
    		return bean;
    	}
    }
    

    为什么过滤器可以而拦截器不行呢?

    因为过滤器依赖于 Servlet 容器,基于函数回调,它可以对几乎所有请求进行过滤。而拦截器是依赖于 Web 框架(如Spring MVC框架),基于反射通过AOP的方式实现的。

    在触发顺序上如下图所示:

     

     

    因为过滤器在触发上是先于拦截器的,但是如果有多个过滤器的话,也需要将 CorsFilter 设置为第一个过滤器才行

  • 相关阅读:
    jQuery-封装的表单元素选择器
    jQuery-获取DOM元素的各类选择器
    jQuery-外部引用和直接引用
    数据库-查询平均成绩大于60分的同学的学号和平均成绩
    数据库-查询“001”课程比“002”课程成绩高的所有学生的学号
    ps让图片背景透明
    echarts中饼图的legend自定义icon图片(扇形为例)
    获取bootstrap模态框点击的对应项(e.relatedTarget.dataset)
    echarts属性的设置(完整大全)
    小程序获取地理位置授权
  • 原文地址:https://www.cnblogs.com/zwjun/p/13162784.html
Copyright © 2020-2023  润新知