Как добавить токен мобильного доступа в приложение Angular 8? - PullRequest
4 голосов
/ 21 июня 2019

Я развернул свое приложение в UAT, но не могу запустить свое приложение на мобильном устройстве, поскольку оно напрямую переходит на страницу «Отказано в доступе» (401). Я думаю, что это связано с проблемой маркера доступа.

У меня есть в основном 2 перехватчика для обработки моего заявления. 1. Перехватчик ошибок - который обрабатывает, когда есть какая-либо ошибка маршрута или неавторизованная ошибка. 2. jwt - назначить токен, который внутренне вызывает сервис аутентификации для получения токена.

Ниже приведен мой служебный файл, в котором я пытаюсь получить токен доступа в сеансе, локальном или оконном событии, отправив postMessage , что я готов получить токен.

аутентификации-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 Ответ

5 голосов
/ 26 июня 2019

Исходный код и демо: https://github.com/trungk18/angular-authentication-demo-with-lazy-loading

Вы можете найти ссылку на стек в этом хранилище

Я добавлю раздел для отложенной загрузки всех остальных модулей при первом запуске. Это означает, что только страница входа будет загружена в первый раз. После входа в систему будет загружен следующий модуль. Это сэкономит нам большую пропускную способность.


Пользователь существует, и я сохраню весь этот объект в 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, я возьму публичную наблюдаемую currentUser внутри AuthService. Например, я хочу показать имя пользователя пользователя в списке

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

Надеюсь, вы поняли это.

...