如何在Angular2 rc3路由中处理来自oauth重定向url的哈希片段

Posted

技术标签:

【中文标题】如何在Angular2 rc3路由中处理来自oauth重定向url的哈希片段【英文标题】:How to handle hash fragments from oauth redirect urls in Angular2 rc3 routing 【发布时间】:2016-11-10 09:21:37 【问题描述】:

我正在尝试找到一种方法来处理设置 Angular2 Typescript 路由(使用 3.0.0-alpha.8 路由器),该路由将处理以哈希片段开头的路由。

我正在开发的应用程序通过带有 oauth2 的 rails 后端处理所有外部登录(我无法控制)。将用户重定向到外部登录页面工作正常,但是当重定向 url 时,总是某种形式的http://localhost:4200#access_token=TOKEN(其中 TOKEN 是一系列数字和字母)被发送回来,但我不知道如何设置一个路由可以处理# 符号,因此我可以捕获它并重定向到相应的组件。

在以前的 Angular1 应用程序中,ui-router 能够在以下路线中使用:

.state('accessToken', 
  url: '/access_token=:token',
  controller: 'LoginController',
  params:  token: null 
)

这在接受返回的重定向 url 时没有问题,然后会将所有内容传递给 LoginController 以在前端处理其余的身份验证/令牌业务。

然而,这个应用程序是 Angular2 和 Typescript,并且路由器查询参数似乎不太灵活,我在实施类似的解决方案时遇到了麻烦。我一直基于文档中的this section,但所有示例都在构建其他东西,例如/heroes,然后再进入查询参数的复杂部分,例如/heroes/:id。我也搜索了 ***,但找不到任何适用于 Angular2 和 Typescript 以及当前路由器的东西。

这是我当前(不工作)的解决方案:

import  provideRouter, RouterConfig  from '@angular/router';

import  HomeComponent  from './components/home/home.component';
import  TestComponent  from './components/test/test.component';


export const appRoutes: RouterConfig = [
  
    path: '',
    component: HomeComponent,
    terminal: true
  ,
  
    path: 'access_token',
    component: TestComponent
  
];

export const APP_ROUTER_PROVIDERS = [
  provideRouter(appRoutes)
];

如果我将发回的重定向 url 修改为类似http://localhost:4200/access_token=TOKEN 的内容(纯粹是为了测试目的),它可以正常工作。不幸的是,我实际上无法控制现实生活中重定向 url 的格式,我无法想出一个解决方案来处理它以哈希片段而不是 / 开头的事实,然后是我的查询参数。我能找到的所有带有复杂符号或字符的路由示例都以/ 开头。

我尝试将上面的解决方案修改为:access_token,但这不起作用,并将其列为基本路由下的子路由,如下所示:


  path: '',
  component: HomeComponent,
  terminal: true,
  children: [
     path: 'access_token',  component: TestComponent ,
  ]

导致以下控制台错误: platform-browser.umd.js:2312 EXCEPTION: Error: Uncaught (in promise): Error: Cannot match any routes: ''

我觉得绝对必须有一个干净的解决方案来解决这个问题,特别是因为如此多的 API 通过这样的重定向 url 处理它们的身份验证,但无论我如何挖掘文档,我似乎都找不到它。任何有关如何实现这一点的建议将不胜感激。

【问题讨论】:

【参考方案1】:

不只是假设您需要的值的索引,您可以使用以下方法从您想要的片段中获取键值对。

`

constructor(private route: ActivatedRoute) 

ngOnInit(): void 
    const fragment: string = this.route.snapshot.fragment;
    this.processFragment(fragment);


processFragment(fragmentString) 
    var ar = fragmentString.split('&');
    var result = 
    for (var i = 0; i < ar.length; i++) 
        var split = ar[i].indexOf("=");
        var key = ar[i].slice(0, split);
        result[key] = ar[i].slice(split + 1);
    
return result;

`

在“结果”对象中,您可以通过 result['key'] 访问所需的值

【讨论】:

【参考方案2】:

我最终能够找到一个使用首选 PathLocationStrategy 的解决方案,但也可以在删除哈希片段之后的部分 url 之前将令牌从 oauth 重定向 uri 中拉出(来自最终答案 here 被拉来自blog post 中的 QueryParams 和 Fragment 部分)。

