在加载任何组件之前从 headers 获取 Auth Token

Get Auth Token from headers before loading any components

我是 angular 的新手,正在修改代码。

目前代码使用默认 angular 方法一起加载所有组件,这意味着多个组件一起进行 http 调用。

我的 angular 应用托管在 azure 应用服务上,并通过 azure 门户配置了 azure 广告身份验证。

从第一点开始,我想改进并确保, 首先,在加载任何组件之前,我从 http://host/.auth/me url 获取 id_token 参数,这将是我的身份验证令牌。(此身份验证令牌为 auth header 将用于对后端进行 api 次调用)

一旦我有了这个授权令牌,我就会将它存储在 local/session 存储中。仅当此过程完成后,我才想继续加载其他 angular 个组件。

我已阅读有关组件延迟加载的内容。但我想知道上述情况的最佳做法是什么。

好吧,有多种方法可以做到这一点

APP_INITIALIZER

您可以编写加载令牌的异步函数,并将该函数注入主模块。在函数的承诺得到解决之前,不会初始化任何组件。这种方式简单但丑陋。令牌请求正在进行时,用户将看到白屏。

@NgModule({
  //...
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: () => () => {
        return fetch('https://token.provired.com/getToken')
          .then(x => x.json())
          .then(x => localStorage.setItem("token", x));
      },
      multi: true,
    }
  ]
})
export class AppModule { }

根组件

您可以从根组件初始化令牌请求,通常是app.cpmponent.ts。在组件的模板中,您可以在请求进行时显示一些标记或微调器,让用户知道该应用正在运行。其他组件的初始化应该被 *ngIf 阻止。检查下面的片段

@Component({
  selector: 'app-root',
  template: `
    <div *ngIf="!tokenLoaded">Please wait</div>
    <div *ngIf="tokenLoaded">
      <app-my-component></app-my-component>
    </div>`,
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
  tokenLoaded = false;

  ngOnInit(){
    getToken().then(token => {
      localStorage.setItem("token", token);
      tokenLoaded = true;
    })
  }
}

后卫

正如@Gytis TG 在评论中提到的,您可以 运行 守卫中的令牌请求。这种方法的优点是您将有一段单独的代码负责令牌加载。

我认为我们有 2 个关于如何处理组件可见性限制的想法。

在我们的应用程序中,我们可以在 auth.service.ts 中处理我们的登录和其他身份验证。

// auth.service.ts
@Injectable({
  providedIn: 'root'
})
export class AuthService {
private token: string;
private loggedIn = false;

constructor(private http: HttpClient) {}


getLoggedIn() {
  return this.loggedIn;
}

login(email:string, password:string): Promise <string> {
  const promise = new Promise < string > ((resolve, reject) => {
    const authData = { email: email, password: password }
      this.http.post < { token: string } > (environment.backendUrl + '/api/user/login', authData).toPromise()
      .then(response => {
         const token = response.token;
         this.token = token;
         if (token) {
           this.loggedIn = true;
         }
}

正如您在上面的代码片段中看到的那样,一旦登录成功,我们就会在响应正文中收到一个令牌,我认为这与您在应用程序中获取令牌的方式类似。然后我们将此 response.token 分配给 auth.service.ts 文件中的标记变量。在将令牌分配给 auth.service.ts 文件内的变量后,我们可以通过导入 auth.service.ts 文件从应用程序的任何位置调用 getLoggedIn() 函数来读取它并检查令牌。

现在回到如何通过检查令牌来限制组件的可见性。一种方法是我们可以将我们的 auth.service.ts 文件导入到我们想要限制组件可见性的组件中,方法是像这样将其添加到构造函数中

// .ts file where you want to restrict visibility
export class component {
loggedIn = false;

constructor(private authService: AuthService) {}

ngOnInit() {
   this.loggedIn = this.authService.getLoggedIn();
}

您可以看到组件正在 ngOnInit 调用期间检查令牌。这意味着在我们相应的 html 文件中,我们可以使用带有 *ngIf 的 loggedIn 变量来限制某些 html 元素甚至整个组件的可见性(如果需要的话)

// .html file
<div *ngIf=loggedIn>
   <h1> title </h1>
   <p> here you be your text </p>
</div>

另一种方法是像 mikhail 和 gytis 之前提到的那样实施守卫。这个守卫在路由到页面之前检查令牌,以确保只有登录的用户才能访问某些页面。我们可以通过添加一个 auth.guard.ts 文件来做到这一点:

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService, private router: Router) {}

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean | Observable < boolean > | Promise < boolean > {
    const loggedIn = this.authService.getLoggedIn();
    if (!loggedIn) {
      this.router.navigate(['/login']);
    }
    return loggedIn;
  }

}

添加此文件后,我们可以像这样在 app-routing.ts 文件中启用令牌检查。

// app-routing.ts file
const routes: Routes = [
    { path: "", component: HomePageComponent },
    { path: "home", component: HomePageComponent },
    { path: "login", component: LoginPageComponent },
    { path: "signup", component: SignupPageComponent },
    { path: "profile/:id", component: ProfilePageComponent, canActivate: [AuthGuard] },

]

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule],
    providers: [AuthGuard]
})
export class AppRoutingModule {}

在截图中,个人资料页面受 AuthGuard 保护,因此只有登录用户才能访问此页面。如果用户未登录,AuthGuard 会将用户重定向到登录页面,因此即使在浏览器中手动输入 url,仍然需要在访问页面之前登录。

我对 SO 也很陌生,但我尽力为您写了一个明确的答案,希望我能对您有所帮助! :) 祝你有美好的一天!