Angular 6 JWT 身份验证
Posted
技术标签:
【中文标题】Angular 6 JWT 身份验证【英文标题】:Angular 6 JWT authentication 【发布时间】:2019-03-15 07:17:36 【问题描述】:我正在使用 JWT 令牌保护我的 Angular 6 应用程序。我有一个身份验证服务来保护 UI 元素:
export class AuthService
constructor(
private http: HttpClient,
private router: Router,
public jwtHelper: JwtHelperService,
)
isAuthenticated(): boolean
return !this.isTokenExpired;
然后我可以在我的组件中调用它:
<span class="page-nav" *ngIf="!authService.isAuthenticated()">
<a mat-button routerLink="/login">
Log In
</a>
</span>
我的路线也有一个身份验证守卫:
export class AuthGuard implements CanActivate
constructor(private authService: AuthService, private router: Router)
canActivate(): boolean
if (!this.authService.isAuthenticated())
this.router.navigate(['/login']);
return false;
return true;
这一切都很好,除非令牌变得无效,然后当前页面不会路由到login
。
例如,如果我登录,获得一个有效的 JWT 令牌,我可以导航到一个受保护的页面,并出现受保护的 UI 元素(导航等)。如果我随后通过 chrome 开发工具从本地存储中删除 JWT 令牌,然后单击导航,受保护的 UI 元素按预期消失,但受保护的页面仍然存在,不会调用路由身份验证保护,并且仅路由到 login
导航到其他页面时。
我尝试在isAuthenticated()
authService 方法中进行路由,但这会导致无限循环。如何在调用 AuthGuard 之外自动路由到 login
?
编辑
路由:
const routes: Routes = [
path: '', redirectTo: '/products', pathMatch: 'full' ,
path: 'products',
component: ProductsComponent,
canActivate: [AuthGuard],
children: [
path: ':id', component: ProductComponent, canActivate: [AuthGuard] ,
],
,
path: 'search',
component: SearchComponent,
canActivate: [AuthGuard],
,
path: 'login', component: LoginComponent ,
];
@NgModule(
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
)
export class AppRoutingModule
编辑:我意识到这是预期的行为,因为 @GangadharJannu mentions,路由器不知道 UI 元素调用了 isAuthenticated()
方法,现在被隐藏了正在返回false
,直到调用 AuthService 刷新方法并执行相同的检查。我很想知道当 UI 元素调用 isAuthenticated()
方法时让路由器知道导航到 login
的最佳方法?
编辑:我的工作解决方案:
import map from 'rxjs/operators';
import interval from 'rxjs';
import JwtHelperService from '@auth0/angular-jwt';
...
public validateToken(): void
interval(1000).pipe(
map(() =>
const isExpired: boolean = this.jwtHelper.isTokenExpired(
localStorage.getItem('jwt_token'),
);
if (isExpired)
this.logout();
),
);
【问题讨论】:
我们可以看到路由设置吗? 可能是因为this.isTokenExpired
没有正确设置。
@NimaHakimi 我已经添加了路由。
@Ploppy 不,不是这样,应用程序有一个刷新计时器,每分钟更新一次 JWT 令牌,它还会检查当前令牌并在无效时导航到登录。此外,它适用于 UI 元素,只是不会被路由到 /login 的任何东西访问。
哪一个是您的受保护组件?
【参考方案1】:
我们有父级空状态只是为了保护我们应用程序的受保护部分,您可能想尝试一下。尝试类似这样的路由配置:
const routes: Routes = [
path: '',
canActivateChild: [AuthGuard],
runGuardsAndResolvers: 'always',
children: [
path: 'products',
component: ProductsComponent,
children: [
path: ':id', component: ProductComponent ,
],
,
path: 'search',
component: SearchComponent
]
,
path: '', redirectTo: '/products', pathMatch: 'full' ,
path: 'login', component: LoginComponent ,
];
注意runGuardsAndResolvers: 'always'
- 它告诉 Angular 始终运行守卫和解析器,即使在导航到相同状态时也是如此。
编辑
所以基本上你想观察本地存储并对变化做出反应,所以当令牌从存储中删除或过期时,你想立即导航到登录组件。一些想法:
您可以将过期检查的频率提高到 1 秒,这不会造成太大影响,因为目前您正在检查令牌是否在每个渲染周期都过期 您可以在每次刷新令牌时设置一个定时可观察对象,以便它在过期后一秒触发并导航到登录 要观察本地存储本身的变化,您可以搜索一些具有此功能的库 (like this one) 或直接使用 native storage change event 如果您不关心用户手动删除令牌时的用例,那么您可以在您的应用从本地存储中删除令牌时导航到登录 (question/answers)【讨论】:
谢谢@Ludevik,但我仍然有同样的行为。问题是它根本没有路由。单击导航只是检查身份验证的状态,如果它无效,则不会对 routerLink 进行操作。 所以您在点击导航时有一些自定义检查?当用户不再经过身份验证时,它什么也不做? 导航按钮上有*ngIf="authService.isAuthenticated()"
。它们在不再经过身份验证时消失,但页面不会路由到login
,直到刷新令牌,这可能是一分钟后,或者我明确地导航到其他地方。
@bordeltabernacle。您正在经历的是预期的行为。 Angular 路由器不知道您何时删除密钥。您的导航消散器有角度的原因是为模板中的表达式分配绑定
谢谢@GangadharJannu 是的,我认为这是预期的行为,我很想知道在这种情况下如何让路由器路由到login
。以上是关于Angular 6 JWT 身份验证的主要内容,如果未能解决你的问题,请参考以下文章
使用 JWT 对 Angular Universal 进行身份验证
Symfony 3 + JWT + angular 2 + 身份验证(选项方法)