Angular - Auth Guard предотвращает доступ авторизованных пользователей к страницам аутентификации через прямой URL - PullRequest
0 голосов
/ 21 июня 2019
 src
  |_auth/
    |_authentication/
    |_auth-service.ts
    |_auth-guard.ts
    |_is-logged-guard.ts
  |_dashboard/

auth-guard-service.ts

export class AuthService {
  public user: Observable<firebase.User>;
  public userDetails: firebase.User = null;
  public userProfileRef: firebase.database.Reference;
  userData: any[] = [];
  constructor(private _firebaseAuth: AngularFireAuth, private router: Router) {
    this.user = _firebaseAuth.authState;
    this.userProfileRef = firebase.database().ref('/userProfile');
        this.user.subscribe(
      (user) => {
        if (user) {
          this.userDetails = user;
        } else {
          this.userDetails = null;
        }
      }
    );
  }

  isLoggedIn() {
    if (this.userDetails == null) {
      return false;
    } else {
      return true;
    }
  }

  doSignOut() {
    this._firebaseAuth.auth.signOut()
      .then((res) => this.router.navigate(['/auth/login']));
  }
}


auth-guard.ts

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private auth: AuthService, private router: Router) { }

  canActivate() {
    return this.auth.user.take(1).map(authState => !!authState).do(authenticated => { new Promise<boolean>( (resolve, reject) => {
      if (!authenticated) {
        this.router.navigate(['auth/sigin']);
        return resolve(false);
      } else {
        return resolve(true);
      }
  }

}

is-logged-guard.ts - Я знаю, что это проблема.Как я это исправлю?

@Injectable()
export class IsLoggedGuard implements CanActivate {

  constructor(private auth: AuthService, private router: Router) { }

  canActivate() {
    return !this.auth.isLoggedIn();
  }

}

app-routing.module.ts


const routes: Routes = [
  { path: 'dashboard',
    canActivate: [AuthGuard],
    loadChildren: './dashboard/dashboard.module#DashboardModule'
  },
  {
    path: 'auth',
    component: NbAuthComponent,
    canActivate: [IsLoggedGuard],
    children: [
      {
        path: '',
        component: SignInComponent,
      },
      {
        path: 'SignIn',
        component: SignInComponent,
      },
      {
        path: 'SignUp',
        component: SignUpComponent,
      },
    ],
  },
  { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
  { path: '**', redirectTo: 'dashboard' },
];

const config: ExtraOptions = {
  useHash: true,
};

@NgModule({
  imports: [RouterModule.forRoot(routes, config)],
  exports: [RouterModule],
})
export class AppRoutingModule {
}

Случай 1: пользователь не вошел в систему

Нет проблем.Auth guard защищает панель управления от неаутентифицированных пользователей и перенаправляет их на страницы авторизации (например, на страницу входа).

Случай 2: пользователь уже вошел в систему #

Нет проблем.Если аутентифицированные пользователи получают доступ к панели мониторинга через localhost: 4200 или localhost: 4200 / # / dashboard или localhost: 4200 / # / или localhost: 4200 / # / RANDOM_INVALID_URL, все это работает.Кроме того, защита не позволит аутентифицированным пользователям, которые уже находятся на панели инструментов, получить доступ к страницам аутентификации.

Случай 3: пользователь уже вошел в систему

Проблема.Если аутентифицированные пользователи получают доступ к панели мониторинга через localhost: 4200 / # / auth или localhost: 4200 / # / auth /, вход в систему, защита не сможет защитить и перенаправить пользователя на домашнюю страницу панели мониторинга.(Т.е. Джон уже вошел в систему и открыл новую вкладку Chrome и ввел localhost: 4200 / # / auth, сторож не помешает ему получить к нему доступ).Как я могу исправить свою охрану, чтобы предотвратить доступ Джона к страницам аутентификации, если он уже аутентифицирован?

Ответы [ 2 ]

1 голос
/ 21 июня 2019

Вы должны изменить свой IsLoggedGuard следующим образом:

@Injectable()
export class IsLoggedGuard implements CanActivate {

  constructor(private auth: AuthService, private router: Router) { }

  canActivate() {
    return this.auth.user
                    .take(1)
                    .map(authState => {
                       if (authState) {
                          //user is already loggedin
                          //route the user to Dashboard page
                          //Or a page where you want the app to naviagte
                          this.router.navigate("dashboard route");
                          //dont show the Login page
                          return false;
                       } else {
                         //user is not loggedin
                         return true;
                       }
                    });
  }

}

Вы видели проблему, потому что, когда вы вводите URL "localhost: 4200 / # / auth" в браузере, тогда ваш AuthGuard.user.subscribe [т.е. в конструкторе this.user.subscribe(], возможно, еще не выдало никакого значения, когда IsLoggedGuard's canActivate() выполнено [т.е. AuthService.isLoggedIn () может возвращать false, поскольку обратный вызов подписки мог не выполняться (что заполняет userDetails)].

Дайте мне знать, если это решит вашу проблему.

Может быть лучший способ реализовать ваш AuthService, а также Guards для использования AuthService. Дайте мне знать, если вы хотите лучший код.

РЕДАКТИРОВАТЬ - Другой подход к написанию AuthService

Давайте изменим AuthService следующим образом:

export class AuthService {

    //NOTE: I AM JUST SHOWING TWO THINGS - isUserLoggedIn AND userDetails
    //FROM THIS CODE YOU WILL GET AN IDEA HOW TO WRITE OTHER PROPERTIES WHICH ARE RELEVANT FOR YOUR APP

    //This will be used as a source for various observables
    private _authState$: Observable<any>;

    //Have an observable which will tell if user is loggedin or not
    isUserLoggedIn$: Observable<boolean>;
    userDetails$: Observable<firebase.User>;

    public userProfileRef: firebase.database.Reference;

    constructor(private _firebaseAuth: AngularFireAuth, private router: Router) {            
      this.userProfileRef = firebase.database().ref('/userProfile');
      this.setupObserables();
    }

    setupObserables() {

        // this observable will broadcast the emited values to multiple subscribers [or composed/dependent observables]
        this._authState$ = this._firebaseAuth.authState
                                        .publishReplay(1)
                                        .refCount();

        // lets componse/derive different observables required by the consumer of this service

        // This observable's emitted value will tell if user is logged in or not
        this.isUserLoggedIn$ = this._authState$
                                   .map(user => {
                                        return user ? true : false;
                                    });

        // This observable's emited value will return the user's detail [NOTE If user is not logged in then the emitted value will be NULL
        // i.e. userDetail is NULL; Your consumer of this observable should decide what to do with NULL/NOT NULL Value]        
        this.userDetails$ = this._authState$
                                .map(user => user);
    }    

    doSignOut() {
      this._firebaseAuth.auth.signOut()
        .then((res) => this.router.navigate(['/auth/login']));
    }
  }

Теперь давайте воспользуемся обновленным AuthService в IsLoggedGuard:

    @Injectable()
    export class IsLoggedGuard implements CanActivate {

      constructor(private auth: AuthService, private router: Router) { }

      canActivate() {
        return this.auth.isUserLoggedIn$
                        .take(1)
                        .map(isLoggedIn => {
                           if (isLoggedIn) {
                              //user is already loggedin
                              //route the user to Dashboard page
                              //Or a page where you want the app to naviagte
                              this.router.navigate("dashboard route");
                              //dont show the Login page
                              return false;
                           } else {
                             //user is not loggedin
                             return true;
                           }
                        });
      }

    }

Теперь давайте воспользуемся обновленным AuthService в AuthGuard:

    @Injectable()
    export class AuthGuard implements CanActivate {

    constructor(private auth: AuthService, private router: Router) { }

    canActivate() {
        return this.auth.isUserLoggedIn$
                   .take(1)
                   .map(isLoggedIn => {
                    if (!isLoggedIn) {
                       //user isNOT loggedin
                       //route the user to login page
                       this.router.navigate(['auth/sigin']);
                       //dont show the next route
                       //lets fail the guard
                       return false;
                    } else {
                      //user is loggedin; pass the guard i.e. show the next route associated with this guard
                      return true;
                    }
                 });
        }

    }

Теперь предположим, что какой-то компонент (предположим, имя компонента UserComponent), вы хотите показать подробности пользователя, зарегистрированного в системе:

....component decorator...
export class UserComponent implements OnInit {

    userDetails$: Observable<User>;
    constructor(private _authService: AuthService) {
        this.userDetails$ = this._authService.userDetails$;
    }
}

Отобразить пользовательские детали, как это:

<div *ngIf="(userDetails$ | async) as userDetails">
    <!-- Render your user details here -->
    <!-- If userDetails is NULL then nothing will be rendered -->
</div>

ВНИМАНИЕ - В этом обновленном коде НИГДЕ мы подписываемся на любую из наблюдаемых. Обратите внимание, async в шаблоне компонента, это заботится о подписке / отмене подписки на используемые наблюдаемые.

Надеюсь, это даст вам направление / идею. Давайте будем как можно более "Реактивными" вместо "Императивов" .. :) 1039 *

ПРИМЕЧАНИЕ: Я протестировал эквивалентный код в rxjs6. Похоже, вы используете rxjs5, поэтому я настроил опубликованный код в соответствии с rxjs5. Надеюсь, это сработает.

0 голосов
/ 21 июня 2019

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

ngOnInit() {
    if(localStorage.getItem('isLoggedIn')){
        this.router.navigate(['/']);
        return;
    }
}

Вот моя конфигурация маршрутов

const routes: Routes = [
    { path: '', loadChildren: './modules/dashboard/dashboard.module#DashboardModule', canActivate: [AuthGuard] },
    { path: 'auth', loadChildren: './modules/auth/auth.module#AuthModule' },
    { path: '**', component: NotFoundComponent }
];

Если у вас много маршрутов, для которых не требуется аутентификация, вместо этого создайте для них другую защиту (не ту, которая была создана для аутентифицированных маршрутов) и проверьте, вошел ли пользователь в систему, чем перенаправить на маршрут по умолчанию для аутентифицированных маршрутов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...