138

私は、承認されていないページにアクセスしようとするユーザーが自動的にログイン ページにリダイレクトされる Asp.Net MVC の世界から来ました。

この動作を Angular で再現しようとしています。@CanActivate デコレータに出くわしましたが、コンポーネントがまったくレンダリングされず、リダイレクトされません。

私の質問は次のとおりです。

  • Angular はこの動作を実現する方法を提供していますか?
  • もしそうなら、どのように?それは良い習慣ですか?
  • そうでない場合、Angular でユーザー認証を処理するためのベスト プラクティスは何でしょうか?
4

7 に答える 7

147

Angular 4 を使用した更新された例を次に示します(Angular 5 - 8 とも互換性があります)。

AuthGuard によって保護されたホーム ルートを持つルート

import { Routes, RouterModule } from '@angular/router';

import { LoginComponent } from './login/index';
import { HomeComponent } from './home/index';
import { AuthGuard } from './_guards/index';

const appRoutes: Routes = [
    { path: 'login', component: LoginComponent },

    // home route protected by auth guard
    { path: '', component: HomeComponent, canActivate: [AuthGuard] },

    // otherwise redirect to home
    { path: '**', redirectTo: '' }
];

export const routing = RouterModule.forRoot(appRoutes);

ユーザーがログインしていない場合、AuthGuard はログイン ページにリダイレクトします

クエリ パラメータの元の URL をログイン ページに渡すように更新

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(private router: Router) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        if (localStorage.getItem('currentUser')) {
            // logged in so return true
            return true;
        }

        // not logged in so redirect to login page with the return url
        this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
        return false;
    }
}

完全な例と動作するデモについては、この投稿をチェックしてください。

于 2016-08-16T07:29:50.657 に答える
91

更新: Github で OAuth2 統合を使用した完全なスケルトンAngular 2 プロジェクトを公開しました。これは、以下で説明するディレクティブの動作を示しています。

これを行う 1 つの方法は、directive. Angular 2 とは異なりcomponents、ページに挿入する基本的に新しい HTML タグ (関連付けられたコードを含む) ですが、属性ディレクティブは、何らかの動作を発生させるタグに配置する属性です。 ドキュメントはこちら

カスタム属性が存在すると、ディレクティブを配置したコンポーネント (または HTML 要素) に何かが起こります。現在の Angular2/OAuth2 アプリケーションで使用しているこのディレクティブを考えてみてください。

import {Directive, OnDestroy} from 'angular2/core';
import {AuthService} from '../services/auth.service';
import {ROUTER_DIRECTIVES, Router, Location} from "angular2/router";

@Directive({
    selector: '[protected]'
})
export class ProtectedDirective implements OnDestroy {
    private sub:any = null;

    constructor(private authService:AuthService, private router:Router, private location:Location) {
        if (!authService.isAuthenticated()) {
            this.location.replaceState('/'); // clears browser history so they can't navigate with back button
            this.router.navigate(['PublicPage']);
        }

        this.sub = this.authService.subscribe((val) => {
            if (!val.authenticated) {
                this.location.replaceState('/'); // clears browser history so they can't navigate with back button
                this.router.navigate(['LoggedoutPage']); // tells them they've been logged out (somehow)
            }
        });
    }

    ngOnDestroy() {
        if (this.sub != null) {
            this.sub.unsubscribe();
        }
    }
}

これは、私が書いた認証サービスを利用して、ユーザーがすでにログインしているかどうかを判断し、認証イベントにサブスクライブして、ユーザーがログアウトまたはタイムアウトした場合にユーザーを追い出すことができるようにします。

同じことができます。ユーザーが認証されていることを示す必要な Cookie やその他の状態情報が存在するかどうかをチェックする、私のようなディレクティブを作成します。探しているフラグがない場合は、ユーザーをメインの公開ページ (私のように) または OAuth2 サーバー (または何でも) にリダイレクトします。保護する必要があるコンポーネントにそのディレクティブ属性を配置します。この場合、protected上に貼り付けたディレクティブのように呼び出される可能性があります。

