我的 Angular 11 应用程序使用带有隐式流的 oidc-client 。我用警卫保护了我的一条路线(应该只对经过身份验证的用户可用)。当我使用应用程序中的链接导航到受限区域(比如说 http://localhost:4200/restricted-area/)时,它工作得很好。然而,问题是当我尝试“手动”导航到那个页面时(例如在我的浏览器中输入 url),应用程序将我视为没有经过身份验证。当我在“正确”导航后简单地刷新页面时,也会出现同样的问题。有趣的是,在这两种情况下,在重定向到主页后,我都会再次登录。
作为一种解决方法,我正在考虑在 [canActivate] 方法的开头实现从会话存储中额外手动提取令牌,但我希望有更好的方法来解决这个问题。
AuthGuard 实现:
import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AuthService } from './shared/services/auth.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private router: Router, private authService: AuthService) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (this.authService.isAuthenticated()) { return true; }
alert('Authentication required.');
this.router.navigate(['/menu'], { queryParams: { redirect: state.url }, replaceUrl: true });
return false;
}
}
AuthService 实现如下所示:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { UserManager, UserManagerSettings, User } from 'oidc-client';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private _authNavStatusSource = new BehaviorSubject<boolean>(false);
authNavStatus$ = this._authNavStatusSource.asObservable();
private manager = new UserManager(getClientSettings());
private user: User | null;
constructor(private http: HttpClient) {
this.manager.getUser().then(user => {
this.user = user;
this._authNavStatusSource.next(this.isAuthenticated());
});
}
login() {
return this.manager.signinRedirect();
}
async completeAuthentication() {
this.user = await this.manager.signinRedirectCallback();
this._authNavStatusSource.next(this.isAuthenticated());
}
register(userRegistration: any) {
// return this.http.post(this.configService.authApiURI + '/account', userRegistration).pipe(catchError(this.handleError)); // temporary disabled
}
isAuthenticated(): boolean {
return this.user != null && !this.user.expired;
}
get authorizationHeaderValue(): string {
return `${this.user.token_type} ${this.user.access_token}`;
}
get name(): string {
return this.user != null ? this.user.profile.name : '';
}
async signout() {
await this.manager.signoutRedirect();
}
}
export function getClientSettings(): UserManagerSettings {
return {
authority: 'http://xxxxxxxxxx',
client_id: 'xxxxxxxxxxx',
redirect_uri: 'http://xxxxxxxxxxx/auth-callback',
post_logout_redirect_uri: 'http://xxxxxxxxxxx',
response_type:"id_token token",
scope:"xxxxxxxxxxxxxxxxxxxxxxxxxx",
filterProtocolClaims: true,
loadUserInfo: true,
automaticSilentRenew: true,
silent_redirect_uri: 'xxxxxxxxxxxxxxxxxxxxxxxxx'
};
}
路由(应用程序):
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthCallbackComponent } from './auth-callback/auth-callback.component';
import { AuthGuard } from './auth.guard';
import { MenuComponent } from './menu/menu.component';
const routes: Routes = [
{ path: 'menu', component: MenuComponent },
{ path: 'auth-callback', component: AuthCallbackComponent },
{ path: 'suppliers', redirectTo: 'suppliers', pathMatch: 'full', canActivate: [AuthGuard] },
{ path: '**', redirectTo: '/menu', pathMatch: 'full' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
路由(父模块):
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthGuard } from '../auth.guard';
import { SupplierContactFormComponent } from './supplier-contact-form/supplier-contact-form.component';
import { SupplierFormComponent } from './supplier-form/supplier-form.component';
import { SupplierListComponent } from './supplier-list/supplier-list.component';
import { SupplierViewComponent } from './supplier-view/supplier-view.component';
import { SupplierResolver } from './supplier.resolver';
import { SupplierComponent } from './supplier/supplier.component';
const routes: Routes = [
{
path: 'suppliers', canActivate: [AuthGuard], children: [
{ path: '', component: SupplierListComponent },
{ path: ':supplierId', component: SupplierComponent, resolve: { supplier: SupplierResolver }, children: [
{ path: '', pathMatch: 'full', component: SupplierViewComponent },
{ path: 'edit', component: SupplierFormComponent},
{ path: 'create-contact', component: SupplierContactFormComponent },
{ path: 'edit-contact/:contactId', component: SupplierContactFormComponent },
] },
]
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class SupplierRoutingModule { }