Giriş sayfasına açısal yönlendirme


122

Yetkili olmadıkları bir sayfaya erişmeye çalışan kullanıcıların otomatik olarak oturum açma sayfasına yönlendirildiği Asp.Net MVC dünyasından geliyorum.

Bu davranışı Angular'da yeniden oluşturmaya çalışıyorum. @CanActivate dekoratörüyle karşılaştım, ancak bileşenin hiç işlememesine neden oluyor, yönlendirme yok.

Sorum şu:

  • Angular, bu davranışa ulaşmak için bir yol sağlıyor mu?
  • Öyleyse nasıl? Bu iyi bir uygulama mı?
  • Değilse, Angular'da kullanıcı yetkilendirmesini ele almak için en iyi uygulama nedir?

Eğer bakmak isterseniz, kimlik doğrulama işlerinin nasıl yapılacağını gösteren gerçek bir yönerge ekledim.
Michael Oryl

Yanıtlar:


86

Güncelleme: Github'da OAuth2 entegrasyonuna sahip, aşağıda belirtilen yönergeyi çalışırken gösteren tam bir Angular 2 projesi yayınladım .

Bunu yapmanın bir yolu, a directive. componentsTemelde sayfanıza eklediğiniz (ilişkili kodla birlikte) yeni HTML etiketleri olan Angular 2'nin aksine , bir nitelik yönergesi, bazı davranışların oluşmasına neden olan bir etikete koyduğunuz bir özniteliktir. Dokümanlar burada .

Özel özniteliğinizin varlığı, yönergeyi yerleştirdiğiniz bileşene (veya HTML öğesine) bazı şeylerin olmasına neden olur. Mevcut Angular2 / OAuth2 uygulamam için kullandığım şu yönergeyi göz önünde bulundurun:

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

Bu, kullanıcının zaten oturum açıp açmadığını belirlemek için yazdığım bir Kimlik Doğrulama hizmetini kullanıyor ve ayrıca oturum kapatırsa veya zaman aşımına uğrarsa bir kullanıcıyı dışarı atabilmesi için kimlik doğrulama olayına abone olur.

Sen de aynı şeyi yapabilirsin. Benimki gibi, gerekli bir tanımlama bilgisinin veya kullanıcının kimliğinin doğrulandığını gösteren diğer durum bilgilerinin varlığını kontrol eden bir yönerge oluşturursunuz. Aradığınız bayrakları yoksa, kullanıcıyı ana genel sayfanıza (benim yaptığım gibi) veya OAuth2 sunucunuza (veya her neyse) yönlendirin. Bu yönerge özniteliğini korunması gereken herhangi bir bileşene koyarsınız. Bu durumda, protectedyukarıda yapıştırdığım direktifte olduğu gibi çağrılabilir .

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

Ardından, kullanıcıyı uygulamanızda bir oturum açma görünümüne yönlendirmek / yönlendirmek ve orada kimlik doğrulamasını yapmak istersiniz. Mevcut rotayı, bunu yapmak istediğiniz rotayla değiştirmeniz gerekir. Bu durumda , yönergenizin işlevinde bir Yönlendirici nesnesi almak için bağımlılık enjeksiyonunu kullanırsınız constructor()ve ardından navigate()kullanıcıyı oturum açma sayfanıza göndermek için yöntemi kullanırsınız (yukarıdaki örneğimde olduğu gibi).

Bu, bir yerde <router-outlet>aşağıdakine benzer bir etiketi kontrol eden bir dizi rotanız olduğunu varsayar , örneğin:

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

Bunun yerine, kullanıcıyı OAuth2 sunucunuz gibi harici bir URL'ye yönlendirmeniz gerekirse , direktifinizin aşağıdaki gibi bir şey yapmasını sağlayabilirsiniz:

window.location.href="https://myserver.com/oauth2/authorize?redirect_uri=http://myAppServer.com/myAngular2App/callback&response_type=code&client_id=clientId&scope=my_scope

4
İşe yarıyor! Teşekkürler! Ayrıca burada başka bir yöntem buldum - github.com/auth0/angular2-authentication-sample/blob/master/src/… Hangi yöntemin daha iyi olduğunu söyleyemem ama belki birisi de onu yararlı bulabilir.
Sergey

3
Teşekkür ederim ! Ayrıca / protected /: returnUrl parametresini içeren yeni bir yol ekledim, returnUrl, yönergenin ngOnInit'inde durdurulan location.path (). Bu, başlangıçta istenen url'ye giriş yaptıktan sonra kullanıcının gezinmesini sağlar.
Amaury