<members-only-info [protected]></members-only-info>

次に、ユーザーをアプリ内のログイン ビューにナビゲート/リダイレクトし、そこで認証を処理します。現在のルートを、やりたいルートに変更する必要があります。その場合、依存性注入を使用してディレクティブの関数でRouter オブジェクトを取得し、メソッドを使用してユーザーをログイン ページに送信します (上記の例のように)。constructor()navigate()

<router-outlet>これは、おそらく次のようなタグを制御する一連のルートがあることを前提としています。

@RouteConfig([
    {path: '/loggedout', name: 'LoggedoutPage', component: LoggedoutPageComponent, useAsDefault: true},
    {path: '/public', name: 'PublicPage', component: PublicPageComponent},
    {path: '/protected', name: 'ProtectedPage', component: ProtectedPageComponent}
])

代わりに、ユーザーをOAuth2 サーバーなどの外部URL にリダイレクトする必要がある場合は、ディレクティブで次のようなことを行う必要があります。

window.location.href="https://myserver.com/oauth2/authorize?redirect_uri=http://myAppServer.com/myAngular2App/callback&response_type=code&client_id=clientId&scope=my_scope
于 2015-12-17T13:35:38.647 に答える
56

ファイナルルーターとの併用

新しいルーターの導入により、ルートの保護が容易になりました。サービスとして機能するガードを定義し、それをルートに追加する必要があります。

import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { UserService } from '../../auth';

@Injectable()
export class LoggedInGuard implements CanActivate {
  constructor(user: UserService) {
    this._user = user;
  }

  canActivate() {
    return this._user.isLoggedIn();
  }
}

をルートに渡し、モジュールの配列にLoggedInGuardも追加します。providers

import { LoginComponent } from './components/login.component';
import { HomeComponent } from './components/home.component';
import { LoggedInGuard } from './guards/loggedin.guard';

const routes = [
    { path: '', component: HomeComponent, canActivate: [LoggedInGuard] },
    { path: 'login', component: LoginComponent },
];

モジュール宣言:

@NgModule({
  declarations: [AppComponent, HomeComponent, LoginComponent]
  imports: [HttpModule, BrowserModule, RouterModule.forRoot(routes)],
  providers: [UserService, LoggedInGuard],
  bootstrap: [AppComponent]
})
class AppModule {}

最終リリースでの動作に関する詳細なブログ投稿: https://medium.com/@blacksonic86/angular-2-authentication-revisited-611bf7373bf9

非推奨のルーターでの使用

より堅牢な解決策は、RouterOutletand を拡張して、ユーザーがログインしているかどうかのルート チェックを有効にすることです。この方法では、すべてのコンポーネントにディレクティブをコピー アンド ペーストする必要がありません。さらに、サブコンポーネントに基づくリダイレ​​クトは誤解を招く可能性があります。

@Directive({
  selector: 'router-outlet'
})
export class LoggedInRouterOutlet extends RouterOutlet {
  publicRoutes: Array;
  private parentRouter: Router;
  private userService: UserService;

  constructor(
    _elementRef: ElementRef, _loader: DynamicComponentLoader,
    _parentRouter: Router, @Attribute('name') nameAttr: string,
    userService: UserService
  ) {
    super(_elementRef, _loader, _parentRouter, nameAttr);

    this.parentRouter = _parentRouter;
    this.userService = userService;
    this.publicRoutes = [
      '', 'login', 'signup'
    ];
  }

  activate(instruction: ComponentInstruction) {
    if (this._canActivate(instruction.urlPath)) {
      return super.activate(instruction);
    }

    this.parentRouter.navigate(['Login']);
  }

  _canActivate(url) {
    return this.publicRoutes.indexOf(url) !== -1 || this.userService.isLoggedIn()
  }
}

