如何在 Angular 8 应用程序中添加移动访问令牌?

How to add a mobile access token in Angular 8 application?

我已经将我的应用程序部署到 UAT,但我无法 运行 我的应用程序在移动设备中,因为它直接转到拒绝访问 (401) 页面。我认为这是由于访问令牌问题。

我主要有 2 个拦截器来处理我的应用程序。 1.Error 拦截器 - 当有任何路由错误或未经授权的错误时处理。 2. jwt - 分配内部调用身份验证服务获取令牌的令牌。

下面是我的服务文件,我试图通过发送我准备好的 postMessage 在会话或本地或从 window 事件中获取访问令牌收到令牌。

身份验证-service.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 检索数据,但事件没有按预期发送令牌。

我在身份验证中执行上述所有这些代码实现。service.ts,我不确定这样做是否正确。

任何人都可以建议我实施此代码并适当接收令牌的正确方法吗?

请纠正我,因为我是第一次尝试使用令牌进行部署。

Source code and demo:

https://github.com/trungk18/angular-authentication-demo-with-lazy-loading

https://stackblitz.com/edit/angular-authentication-demo-with-lazy-loading

I will add the section to lazy load all the other modules when we first run. Meaning only login page will be loaded first time. After login, the next module will be loaded. It will save us a lot of bandwidth.


有一个User,登录成功后,我会把这个object全部存入localStorage

流程可能是这样的。

  1. 打开应用程序

  2. AuthGuard 将被触发。如果localStorage中有用户object有token,则激活路由。如果没有,则返回登录页面。

  3. 一旦路由被激活并开始对服务器进行 API 调用,JWTInterceptor 将被触发以在每个后续请求。

  4. ErrorInterceptor 检查是否有 401,然后从 localStorage 中删除用户并重新加载页面。这种处理更多关于某人尝试使用不同的令牌或 object 手动更新 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;
}

auth.service

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 将令牌附加到 header 每个请求

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;
    }
}

如果我想使用用户 object,我将在 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"]);
    }
}

我希望你能从中得到灵感。