新建的 H5 页面项目在测试站出现跨域报错。但访问正式站的时候,竟然没有报错。H5 页面与后台的前端(前后端分离)请求的是同一个后端接口地址,只不过是在用户授权登录上使用了独立的验证(抄了若依的前后端分离版本的登录验证和 redisService 存储的 token 服务)。相当于是在现有项目上开了一个小口,允许不通过当前的 userDetailSerice 认证,直接访问。

以下是继承了 ResourceServerConfigurerAdapterOAuthResourceConfig 配置:

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                .antMatchers("/xx/login/**", "/xx/h5/**").permitAll()
                .antMatchers("/xx/**").authenticated();
    }

一开始没读懂这些配置的内容,但 .antMatchers("/xx/login/**", "/xx/h5/**").permitAll() 放行请求是没问题的。主要是对 authorizeRequests() 理解有偏差,以为针对认证过的请求,放行 OPTIONS 类型的请求。

后面百度补了一些基础,authorizeRequests() 是在定义请求的授权规则之前,需要加上来返回可操作的对象。基本涉及到 路由 pattern 设置的都会加上这一句。authenticated() 才是要求请求已登录,存在验证过的授权,比如请求携带了有效的 token,并且验证通过了。

上面的配置允许 OPTIONS 请求通过,但显然这并不够,还需要设置返回的头部,允许访问的源 Origin 之类的,参考网上的补全:

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.cors().configurationSource(configurationSource()).and().exceptionHandling()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                .antMatchers("/xx/login/**", "/xx/h5/**").permitAll()
                .antMatchers("/xx/**").authenticated();
    }

    CorsConfigurationSource configurationSource(){
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
        corsConfiguration.setAllowedMethods(Arrays.asList("*"));
//        corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:8080"));
        corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**",corsConfiguration);
        return source;
    }

一开始设置的 Origins 为具体的测试站和正式站的源,但这显然不够灵活,应该是将访问的源设置为允许的源。但苦于不知道怎么加 request 进来获取,所以尝试设置为 *,看是否可用。通过 Postman 设置 Headers 中 Origin 字段来调试,查看返回 Headers 中是否携带了 Access-Control-Allow-* 之类的字段,以此判断服务端是否允许跨域访问。如果携带了就说明服务端开启了允许跨域访问。测试发现,设置为 *,返回的 Access-Control-Allow-Origin 刚好就是请求中设置的 Origin,也就是说这么设置正好实现了想要的效果。

反向代理解决跨域问题

之后反应过来,后台是前端分离的,后台的前端也应该存在相同的问题才是。然后就发现,原来前端做了反向代理。

这也是一种解决方案:

http://h5.xx.com/api/xx -> http://xxx.com:8080/api/xx

通过宝塔面板,设置网站的反向代理 /api/ -> http://xxx.com:8080,前端也修改一下访问地址为 /api/ 然后就好了。