【问题标题】:Authorize with Spotify using Nginx使用 Nginx 向 Spotify 授权
【发布时间】:2019-08-14 01:09:14
【问题描述】:

我有一个运行三个服务的 docker 应用:

  • 客户端 --> 响应前端
  • 网络 --->烧瓶后端
  • nginx ->- 两者的反向代理

这是(简化的)项目结构:

docker-compose-dev.yml
services/
        client/
              src/
                 app.jsx
                 components/ 
                           spotify-auth.js
                           Spotify.jsx
         nginx/
              dev.conf
         web/

这是我在构建时定义暴露端口的地方:

docker-compose-dev.yml

  web:
    build:
      context: ./services/web
      dockerfile: Dockerfile-dev
    volumes:
      - './services/web:/usr/src/app'
    ports:
      - 5001:5000 <----------------
    environment:
      - FLASK_ENV=development
      - APP_SETTINGS=project.config.DevelopmentConfig
    depends_on:  
      - web-db

  nginx:
    build:
      context: ./services/nginx
      dockerfile: Dockerfile-dev
    restart: always
    ports:
      - 80:80     <----------------
      - 8888:8888 <----------------
    depends_on:
      - web
      - client

  client:
    build:
      context: ./services/client
      dockerfile: Dockerfile-dev
    volumes:
      - './services/client:/usr/src/app'
      - '/usr/src/app/node_modules'
    ports:
      - 3007:3000   <----------------
    environment:
      - NODE_ENV=development
      - REACT_APP_WEB_SERVICE_URL=${REACT_APP_WEB_SERVICE_URL}
    depends_on:
      - web

重定向

Client 服务需要通过Spotify 进行身份验证,这需要Redirect URI,在https://developer.spotify.com 列入白名单。对于我来说,我有几个选择:

这是我的 nginx 文件,我尝试在其中组织正确的端口:

dev.conf

server {

  listen 80;
  listen 8888;

  location / {        // frontend at localhost:3000
    proxy_pass        http://client:3000;
    proxy_redirect    default;
    proxy_set_header  Upgrade $http_upgrade;
    proxy_set_header  Connection "upgrade";
    proxy_set_header  Host $host;
    proxy_set_header  X-Real-IP $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header  X-Forwarded-Host $server_name;
  }

  location /users {   // backend at localhost:5000
    proxy_pass        http://web:5000;
    proxy_redirect    default;
    proxy_set_header  Upgrade $http_upgrade;
    proxy_set_header  Connection "upgrade";
    proxy_set_header  Host $host;
    proxy_set_header  X-Real-IP $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header  X-Forwarded-Host $server_name;
  }

  location /auth {    # this authentication is for the app, not spotify
    proxy_pass        http://web:5000;
    proxy_redirect    default;
    proxy_set_header  Upgrade $http_upgrade;
    proxy_set_header  Connection "upgrade";
    proxy_set_header  Host $host;
    proxy_set_header  X-Real-IP $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header  X-Forwarded-Host $server_name;
  }
}

最后,我的jsjsx文件用于:

  1. 通过 Spotify 进行身份验证 -----> Implicit Grant
  2. 将我的应用重定向回localhost,或'/'

spotify-auth.js

export const stateKey = 'spotify_auth_state';
export const client_id = 'my_client_id'; // Your client id
export const redirect_uri = 'http://localhost:3000'; // my redirect uri
//export const redirect_uri = 'http://localhost:8888'; // my second try for uri
export const scope ='user-read-private user-read-email user-read-playback-state playlist-modify-public playlist-modify-private';

Spotify.jsx

class SpotifyAuth extends Component {  
  constructor (props) {
    super(props);
    this.state = {
      isAuthenticatedWithSpotify: false
    };
    this.state.handleRedirect = this.handleRedirect.bind(this);
    this.loginSpotifyUser = this.loginSpotifyUser.bind(this);
  };

  function generateRandomString(length) {
    let text = '';
    const possible =
      'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

    for (let i = 0; i < length; i++) {
      text += possible.charAt(Math.floor(Math.random() * possible.length));
    }

    return text;
  }

  getHashParams() {
    const hashParams = {};
    const r = /([^&;=]+)=?([^&;]*)/g;
    const q = window.location.hash.substring(1);
    let e = r.exec(q);
    while (e) {
      hashParams[e[1]] = decodeURIComponent(e[2]);
      e = r.exec(q);
    }
    return hashParams;
  }

  componentDidMount() {
    const params = this.getHashParams();

    const access_token = params.access_token;
    const state = params.state;
    const storedState = localStorage.getItem(stateKey);
    //localStorage.setItem('spotifyAuthToken', access_token);
    //localStorage.getItem('spotifyAuthToken');
    if (access_token && (state == null || state !== storedState)) {
      alert('There was an error during the authentication');
    } else {
      localStorage.removeItem(stateKey);
    }   
    // DO STUFF WITH ACCESS TOKEN HERE -- send ajax to backend routes    
  };