UserService、ユーザーがログインしているかどうかに関係なく、ビジネス ロジックが存在する場所を表します。コンストラクターのDIで簡単に追加できます。

ユーザーが Web サイトの新しい URL に移動すると、現在の命令で activate メソッドが呼び出されます。そこから URL を取得し、許可するかどうかを決定できます。ログインページにリダイレクトするだけではない場合。

それを機能させるために残っている最後のことは、組み込みコンポーネントではなくメインコンポーネントに渡すことです。

@Component({
  selector: 'app',
  directives: [LoggedInRouterOutlet],
  template: template
})
@RouteConfig(...)
export class AppComponent { }

@CanActive渡された関数が false を解決すると、の activate メソッドが呼び出されないため、このソリューションはライフサイクル デコレータでは使用できませんRouterOutlet

また、それについての詳細なブログ投稿を書きました: https://medium.com/@blacksonic86/authentication-in-angular-2-958052c64492

于 2016-03-05T17:30:27.447 に答える
55

Router Outlet をオーバーライドしないでください。最新のルーター リリース (3.0 ベータ) では悪夢です。

代わりに、インターフェイス CanActivate および CanDeactivate を使用し、ルート定義でクラスを canActivate / canDeactivate として設定します。

そのように:

{ path: '', component: Component, canActivate: [AuthGuard] },

クラス:

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(protected router: Router, protected authService: AuthService)
    {

    }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {

        if (state.url !== '/login' && !this.authService.isAuthenticated()) {
            this.router.navigate(['/login']);
            return false;
        }

        return true;
    }
}

参照: https://angular.io/docs/ts/latest/guide/router.html#!#can-activate-guard

于 2016-07-14T09:04:58.873 に答える
2

このコード、auth.ts ファイルを参照してください

import { CanActivate } from '@angular/router';
import { Injectable } from '@angular/core';
import {  } from 'angular-2-local-storage';
import { Router } from '@angular/router';

@Injectable()
export class AuthGuard implements CanActivate {
constructor(public localStorageService:LocalStorageService, private router: Router){}
canActivate() {
// Imaginary method that is supposed to validate an auth token
// and return a boolean
var logInStatus         =   this.localStorageService.get('logInStatus');
if(logInStatus == 1){
    console.log('****** log in status 1*****')
    return true;
}else{
    console.log('****** log in status not 1 *****')
    this.router.navigate(['/']);
    return false;
}


}

}
// *****And the app.routes.ts file is as follow ******//
      import {  Routes  } from '@angular/router';
      import {  HomePageComponent   } from './home-page/home- page.component';
      import {  WatchComponent  } from './watch/watch.component';
      import {  TeachersPageComponent   } from './teachers-page/teachers-page.component';
      import {  UserDashboardComponent  } from './user-dashboard/user- dashboard.component';
      import {  FormOneComponent    } from './form-one/form-one.component';
      import {  FormTwoComponent    } from './form-two/form-two.component';
      import {  AuthGuard   } from './authguard';
      import {  LoginDetailsComponent } from './login-details/login-details.component';
      import {  TransactionResolver } from './trans.resolver'
      export const routes:Routes    =   [
    { path:'',              component:HomePageComponent                                                 },
    { path:'watch',         component:WatchComponent                                                },
    { path:'teachers',      component:TeachersPageComponent                                         },
    { path:'dashboard',     component:UserDashboardComponent,       canActivate: [AuthGuard],   resolve: { dashboardData:TransactionResolver } },
    { path:'formone',       component:FormOneComponent,                 canActivate: [AuthGuard],   resolve: { dashboardData:TransactionResolver } },
    { path:'formtwo',       component:FormTwoComponent,                 canActivate: [AuthGuard],   resolve: { dashboardData:TransactionResolver } },
    { path:'login-details', component:LoginDetailsComponent,            canActivate: [AuthGuard]    },

]; 
于 2017-05-20T11:35:23.470 に答える