在Lazy Loaded NG2 App

时间:2017-02-27 20:37:33

标签: angular lazy-loading auth0

背景 -

我最近使用了我的工作应用并将其转换为使用lazy loading。现有应用没有问题,我使用Auth0作为authentication。转换为lazy loading后,当我运行应用程序时,一切似乎都运行正常。除了一个问题。

问题 -

当我从Auth0 widget点击登录时,应用程序正常继续。但是有两件事情发生了。

  1. Login成功authentication用户。
  2. Login失败,没有错误。 当它失败时,它会转发到auth0,然后快速重定向到主页,而不会将令牌保存到本地存储。
  3. 我可以连续登录和退出10次。其中一个登录将工作,然后它将失败9次。我没有收到任何错误消息或警告。我甚至无法想出一个解决这个问题的方法,因为有时它会起作用,当它不起作用时,没有任何改变导致它失败。

    我尝试过的事情 -

    1. 运行不使用lazy loading的应用程序。 完美运作

    2. 从另一台计算机上运行延迟加载应用程序。 存在问题

    3. 重新启动的服务器。 存在问题

    4. 更改了回调网址。 存在问题

    5. 问题 -

      有谁知道解决此问题的聪明方法? 有谁知道可能导致这种事情发生的原因?

      代码示例

      app.module.ts

      /* Routing Module */
      import { AppRoutingModule } from './app-routing.module';
      
      // Shared Stuff
      import { SharedModule } from './shared/shared.module';
      
      //Page Modules
      import { HomeModule } from './home/home.module';
      
      @NgModule({
        declarations: [
          AppComponent
        ],
        imports: [
          BrowserModule,
          HttpModule,
          AppRoutingModule,
          HomeModule,
          SharedModule
        ],
        providers: [],
        bootstrap: [AppComponent]
      })
      export class AppModule { }
      

      APP-routing.module.ts

      import { NgModule }             from '@angular/core';
      import { Routes, RouterModule } from '@angular/router';
      import { ApiKeyGuard }          from "./shared/services/api.key.guard.service";
      import { ProfileGuard }         from "./shared/services/profile.guard.service";
      
      const routes: Routes = [
        { path: '', redirectTo: '/home', pathMatch: 'full' },
        { path: 'benefits', loadChildren: './benefits/benefits.module#BenefitsModule' },
        { path: 'fcra', loadChildren: './fcra/fcra.module#FcraModule' },
        { path: 'croa', loadChildren: './croa/croa.module#CroaModule' },
        { path: 'tips', loadChildren: './tips/tips.module#TipsModule' },
        { path: 'maintenance', loadChildren: './maintenance/maintenance.module#MaintenanceModule' },
        { path: 'verify-email', loadChildren: './verify-email/verify-email.module#VerifyEmailModule' },
      { path: 'profile', canActivate: [ProfileGuard], loadChildren: './profile/profile.module#ProfileModule' },
        { path: 'recommendations', canActivate: [ApiKeyGuard], loadChildren: './recommendations/recommendations.module#RecommendationsModule' }
      ];
      
      @NgModule({
        imports: [RouterModule.forRoot(routes)],
        exports: [RouterModule],
      })
      
      export class AppRoutingModule { }
      

      /shared/shared.module.ts

      import { NgModule } from '@angular/core';
      import { CommonModule } from '@angular/common';
      
      //Services
      import { ApiKeyGuard }                      from './services/api.key.guard.service';
      import { ProfileGuard }                     from './services/profile.guard.service';
      import { Auth }                             from './services/auth.service';
      
      
      @NgModule({
        imports: [
              CommonModule,
              RouterModule,
              FormsModule,
              ReactiveFormsModule,
              CollapseModule,
              ChartsModule,
              TabsModule.forRoot(),
              ToastyModule.forRoot(),
              SignaturePadModule,
        ],
        declarations: [
              HeaderComponent,
              BreadcrumbsComponent,
              FooterComponent,
              LsideComponent,
              RsideComponent,
              NAV_DROPDOWN_DIRECTIVES,
              SIDEBAR_TOGGLE_DIRECTIVES,
              AsideToggleDirective
          ],
          providers: [
                ApiKeyGuard,
              ProfileGuard,
              Auth
              ],
        exports: [
            CommonModule,
              FormsModule,
              ReactiveFormsModule,
              RouterModule,
              HeaderComponent,
              BreadcrumbsComponent
              ]
      })
      export class SharedModule { }
      

      breadcrumb是一个共享组件,它位于共享目录中。此组件在网站上显示的每个页面中呈现,并注入app.component.tsapp.component.html

      auth服务被注入breadcrumb组件。

      import { Component }                                from '@angular/core';
      import { Router, ActivatedRoute }    from '@angular/router';
      import { Auth }                     from './../services/auth.service';
      
      @Component({
          selector: 'breadcrumbs',
          templateUrl: './breadcrumb.component.html'
      })
      export class BreadcrumbsComponent {
          constructor(private router:Router, private route:ActivatedRoute, private auth: Auth) {}
          ngOnInit(): void { }
      }
      

      在breadcrumb.component.html文件中调用auth.service的登录功能。

      breadcrumb.component.html

          <a class="nav-link" (click)="auth.login()" *ngIf="!auth.authenticated()">Login / SignUp</a>
      

      验证服务

      import { Injectable }                      from '@angular/core';
      import { tokenNotExpired, JwtHelper }      from 'angular2-jwt';
      import { Router }                          from '@angular/router';
      import { myConfig }                        from './auth.config';
      import {Http, Response, Headers, URLSearchParams}         from '@angular/http';
      import { User }                            from './../models/user';
      import { LogReg }                          from './../models/logreg';
      import { STATICS }                          from './../static/static';
      
      declare var Auth0Lock: any;
      
      var options = {
          theme: {
          logo: 'assets/img/logo.png',
          primaryColor: '#779476'
          },
          languageDictionary: {
          emailInputPlaceholder: "email@example.com",
          title: "Login or SignUp"
        },
       };
      
      @Injectable()
      export class Auth {
        lock = new Auth0Lock(myConfig.clientID, myConfig.domain, options, {});
        userProfile: Object;
        logreg: LogReg;
        user: User;
      
        constructor(private router: Router, private http: Http ) {
      
          this.userProfile = JSON.parse(localStorage.getItem('profile'));
          this.user = JSON.parse(localStorage.getItem('user'));
          this.lock.on('authenticated', (authResult: any) => {
            localStorage.setItem('access_token', authResult.idToken);
            this.lock.getProfile(authResult.idToken, (error: any, profile: any) => {
              if (error) {
                console.log(error);
                return;
              }
      
                // Login Or Register User On Our Server
              this.logreg = new LogReg(profile.email_verified, profile.email);
      
              this.checkRegister(this.logreg).subscribe(
                  (res)=>{
                    console.log("Hey this runs");
                    console.log(res);
                      if (res.email_verified === false) {
                       localStorage.removeItem('profile');
                       localStorage.removeItem('api_key');
                       localStorage.removeItem('access_token');
                       localStorage.removeItem('user');
                       this.userProfile = null;
                       this.user = null;
                       this.router.navigate(['/verify-email']);
      
                      }
                  else if (res.api_key_exist === false) {
                        console.log("Hey this works")
                      localStorage.setItem('profile', JSON.stringify(profile));
                      this.userProfile = profile;
                      console.log(this.userProfile);
                      this.user = new User(profile.email, '', '', '', '', '', '', '', '', '', '', res.api_key_exist, '')
                      localStorage.setItem('user', JSON.stringify(this.user));
                      this.router.navigate(['/profile']);
      
                  } else if (res.api_key_exist === true) {
                      this.user = new User(res.user.email,
                          res.user.first_name,
                          res.user.middle_name,
                          res.user.last_name,
                          res.user.dob,
                          res.user.phone,
                          res.user.street_address,
                          res.user.city_address,
                          res.user.state_address,
                          res.user.zip_address,
                          res.user.client_ss,
                          res.api_key_exist,
                          res.api_key);
                      console.log(this.user);
                      localStorage.setItem('api_key', JSON.stringify(res.api_key));
                      localStorage.setItem('user', JSON.stringify(this.user));
                      localStorage.setItem('profile', JSON.stringify(profile));
                      this.router.navigate(['/overview']);
                  }
              },
                  (err)=>{ console.log(err);}
      
              );
            });
            this.lock.hide();
          });
        }
      
        public checkRegister(model: LogReg) {
            // Parameters obj-
            let params: URLSearchParams = new URLSearchParams();
            params.set('email', model.email);
            params.set('email_verified', model.email_verified);
      
            return this.http.get(STATICS.API_BASE + STATICS.API_LOGIN,
                { search: params }).map((res:Response) =>  res.json());
            }
      
        public login() {
          this.lock.show();
        }
      
        private get accessToken(): string {
              return localStorage.getItem('access_token');
          }
      
        private get apiKey(): string {
            var apiKey = JSON.parse(localStorage.getItem('api_key'));
              return apiKey
          }
      
        public authenticated(): boolean {
          try {
              var jwtHelper: JwtHelper = new JwtHelper();
              var token = this.accessToken;
              if (jwtHelper.isTokenExpired(token))
                  return false;
              return true;
          }
          catch (err) {
              return false;
          }
        }
      
        public logout() {
          var apiKeyExist = this.user.api_key_exist;
          console.log(apiKeyExist);
          if (apiKeyExist === true) {
            let params: URLSearchParams = new URLSearchParams();
            params.set('email', this.user.email);
            params.set('api_key', this.apiKey);
      
            localStorage.removeItem('profile');
            localStorage.removeItem('api_key');
            localStorage.removeItem('access_token');
            localStorage.removeItem('user');
            this.userProfile = null;
            this.user = null;
            this.router.navigateByUrl('/home');
      
            return this.http.get(STATICS.API_BASE + STATICS.API_LOGOUT,
              { search: params })
              .map((res: Response) => res.json())
              .subscribe((res) => {
                console.log(res);
                this.user = null;
                console.log(this.user);
              });
          } else {
            localStorage.removeItem('profile');
            localStorage.removeItem('api_key');
            localStorage.removeItem('access_token');
            localStorage.removeItem('user');
            this.userProfile = null;
            this.user = null;
            this.router.navigateByUrl('/home');
          }
        };
      }
      

      ********************更新************************* 在shared.module.ts中,我做了一个更改。

      我将导出更改为文件底部的

      export class SharedModule {
        static forRoot(): ModuleWithProviders {
          return {
            ngModule: SharedModule,
            providers: [Auth]
          };
        }
      }
      

      现在我已经能够成功连续登录和退出5次。但是,用户的图像没有显示从auth0响应中提供的图像。我现在想知道它是否仍然有点小故障,因为我需要确保在正确的位置指定forRoot和forChild。

      我在app-routing.moduel.ts文件中指定了所有路由,该文件位于应用程序的根目录中,可以在上面的代码中看到。

      在每个延迟加载的模块中都有一个路由文件。以下是家庭路线文件的示例

      家庭routing.module.ts

      import { NgModule } from '@angular/core';
      import { Routes, RouterModule } from '@angular/router';
      import { HomeComponent } from './home.component';
      
      const routes: Routes = [
         { path: '', redirectTo: 'home', pathMatch: 'full'},
        { path: 'home',    component: HomeComponent }
      ];
      
      @NgModule({
        imports: [RouterModule.forChild(routes)],
        exports: [RouterModule],
      })
      export class HomeRoutingModule {}
      

      请注意,forChild位于RouterModule上。

      配置是否正确?我还应该将shared.module.ts添加到每个模块还是仅?