  handleRedirect() {
    const state = generateRandomString(16);
    localStorage.setItem(stateKey, state);

    let url = 'https://accounts.spotify.com/authorize';
    url += '?response_type=token';
    url += '&client_id=' + encodeURIComponent(client_id);
    url += '&scope=' + encodeURIComponent(scope);
    url += '&redirect_uri=' + encodeURIComponent(redirect_uri);
    url += '&state=' + encodeURIComponent(state);

    window.location = url;
    // post data to backend
    const url = `${process.env.REACT_APP_WEB_SERVICE_URL}/auth/spotify}`;
    axios.post(url, data)
    .then((res) => {
      this.loginSpotifyUser(res.data.auth_token);
    })
    .catch((err) => { console.log(err); });    
  };

  loginSpotifyUser(token) {
    window.localStorage.setItem('spotifyAuthToken', token);
    this.setState({ isAuthenticatedWithSpotify: true });
    this.props.createMessage('Welcome to Spotify', 'success');
  };

  render() {
    return (
      <div className="button_container">
        <button className="sp_button" onClick={this.handleRedirect}>
          <strong>CONNECT YOUR SPOTIFY ACCOUNT</strong>
        </button>
      </div>
      )
    }
}

export default SpotifyAuth;

然后渲染,像这样:

App.jsx

render() {
  return (
  <div>  
    <Switch>
       <Route exact path='/' render={() => (
          <SpotifyAuth/>
        )} 
       />
    </Switch>
   </div>

错误:

在所有这些设置并运行之后,我得到:

INVALID_CLIENT: Invalid redirect URI 

日志:

在构建之前,我导出了这个env 变量:

$ export REACT_APP_WEB_SERVICE_URL=http://localhost

服务建立后,我得到logs

client_1   | You can now view client in the browser.
client_1   | 
client_1   |   Local:            http://localhost:3000/

web_1      |  * Environment: development
web_1      |  * Debug mode: on
web_1      |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

nginx_1    | 172.21.0.1 - - [27/Mar/2019:03:58:56 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" "-"
nginx_1    | 172.21.0.1 - - [27/Mar/2019:03:58:56 +0000] "GET /static/js/0.chunk.js HTTP/1.1" 304 0 "http://localhost/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" "-"
nginx_1    | 172.21.0.1 - - [27/Mar/2019:03:58:56 +0000] "GET /static/js/bundle.js HTTP/1.1" 304 0 "http://localhost/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" "-"
nginx_1    | 172.21.0.1 - - [27/Mar/2019:03:58:56 +0000] "GET /static/js/main.chunk.js HTTP/1.1" 304 0 "http://localhost/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" "-"

注意

如果我尝试在Spotify.jsx 中使用localhost:8888,则该应用会设法使用Spotify 进行身份验证,但随后所有位置都以localhost:8888/auth/login 开头,依此类推,这是不希望的。


问题

为什么localhost:3000,我的client,不能作为redirect uri 工作?我错过了什么?

这是在像这样的docker 项目上使用Spotify 进行身份验证的最可靠方法吗?

【问题讨论】:

    标签: reactjs docker nginx spotify


    【解决方案1】:

    您的问题:

    您的docker-compose.yml 具有配置有端口映射3007:3000client 容器。注意 Docker-compose 的端口映射是host:container (Compose file reference),这意味着你主机的 3007 端口映射到容器的 3000 端口。

    这样,您尝试连接到您的主机中不可用的容器端口,尽管nginx 对此感到满意,因为它与您的客户端位于同一网络上,因此它可以访问它并重定向请求。

    如果是这样的话:

    • http://localhost:3000 不起作用,因为它在主机端关闭。
    • http://localhost:3007 打开您的客户端,因为它被重定向到您的客户端容器,但您将无法在那里使用 Spotify 身份验证,除非您将此 URL 列入白名单并更改您的 redirect_uri
    • http://localhost:8888 打开您的客户端,因为您将nginx 设置为反向代理,并且它可以访问客户端端口 3000,因为它位于同一网络上。

    您的解决方案:

    您的解决方案正在更改docker-compose,因此客户端映射到端口3000:3000。然后 Spotify 身份验证应该没问题,因为端口已打开并且 URL 已正确配置。

    额外:

    关于您对设计提出意见的请求,nginx 感觉您的设计未充分利用。如果您设置了反向代理,那么您重定向到的服务将隐藏在无法从外部访问的安全网络中。例如,通过这种方式,您可以在nginx 上配置 SSL,而无需在其余服务上使用 HTTP。但是,如果可以从其他端口访问此类服务,则进行这种配置是没有用的。

    在生产设置中,您可能希望从 docker-compose 关闭 clientweb 端口(字面意思是删除端口映射。Nginx 访问您的容器没有问题,因为它位于同一网络上,不像你的主人),只让nginx暴露在现实世界中。

    您可能还想设置一个rewrite 规则,其中客户端和服务器分别挂在http://localhost/clienthttp://localhost/server 地址上,但 nginx 会重写请求并将其代理到适当的容器,以便容器实际看到请求来http://localhost:3000/。您可以在Stack Exchange - Nginx reverse proxy + URL rewrite 看到有关如何配置所有这些的示例。

    【讨论】:

      猜你喜欢
      • 2019-08-05
      • 2020-06-26
      • 1970-01-01
      • 2020-10-06
      • 2018-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多