1
Basit bir çözüm için aşağıdaki yanıtlara bakın. Bu kadar yaygın olan her şey (kimliği doğrulanmamışsa yeniden yönlendirin) tek bir cümle cevabı ile basit bir çözüme sahip olmalıdır.
Rick O'Shea

7
Not: Bu cevap, Angular 2'nin beta veya sürüm aday versiyonuna yöneliktir ve Angular 2 final için artık geçerli değildir.
jbandi

1
Şimdi Açısal Muhafızları kullanarak buna çok daha iyi bir çözüm var
mwilson

116

İşte Angular 4 kullanan güncellenmiş bir örnek (ayrıca Angular 5 - 8 ile uyumludur)

AuthGuard tarafından korunan ana rotaya sahip rotalar

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, kullanıcı oturum açmamışsa oturum açma sayfasına yönlendirir

Sorgu parametrelerindeki orijinal url'yi giriş sayfasına geçirmek için güncellendi

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

Tam örnek ve çalışma demosu için bu gönderiye göz atabilirsiniz.


6
Bir takip Q'm var, keyfi bir değerin currentUseriçinde olarak ayarlandığında localStoragekorumalı rotaya yine de erişilebilecek mi? Örneğin. localStorage.setItem('currentUser', 'dddddd')?
jsd

2
İstemci tarafı güvenliğini atlar. Ancak, sunucu tarafı işlemler için gerekli olan belirteci de temizler, böylece uygulamadan hiçbir yararlı veri çıkarılamaz.
Matt Meng

55

Son yönlendirici ile kullanım

Yeni yönlendiricinin tanıtılmasıyla rotaları korumak daha kolay hale geldi. Hizmet olarak hareket eden bir koruma tanımlamanız ve bunu rotaya eklemeniz gerekir.

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

Şimdi LoggedInGuardrotayı rotaya aktarın ve ayrıca onu providersmodül dizisine ekleyin .

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

Modül beyanı:

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

Son sürümle nasıl çalıştığı hakkında ayrıntılı blog yazısı: https://medium.com/@blacksonic86/angular-2-authentication-revisited-611bf7373bf9

Kullanımdan kaldırılan yönlendiriciyle kullanım

Daha sağlam bir çözüm, RouterOutletbir rota kontrolünü ve etkinleştirirken kullanıcının oturum açıp açmadığını genişletmektir . Bu şekilde direktifinizi kopyalayıp her bileşene yapıştırmanız gerekmez. Ayrıca, bir alt bileşene göre yeniden yönlendirme yapmak yanıltıcı olabilir.

@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İş mantığı kullanıcı veya kayıt tutulmasını olmadığını bulunduğu yerde yer almaktadır. Yapıcıda DI ile kolayca ekleyebilirsiniz.

Kullanıcı web sitenizde yeni bir url'ye gittiğinde, etkinleştirme yöntemi mevcut Talimat ile çağrılır. Ondan url'yi alabilir ve izin verilip verilmediğine karar verebilirsiniz. Sadece giriş sayfasına yönlendirmek değilse.

Çalışması için geriye kalan son bir şey, onu yerleşik bileşen yerine ana bileşenimize geçirmektir.

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

Bu çözüm, @CanActiveyaşam döngüsü dekoratörü ile kullanılamaz , çünkü ona iletilen işlev yanlışı çözerse, etkinleştirme yöntemi RouterOutletçağrılmayacaktır.

Ayrıca bununla ilgili ayrıntılı bir blog yazısı yazdı: https://medium.com/@blacksonic86/authentication-in-angular-2-958052c64492


2
Ayrıca bu konuda daha ayrıntılı bir blog yazısı yazdı medium.com/@blacksonic86/…
Blacksonic

Merhaba @Blacksonic. NG2'yi araştırmaya başladım. Önerinizi takip ettim ancak gulp-tslint sırasında bu hatayı aldım: Failed to lint <classname>.router-outlet.ts[15,28]. In the constructor of class "LoggedInRouterOutlet", the parameter "nameAttr" uses the @Attribute decorator, which is considered as a bad practice. Please, consider construction of type "@Input() nameAttr: string". Bu mesajdan kurtulmak için yapıcıda ("_parentRounter") neyin değiştirileceğini anlayamadım. Düşüncesi olan var mı?
leovrf

bildirim, genişletilmiş sınıfla aynı imzaya sahip olması için temel yerleşik nesne RouterOutlet'ten kopyalanır, bu satır için belirli tslint kuralını devre dışı bırakırdım
Blacksonic