基本上,我在使用 doorkeeper/oauth2 注册我的应用程序时将重定向 url 更新为 http://localhost:4200/login/(这导致包含令牌的重定向 url 看起来像 http://localhost:4200/login/#access_token=TOKEN)并添加了以下路由:


  path: 'login',
  component: LoginComponent

这会捕获重定向 url,但会删除哈希片段之后的所有内容,从而删除我需要的令牌。为了防止它删除哈希片段之后的所有内容,我将以下代码添加到我的 LoginComponent 的构造函数中:

constructor(private activatedRoute: ActivatedRoute, 
            private router: Router, 
            private tokenService: TokenService) 

// Pulls token from url before the hash fragment is removed

const routeFragment: Observable<string> = activatedRoute.fragment;
routeFragment.subscribe(fragment => 
  let token: string = fragment.match(/^(.*?)&/)[1].replace('access_token=', '');
  this.tokenService.setToken(token);
);

您选择如何处理令牌取决于您(我有一个 TokenService,其中包含从 localStorage 设置、检索和清除它的方法),但这是您在哈希片段之后访问 url 部分的方式。如果有人有更好的解决方案,请随时在此处更新/发布。

更新: 对上述登录组件代码的小更新,以处理 Angular v4.2.0 中的“片段可能为空”打字稿错误,并且在 tsconfig.json 中将 strictNullChecks 设置为 true,以防万一有人需要它。功能相同:

let routeFragment = this.activatedRoute.fragment.map(fragment => fragment);

routeFragment.subscribe(fragment => 
  let f = fragment.match(/^(.*?)&/);
  if(f) 
   let token: string = f[1].replace('access_token=', '');
   this.tokenService.setToken(token);


注意:从 RxJS 6 开始,map 运算符已成为可管道化的,这意味着您必须在 Observablepipe 方法中传递它,如下所示:

import  map  from 'rxjs/operators';

// ...

this.activatedRoute.fragment
  .pipe(map(fragment => fragment))
  .subscribe(fragment => 
    let f = fragment.match(/^(.*?)&/);
    if(f) 
      let token: string = f[1].replace('access_token=', '');
      this.tokenService.setToken(token);
    

【讨论】:

你需要从window.location.hash中拉出accessToken吗?我使用了一个基于ActivatedRoute 的稍微不同的示例,但它也有一个可观察的片段,片段的结果是 # 之后的数据。 @J.FritzBarnes 嗨,我在上面发布的原始答案截至 2016 年 7 月有效,但现在 Angular 2.0 最终版已经发布(我使用的是 2.4.0 版) )。我刚刚更新了我目前检索令牌的方式的答案,我怀疑这可能与您拉取它的方式相似,并且是我建议从 window.location.hash 拉取它的方式(当我第一次遇到这个问题时解决方案是有限的,一旦我切换到从路由片段中提取它,我就没有回去更新)。感谢您指出这一点! @NColey 你在 IE 中测试过你的解决方案吗?当用户代理被重定向回 Angular 站点到 IE 中的 /login?token=blah 之类的 url 时,我收到 404 错误,这非常奇怪,适用于 chrome 和 firefox。此外,如果我只是在浏览器的 url 地址中点击相同的 url,它就可以工作。任何想法如何解决? 嗨@EvgenyFedorenko,这个代码所在的应用程序已经运行了大约一年,据我所知,在那段时间里我们在IE上没有任何登录问题。也许 404 错误来自一个不相关的问题?我还会检查您的 oauth 设置是什么,以确保重定向 url 最后有 /login 可能它正在重定向到其他地方。 @NColey 如果有帮助,我已经更新了您的答案,以包含 RxJS 6 的更新代码 sn-p。无论如何,感谢您的回答!非常感谢。

以上是关于如何在Angular2 rc3路由中处理来自oauth重定向url的哈希片段的主要内容,如果未能解决你的问题,请参考以下文章

如何对依赖于 RC3 路由器的组件进行单元测试?

将 ES6 与 Angular2 rc3 一起使用时,需要未捕获的反射元数据 shim

Angular2:通过路由器在兄弟组件之间传递数据?

如何在提交按钮表单中使用路由 - Angular2

如何在Angular2中使用导航进行路由时显示加载屏幕?

PHP 7 RC3:如何安装缺少的 MySQL PDO