1 个答案:

答案 0 :(得分:0)

因为你说当它失败时它实际上是重定向你而不是保存令牌,我有一个理论认为Auth0重定向将转到你的应用程序中某个页面,你的Auth服务尚未初始化,或者路由正在清除令牌片段。

由于角度正在进行客户端路由,默认情况下,Auth0重定向并不总是定向回当前可见网址,但更可能是上次完整导航。有时当您进行测试时可能是"/",并且由于您在根路径上立即重定向,因此片段传递的标记将被角度清除,因此可能无法通过Auth服务获取。

您可以尝试两件事。单独和组合测试所提出的解决方案。

<强> 1。明确指定Auth0应与获取的令牌重定向的位置 我建议你先试试

// in your Auth service
var options = {
  theme: {
    logo: 'assets/img/logo.png',
    primaryColor: '#779476'
  },
  languageDictionary: {
    emailInputPlaceholder: "email@example.com",
    title: "Login or SignUp"
  },
  auth: {                              // Added
    redirectUrl: window.location.href, // Added
    responseType: 'token',             // Added
  }                                    // Added
};

<强> 2。通过聆听路由器更改替换lock.on('authenticated', ..)的侦听器

在您的Auth服务构造函数中,使用以下内容替换经过身份验证的事件侦听器。请记住在获取authResult时包含其他逻辑。

constructor(private router: Router, private http: Http ) {
    /*...*/

    router
    .events
    .filter(event => event.constructor.name === 'NavigationStart')
    .filter(event => (/access_token|id_token|error/).test(event.url))
    .subscribe(() => {
        this.lock.resumeAuth(window.location.hash, (error, authResult) => {
        if (error) return console.log(error);
            localStorage.setItem('id_token', authResult.idToken);
            // TODO: Add the logic you currently have in the authenticated listener
        });
    });

    /*...*/
}

第二个例子直接来自Angular 2 Auth0 docs