uniquejava / blog

My notes regarding the vibrating frontend :boom and the plain old java :rofl.
Creative Commons Zero v1.0 Universal
11 stars 5 forks source link

CORS and node-http-proxy #133

Open uniquejava opened 7 years ago

uniquejava commented 7 years ago

前后端分离,本地前端开发调用接口会有跨域问题,有以下几种解决方法(任选一种) :

1.将前后端代码放一起: 将前端打包后的代码(index.html+js)放在spring boot的src/main/resources/public目录下, 然后用maven将后台打成war包.(缺点:对于FEer来说, 每次后端更新都要去测试服下一个更新包,还要在本地搭建java运行环境,麻烦)

  1. CORS跨域:后端接口在返回的时候,在response header中加入'Access-Control-Allow-origin':* 之类的(有的时候后端不方便这样处理,前端就蛋疼了) 关键是很多情况下这些服务是来自外部的service,我们无法要求他们更改服务端代码 见: https://github.com/uniquejava/blog/issues/22 CORS部分

  2. 用nodejs搭建本地http服务器,并且判断访问接口URL时进行转发,完美解决本地开发时候的跨域问题。这正是node-http-proxy的作用之一

  3. 使用浏览器的插件, chrome我用的Moesif Origin & CORS Changer , firefox我用的https://addons.mozilla.org/en-US/firefox/addon/cors/reviews/

  4. 谷歌开启允许跨域, 给chrome browser加上启动参数--args --disable-web-security --user-data-dir参见: https://github.com/uniquejava/blog/issues/17

  5. 将前端代码放到apache或nginx下, 并开启反向代理.

  6. 使用JSONP.

使用http-proxy-middleware

http-proxy-middleware 基于node-http-proxy提供了比后者更简单易用的API, 我最终选择了前者..

以下参考 http-proxy-middleware的working examples express 版本

var proxy = require('http-proxy-middleware');
var proxyConfig = {
  target: 'https://watson-speech.mybluemix.net/api/speech-to-text/token',
  changeOrigin: true,
  logLevel: 'debug'
};
...
app.use(proxy('/api/speech-to-text/token', proxyConfig));

如果用IBM Watson speech service,上面的token URL是唯一需要代理的地址. 其中参数changeOrigin: true是必须的, 去掉就会报下面的错误:

[HPM] Error occurred while trying to proxy request /api/speech-to-text/token 
from localhost:3000 to https://watson-speech.mybluemix.net/api/speech-to-text/token (undefined) (https://nodejs.org/api/errors.html#errors_common_system_errors)
^

官方的README上还提供了以上配置的shorthand syntax,用起来不要太爽.

var proxy = require('http-proxy-middleware');

var app = express();

// proxy watson speech to text service
app.use(proxy(config.watson.speech_token_url, {changeOrigin: true, logLevel: 'debug'}));

这个中间件放在哪里都无所谓, 我是把它放在所有express中间件的最前面, 避免不必要的request处理.

使用node-http-proxy (备份, 不能WORK)

var httpProxy = require('http-proxy');

var apiProxy = httpProxy.createProxyServer();

app.get('/watson/speech', function(req, res){ 
  apiProxy.web(req, res, { target: 'https://watson-speech.mybluemix.net' });
});

References

  1. Node.js配合node-http-proxy解决本地开发ajax跨域问题

  2. https://stackoverflow.com/questions/20431697/node-http-proxy-and-express

uniquejava commented 4 years ago

spring boot oauth2 (spring-cloud-starter-oauth2)

碰到CORS的问题

第一步看浏览器console:

:8080/#/login:1 Access to XMLHttpRequest at 'http://localhost:9090/sales_anchor/oauth/token' from origin 'http://localhost:8080' 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.

第二步是调低logback的日志级别。

能看到如下信息

[20-04-01 22:52:02 DEBUG][o.s.web.cors.DefaultCorsProcessor] Reject: 'http://localhost:8080' origin is not allowed

顺藤摸瓜(DefaultCorsProcessor), 找到了如下配置:

来自: https://docs.spring.io/spring-framework/docs/5.0.0.BUILD-SNAPSHOT/spring-framework-reference/html/cors.html

@Configuration
public class CorsConfig {
    // IMPORTANT: it has to be a normal configuration class,
    // not extending WebMvcConfigurerAdapter or other Spring Security class
    @Bean
    public FilterRegistrationBean customCorsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.setAllowedOrigins(Arrays.asList("*"));
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));

        // IMPORTANT #2: I didn't stress enough the importance of this line in my
        // original answer,
        // but it's here where we tell Spring to load this filter at the right point in
        // the chain
        // (with an order of precedence higher than oauth2's filters)
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}

问题解决。 (注意setAllowedOrigins可以传数组, 还有个addAllowedOrigin, 前者更好用)