Mgechev stil kılavuzunda bir referans buldum ("@ Attribute parametre dekoratörü yerine girişleri tercih et" konusuna bakın). Satır olarak değiştirildi _parentRouter: Router, @Input() nameAttr: string,ve tslint artık hatayı yükseltmiyor. Ayrıca açısal çekirdekten "Girdi" için "Öznitelik" içe aktarımı değiştirildi. Bu yardımcı olur umarım.
leovrf

1
2.0.0-rc.1 ile ilgili bir sorun var çünkü RouterOutlet dışa aktarılmıyor ve genişletme imkanı yok
mkuligowski 01

53

Lütfen, Yönlendirici Çıkışını geçersiz kılmayın! En son yönlendirici sürümü (3.0 beta) ile tam bir kabus.

Bunun yerine CanActivate ve CanDeactivate arayüzlerini kullanın ve rota tanımınızda sınıfı canActivate / canDeactivate olarak ayarlayın.

Bunun gibi:

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

Sınıf:

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

Ayrıca bkz .: https://angular.io/docs/ts/latest/guide/router.html#!#can-activate-guard


2
Güzel olan @ Blacksonic'in cevabı, benim için kullanımdan kaldırılan yönlendiriciyle mükemmel bir şekilde çalışıyordu. Yeni yönlendiriciye yükselttikten sonra çok fazla yeniden düzenleme yapmam gerekti. Çözümünüz tam da ihtiyacım olan şey!
evandongen

App.component'imde çalışmak için canActivate alamıyorum. Kimliği doğrulanmamışsa kullanıcıyı yeniden yönlendirmek istiyorum. Bu, sahip olduğum yönlendiricinin sürümüdür (Güncellemem gerekirse git bash komut satırını kullanarak bunu nasıl yaparım?) Sahip olduğum Sürüm: "@ angular / router": "2.0.0-rc.1"
AngularM

başka bir bileşen yolunu korumak için aynı sınıfı (AuthGuard) kullanabilir miyim?
tsiro

4

Yukarıdaki harika cevapların ardından, CanActivateChildçocuk rotalarını korumak da istiyorum . guardACL'ler gibi durumlarda yardımcı olacak çocuk rotaları eklemek için kullanılabilir

Bu böyle devam ediyor

src / app / auth-guard.service.ts (alıntı)

import { Injectable }       from '@angular/core';
import {
  CanActivate, Router,
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
  CanActivateChild
}                           from '@angular/router';
import { AuthService }      from './auth.service';

@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild {
  constructor(private authService: AuthService, private router:     Router) {}

  canActivate(route: ActivatedRouteSnapshot, state:    RouterStateSnapshot): boolean {
    let url: string = state.url;
    return this.checkLogin(url);
  }

  canActivateChild(route: ActivatedRouteSnapshot, state:  RouterStateSnapshot): boolean {
    return this.canActivate(route, state);
  }

/* . . . */
}

src / app / admin / admin-routing.module.ts (alıntı)

const adminRoutes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [AuthGuard],
    children: [
      {
        path: '',
        canActivateChild: [AuthGuard],
        children: [
          { path: 'crises', component: ManageCrisesComponent },
          { path: 'heroes', component: ManageHeroesComponent },
          { path: '', component: AdminDashboardComponent }
        ]
      }
    ]
  }
];

@NgModule({
  imports: [
    RouterModule.forChild(adminRoutes)
  ],
  exports: [
    RouterModule
  ]
})
export class AdminRoutingModule {}

Bu, https://angular.io/docs/ts/latest/guide/router.html#!#can-activate-guard adresinden alınmıştır.


2

Bu kodu, auth.ts dosyasına bakın

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]    },

]; 

1

1. Create a guard as seen below. 2. Install ngx-cookie-service to get cookies returned by external SSO. 3. Create ssoPath in environment.ts (SSO Login redirection). 4. Get the state.url and use encodeURIComponent.

import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from 
  '@angular/router';
import { CookieService } from 'ngx-cookie-service';
import { environment } from '../../../environments/environment.prod';

@Injectable()
export class AuthGuardService implements CanActivate {
  private returnUrl: string;
  constructor(private _router: Router, private cookie: CookieService) {}

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if (this.cookie.get('MasterSignOn')) {
      return true;
    } else {
      let uri = window.location.origin + '/#' + state.url;
      this.returnUrl = encodeURIComponent(uri);      
      window.location.href = environment.ssoPath +  this.returnUrl ;   
      return false;      
    }
  }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.