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

时间:2019-06-21 07:48:09

标签: javascript angular security oauth access-token

我已将应用程序部署到UAT,但是无法直接在移动设备上运行我的应用程序,因为它直接进入了access-denied(401)页面。我认为这是由于访问令牌问题引起的。

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

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

authentication-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能够检索数据,但是事件未按预期发送令牌。

我正在authentication.service.ts中执行以上所有代码,我不确定这样做是否正确。

有人可以建议我正确的方法来实现此代码并适当地接收令牌吗?

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

1 个答案:

答案 0 :(得分:5)

  

源代码和演示:   https://github.com/trungk18/angular-authentication-demo-with-lazy-loading

     

您可以在此存储库中找到stackblitz链接

     

我将添加该部分以在我们第一次运行时延迟加载所有其他模块。这意味着将仅在首次加载登录页面。登录后,将加载下一个模块。它将为我们节省大量带宽。


有一个用户,登录成功后,我将把整个对象存储到localStorage中。

流程可能像这样。

  1. 打开应用程序

  2. AuthGuard 将被触发。如果 localStorage 中存在带令牌的用户对象,请激活路由。如果没有,则返回到登录页面。

  3. 在激活路由并开始向服务器进行API调用后,将触发 JWTInterceptor 在每个后续请求上发送令牌。

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

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将令牌附加到标头中

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,可在激活路由之前确保您拥有令牌。配置角度路由器时,除登录页面外,它必须包含在所有路由中。

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的公共观察对象。例如,我想在列表上显示用户用户的名字

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"]);
    }
}

希望您能从中得到启发。