背景 -
我最近使用了我的工作应用并将其转换为使用lazy loading
。现有应用没有问题,我使用Auth0
作为authentication
。转换为lazy loading
后,当我运行应用程序时,一切似乎都运行正常。除了一个问题。
问题 -
当我从Auth0 widget
点击登录时,应用程序正常继续。但是有两件事情发生了。
Login
成功authentication
用户。 Login
失败,没有错误。
当它失败时,它会转发到auth0,然后快速重定向到主页,而不会将令牌保存到本地存储。 我可以连续登录和退出10次。其中一个登录将工作,然后它将失败9次。我没有收到任何错误消息或警告。我甚至无法想出一个解决这个问题的方法,因为有时它会起作用,当它不起作用时,没有任何改变导致它失败。
我尝试过的事情 -
运行不使用lazy loading
的应用程序。
完美运作
从另一台计算机上运行延迟加载应用程序。 存在问题
重新启动的服务器。 存在问题
更改了回调网址。 存在问题
问题 -
有谁知道解决此问题的聪明方法? 有谁知道可能导致这种事情发生的原因?
代码示例
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.ts
和app.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添加到每个模块还是仅?
答案 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