【问题标题】:Oauth 2 popup with Angular 2Angular 2 的 Oauth 2 弹出窗口
【发布时间】:2016-04-09 13:07:53
【问题描述】:

我正在升级/重写现有的 Angular 应用程序以使用 angular2。我的问题是我想在一个新的弹出窗口中打开一个 OAuth 流程,一旦 OAuth 流程完成,使用 window.postMessage 与 angular 2 应用程序进行通信,表明 OAuth 流程是成功的。

目前我在 angular 2 服务中拥有的是

export class ApiService { 
    constructor(private _loggedInService: LoggedInService) {
        window.addEventListener('message', this.onPostMessage, false);
     }

    startOAuthFlow() {
       var options = 'left=100,top=10,width=400,height=500';
       window.open('http://site/connect-auth', , options);
    }

    onPostMessage(event) {
      if(event.data.status === "200") {
          // Use an EventEmitter to notify the other components that user logged in
          this._loggedInService.Stream.emit(null);
      }
    }

}

在 OAuth 流程结束时加载的此模板

<html>
  <head>
    <title>OAuth callback</title>
    <script>
      var POST_ORIGIN_URI = 'localhost:8000';
      var message = {"status": "200", "jwt":"2"};
      window.opener.postMessage(message, POST_ORIGIN_URI);
      window.close();
    </script>
  </head>
</html>

像这样使用window.addEventListener 似乎完全破坏了angular 2 应用程序,取消引用this

所以我的问题是我可以使用 window.addEventListener 还是不应该使用 postMessage 与 angular2 应用程序进行通信?

** 完整的 angular2 noob 所以任何帮助表示赞赏

【问题讨论】:

    标签: javascript oauth typescript angular


    【解决方案1】:

    我一直在摆弄这个,但最后,对我来说最可靠的方法是将用户重定向到誓言页面

    window.location.href = '/auth/logintwitter';
    

    在后台跳起誓言(我用的是快递),然后重定向回接收前端页面...

    res.redirect(`/#/account/twitterReturn?userName=${userName}&token=${token}`);
    

    我的解决方案有一些特殊之处,因为例如无论登录类型如何,我都想在客户端上只使用 JsonWebToken,但如果您有兴趣,完整的解决方案就在这里。

    https://github.com/JavascriptMick/learntree.org

    【讨论】:

      【解决方案2】:

      所以通过一些调查发现了问题。我正在取消引用this。这个github wiki 帮助我理解了更多。

      要解决我的情况需要做几件事。首先,我创建了一个服务,该服务封装了 eventListener 的添加

      import {BrowserDomAdapter} from 'angular2/platform/browser';
      
      export class PostMessageService {
         dom = new BrowserDomAdapter();
         addPostMessageListener(fn: EventListener): void {
           this.dom.getGlobalEventTarget('window').addEventListener('message', fn,false)
         }
      }
      

      然后使用这个addPostMessageListener 我可以在我的其他服务中附加一个函数来触发

      constructor(public _postMessageService: PostMessageService,
          public _router: Router) {
          // Set up a Post Message Listener
          this._postMessageService.addPostMessageListener((event) => 
                this.onPostMessage(event)); // This is the important as it means I keep the reference to this
      
      }
      

      然后它会按照我期望的方式工作

      【讨论】:

      • 知道现在 BrowserDomAdapter 不再在 Angular 2 RC 中导出,如何做到这一点?
      • 更新:实际上,您可以只使用window.addEventListener - 正如您所指出的,重要的部分是使用匿名函数来保留执行上下文。谢谢!
      • 您好,如果从同一台机器接收到不同端口的消息,是否有可能?我想将数据从我的 jsp/java(8080 端口)发送到 angular2 监听器(4200 端口)..
      【解决方案3】:

      我在 Github 上有一个完整的 Angular2 OAuth2 框架应用,你可以参考一下。

      它使用 Auth 服务进行 OAuth2 隐式授权,然后使用 Window 服务来创建弹出窗口。然后,它会监视该窗口以获取 URL 上的访问令牌。

      您可以访问demo OAuth2 Angular code (with Webpack) here

      这是来自 Auth 服务的登录例程,它可以让您了解正在发生的事情,而无需查看整个项目。我已经为你添加了一些额外的 cmets。

      public doLogin() {
          var loopCount = this.loopCount;
          this.windowHandle = this.windows.createWindow(this.oAuthTokenUrl, 'OAuth2 Login');
      
          this.intervalId = setInterval(() => {
              if (loopCount-- < 0) { // if we get below 0, it's a timeout and we close the window
                  clearInterval(this.intervalId);
                  this.emitAuthStatus(false);
                  this.windowHandle.close();
              } else { // otherwise we check the URL of the window
                  var href:string;
                  try {
                      href = this.windowHandle.location.href;
                  } catch (e) {
                      //console.log('Error:', e);
                  }
                  if (href != null) { // if the URL is not null
                      var re = /access_token=(.*)/;
                      var found = href.match(re);
                      if (found) { // and if the URL has an access token then process the URL for access token and expiration time
                          console.log("Callback URL:", href);
                          clearInterval(this.intervalId);
                          var parsed = this.parse(href.substr(this.oAuthCallbackUrl.length + 1));
                          var expiresSeconds = Number(parsed.expires_in) || 1800;
      
                          this.token = parsed.access_token;
                          if (this.token) {
                              this.authenticated = true;
                          }
      
                          this.startExpiresTimer(expiresSeconds);
                          this.expires = new Date();
                          this.expires = this.expires.setSeconds(this.expires.getSeconds() + expiresSeconds);
      
                          this.windowHandle.close();
                          this.emitAuthStatus(true);
                          this.fetchUserInfo();
                      }
                  }
              }
          }, this.intervalLength);
      }
      

      如果您对启动和运行应用程序有任何疑问或问题,请随时询问。

      【讨论】:

      • 我在href = this.windowHandle.location.href; 线上收到Chrome 抛出的错误,错误提示Error: DOMException: Blocked a frame with origin "http://localhost:[PORT]" from accessing a cross-origin frame. - 只有我一个吗?自这篇文章以来,Chrome 是否更新了他们的安全政策?
      • @[Michael Oryl] 这可以用于授权授权流程吗?
      【解决方案4】:

      我认为这是 Angular2 的方式:

      (Dart 代码但 TS 应该很相似)

      @Injectable()
      class SomeService {
        DomAdapter dom;
        SomeService(this.dom) {
          dom.getGlobalEventTarget('window').addEventListener("message", fn, false);
        }
      }
      

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多