javascript - 如何在 Angular 8 应用程序中添加移动访问令牌?
问题描述
我已将我的应用程序部署到 UAT,但我无法在移动设备中运行我的应用程序,因为它直接进入拒绝访问(401)页面。我认为这是由于访问令牌问题。
我主要有 2 个拦截器来处理我的应用程序。1.错误拦截器 - 当有任何路由错误或未经授权的错误时进行处理。2. jwt - 分配令牌,内部调用身份验证服务来获取令牌。
下面是我的服务文件,我试图通过发送我准备接收令牌的postMessage在会话或本地或窗口事件中获取访问令牌。
身份验证服务.ts
public getToken() {
let accessToken = null;
const auth = JSON.parse(sessionStorage.getItem('auth'));
if (auth) {
accessToken = auth.access_token;
} elseif (accessToken == null || accessToken === undefined) {
accessToken = localStorage.getItem('access_token');
}
window.addEventListener('message', function(event){
// this event should have all the necessary tokens
}, false);
// once my page is loaded to indicate that I am ready to receive the message from server side.
parent.postMessage({ askForToken:"true"}, "*");
return accessToken;
}
我正在发送 parent.postMessage 以使 window.addEventListener 检索数据,但事件未按预期发送令牌。
我在 authentication.service.ts 中执行上述所有这些代码实现,我不确定这是否是正确的方法。
谁能建议我正确的方法来实现此代码并适当地接收令牌?
请纠正我,因为我是第一次尝试使用令牌进行部署。
解决方案
源代码和演示:
https://github.com/trungk18/angular-authentication-demo-with-lazy-loading
https://stackblitz.com/edit/angular-authentication-demo-with-lazy-loading
当我们第一次运行时,我将添加该部分以延迟加载所有其他模块。这意味着只有登录页面将首次加载。登录后,将加载下一个模块。它将为我们节省大量带宽。
有一个用户,我将在登录成功后将整个对象存储到 localStorage 中。
流程可能是这样的。
打开应用程序
AuthGuard将被触发。如果localStorage中存在带有令牌的用户对象,则激活路由。如果没有,则路由回登录页面。
在路由被激活并开始对服务器进行 API 调用后,JWTInterceptor将被触发以在每个后续请求中发送令牌。
ErrorInterceptor检查是否有 401,然后从localStorage中删除用户并重新加载页面。这种处理更多的是针对某人尝试使用不同的令牌或对象手动更新 localStorage 的用例。如果令牌是正确的,并且没有来自服务器的修饰符,则它不应该发生。
模型
export const ConstValue = {
ReturnUrl: "returnUrl",
CurrentUser: "currentUser",
}
export const ConstRoutingValue = {
Login: "login"
}
export interface AICreateUser {
firstName: string;
lastName: string;
email: string;
password: string;
roleIds: string[]
}
export interface PartnerUser extends AICreateUser {
id: string;
createdAt: string;
token: string;
featureSet: string[]
}
export interface AuthDto {
email: string;
password: string;
}
身份验证服务
export class AuthService {
private _currentUserSubject: BehaviorSubject<User>;
public currentUser: Observable<User>;
public get currentUserVal(): User {
return this._currentUserSubject.value;
}
get currentUserToken() {
return this.currentUserVal.token;
}
constructor(private http: HttpClient) {
this._currentUserSubject = new BehaviorSubject<User>(this.getUserFromLocalStorage());
this.currentUser = this._currentUserSubject.asObservable();
}
login(username, password) {
return this.http.post<any>(`/users/authenticate`, { username, password })
.pipe(map(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem(ConstValue.CurrentUser, JSON.stringify(user));
this._currentUserSubject.next(user);
return user;
}));
}
logout() {
// remove user from local storage and set current user to null
localStorage.removeItem(ConstValue.CurrentUser);
this._currentUserSubject.next(null);
}
private getUserFromLocalStorage(): User {
try {
return JSON.parse(localStorage.getItem(ConstValue.CurrentUser)!);
} catch (error) {
return null!;
}
}
}
并且有一个 JwtInterceptor 将令牌附加到每个请求的标头中
export class JwtInterceptor implements HttpInterceptor {
constructor(private authenticationService: AuthService) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
let currentUser = this.authenticationService.currentUserVal;
if (currentUser && currentUser.token) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.token}`
}
});
}
return next.handle(request);
}
}
export const JWTInterceptorProvider = {
provide: HTTP_INTERCEPTORS,
useClass: JwtInterceptor,
multi: true
};
ErrorInterceptor 检查是否有 401 然后从 localStorage 中删除用户重新加载页面。
export class ErrorInterceptor implements HttpInterceptor {
constructor(private authenticationService: AuthService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(catchError(err => {
if (err.status === 401) {
// auto logout if 401 response returned from api
this.authenticationService.logout();
location.reload(true);
}
const error = err.error.message || err.statusText;
return throwError(error);
}))
}
}
export const ErrorInterceptorProvider = { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }
还有一个 AuthGuard 可确保您在激活路由之前拥有令牌。配置 Angular 路由器时,必须包含在所有路由中,登录页面除外。
export class AuthGuard implements CanActivate {
constructor(
private router: Router,
private authenticationService: AuthService
) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const currentUserToken = this.authenticationService.currentUserToken;
if (currentUserToken) {
return true;
}
// not logged in so redirect to login page with the return url
this.router.navigate(['/login'], { queryParams: { [ConstValue.ReturnUrl]: state.url }});
return false;
}
}
如果我想使用 User 对象,我将在 AuthService 中获取 currentUser 的 public observable。例如我想在列表中显示用户用户名
export class AppComponent {
currentUser: User;
constructor(
private router: Router,
private authenticationService: AuthService
) {
this.authenticationService.currentUser.subscribe(
x => (this.currentUser = x)
);
}
logout() {
this.authenticationService.logout();
this.router.navigate(["/login"]);
}
}
我希望你能从中得到想法。
推荐阅读
- pandas - 无法在python中将字符串转换为浮点数
- c# - .NET Core 3 中不触发 ShouldSerialize 方法
- javascript - 目标 Webpack 从纯 JavaScript 捆绑 TypeScript 功能
- android - 当所有活动都在后台时,从 TileService 或视图开始活动(android)
- ios - 快速重用单元格自定义视图问题
- python - 将带有 M 和 K 的字符串对象值转换为百万和千
- excel - 通过excel公式在列中列出每年、每月、每季度、每两周的日期间隔
- fortran - 从 intel MKL 调用函数时,Matlab 崩溃
- excel - 如何根据多个工作表记录制作包含唯一个人 ID 和凭据的新 Excel 工作表?
- amazon-iam - How do I add a policy the role that EC2 instances assume in an EKS cluster?