如何使用 HashLocationStrategy 和 Auth0 Lock 小部件进行用户登录

Posted

技术标签:

【中文标题】如何使用 HashLocationStrategy 和 Auth0 Lock 小部件进行用户登录【英文标题】:How to use the HashLocationStrategy with the Auth0 Lock widget for user login 【发布时间】:2017-01-31 04:18:53 【问题描述】:

更新Auth0 login sample 以在app.module.ts 中使用HashLocationStrategy 后:

import  LocationStrategy, HashLocationStrategy  from '@angular/common';
// (...)
@NgModule(
  providers: [
    provide: LocationStrategy, useClass: HashLocationStrategy,
    appRoutingProviders,
    AUTH_PROVIDERS
  ],  
//(...)

不再引发 Auth0 Lock authenticated 事件:

import  Injectable  from '@angular/core';
import  tokenNotExpired  from 'angular2-jwt';

// Avoid name not found warnings
declare var Auth0Lock: any;

@Injectable()
export class Auth0Service 

  // Configure Auth0
  lock = new Auth0Lock('I21EAjbbpf...', '....au.auth0.com', );

  constructor() 
    // Add callback for lock `authenticated` event
    this.lock.on("authenticated", (authResult) => 
      // Use the token in authResult to getProfile() and save it to localStorage
      this.lock.getProfile(authResult.idToken, function(error, profile) 
        if (error) 
          // Handle error
          return;
        

        localStorage.setItem('id_token', authResult.idToken);
        localStorage.setItem('profile', JSON.stringify(profile));
      );
    );    
  
// (...)

【问题讨论】:

你遇到了什么错误? 【参考方案1】:

您遇到此问题的原因是,Angular 2 路由器会在路由导航时自动清理 URL,导致 Auth0 Lock 永远看不到验证用户所需的数据。从 GitHub 来看,这种行为并不总是这样,但它是当前的。有关背景信息,请参阅 RC2 Router strips extra arguments from the path after matching a route 和 navigation should not preserve query params and fragment。

在执行登录时,Auth0 将请求您的浏览器导航到类似于此的 URL:

http://example.com/#access_token=RENH3twuqx&id_token=eyJ0.epcOidRwc.Qdx3ac&token_type=Bearer

此 URL 包含 Lock 识别用户已通过身份验证的所有必要信息,但是,前面提到的 Angular 路由器行为意味着在 Lock 有机会处理此信息之前,身份验证数据包含在 URL 片段中的 URL 被剥离,保留 URL 为 (http://example.com/#/)。发生这种情况是因为您很可能已经配置了与任何 URL 匹配的包罗万象的路由。

假设您配置了以下路由:

const appRoutes: Routes = [
   path: '', component: HomeComponent ,
   path: '**', redirectTo: '' 
];

免责声明:下面将显示的第一个解决方案是作为一种解决方法提供的,该解决方案被证明适用于 Angular 2.0.0、Angular 路由器 3.0.0 与 Lock 10.2 一起使用。从那以后,路由器和/或 Lock 似乎发生了一些变化,导致最初的解决方法失败了。我提供了第二种解决方法,它似乎适用于 Angular 2.4.1、Angular 路由器 3.4.1 和 Lock 10.7 p>


解决方法 #1 - (angular/core@2.0.0, angular/router@3.0.0, lock@10.2)

尝试规避此默认行为的一种可能方法是执行以下步骤:

    向处理身份验证回调请求的路由添加激活保护,以在当前 URL 似乎是登录结果时不允许激活路由(例如,包含 access_token 关键字在它的片段中。 在触发经过身份验证的事件后,强制导航到您想要的路线,以便应用程序识别登录。

您可以创建以下类:

import  Injectable  from '@angular/core';
import  CanActivate  from '@angular/router';
import  Location  from '@angular/common';

@Injectable()
export class AuthenticationCallbackActivateGuard implements CanActivate 

  constructor(private location: Location)  

  canActivate() 
    // You may want to make a more robust check here
    return this.location.path(true).indexOf("access_token") === -1;
  

将其注册为您的家乡路线的守卫:

const appRoutes: Routes = [
   path: '', component: HomeComponent, canActivate: [AuthenticationCallbackActivateGuard] ,
   path: '**', redirectTo: '' 
];

export const appRoutingProviders: any[] = [
  AuthenticationCallbackActivateGuard
];

最后,在身份验证后导航到您的路线:

this.lock.on('authenticated', (authResult) => 
  localStorage.setItem('id_token', authResult.idToken);
  this.router.navigate([''], );
);

解决方法 #2 - (angular/core@2.4.1, angular/router@3.4.1, lock@10.7)

与之前所做的类似,但是命令式导航是在警卫本身上完成的,身份验证回调数据作为片段提供,以便 Lock 在处理事件时能够看到此信息。由于导航移至守卫,您不再需要在锁定身份验证事件上进行导航。

创建以下类:

import  Injectable  from '@angular/core';
import  CanActivate  from '@angular/router';
import  Location  from '@angular/common';
import  Router  from '@angular/router';

@Injectable()
export class AuthenticationCallbackActivateGuard implements CanActivate 

  constructor(private router: Router, private location: Location)  

  canActivate() 
    var path = this.location.path(true);

    // You may want to make a more robust check here
    var isAuthenticationCallback = path.indexOf("access_token") !== -1;

    if (isAuthenticationCallback) 
      this.router.navigate([''],  fragment: path );

      return false;
    

    return true;
  

将其注册为您的家乡路线的守卫:

const appRoutes: Routes = [
   path: '', component: HomeComponent, canActivate: [AuthenticationCallbackActivateGuard] ,
   path: '**', redirectTo: '' 
];

export const appRoutingProviders: any[] = [
  AuthenticationCallbackActivateGuard
];

最后,处理认证事件:

this.lock.on('authenticated', (authResult) => 
  localStorage.setItem('id_token', authResult.idToken);
);

【讨论】:

解决方法很有效,非常好。我想知道我是否可以将身份验证处理直接放在canActivate 函数中。因为在我看来,我们正在重定向到“”,然后再次重定向到“/home”。 在 Angular 4.2.5、Router 4.2.5 中仍然有效。我现在只是在 AuthenticationCallbackActivateGuard 中使用 JWT 设置 localStorage 而不是转发它。

以上是关于如何使用 HashLocationStrategy 和 Auth0 Lock 小部件进行用户登录的主要内容,如果未能解决你的问题,请参考以下文章

Angular2 beta 7 上的 HashLocationStrategy

Location 和 HashLocationStrategy 在 beta.16 中停止工作

Angular Module 共享模块使用 父模块使用多个子模块

导航到同一条路线不刷新组件?

为啥路由保护 canLoad 不会触发,但 canActivate 会

无法绑定到“routerLink”,因为它不是已知的本机属性[重复]