Parametreyi rota korumasına geçir


110

Bu rollere göre uygulamanın bazı bölümlerinde gezinmeyi engellemek için korumaları kullanmam gereken birçok role sahip bir uygulama üzerinde çalışıyorum. Her rol için ayrı koruma sınıfları oluşturabileceğimi fark ediyorum, ancak bir şekilde bir parametre geçirebileceğim bir sınıfa sahip olmayı tercih ederim. Başka bir deyişle, buna benzer bir şey yapabilmek isterim:

{ 
  path: 'super-user-stuff', 
  component: SuperUserStuffComponent,
  canActivate: [RoleGuard.forRole('superUser')]
}

Ancak geçtiğiniz tek şey korumanızın tür adı olduğu için, bunu yapmanın bir yolunu düşünemiyorum. Sadece mermiyi ısırmalı ve rol başına bireysel koruma sınıflarını yazmalı ve bunun yerine tek bir parametreleştirilmiş türe sahip olmak için zarafet illüzyonumu kırmalı mıyım?

Yanıtlar:


231

Kullanmak yerine şunu forRole()yapabilirsiniz:

{ 
   path: 'super-user-stuff', 
   component: SuperUserStuffComponent,
   canActivate: RoleGuard,
   data: {roles: ['SuperAdmin', ...]}
}

ve bunu RoleGuard'ınızda kullanın

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

    let roles = route.data.roles as Array<string>;
    ...
}

Ayrıca harika bir seçenek, teşekkürler. Aluan'ın fabrika yöntemini biraz daha seviyorum ama olasılıklar konusunda beynimi genişlettiğin için teşekkürler!
Brian Noyes

7
Bu verilerin güvenliğinin alakasız olduğunu düşünüyorum. Sunucu tarafında kimlik doğrulama ve yetkilendirme kullanmalısınız. Bence korumanın amacı, başvurunuzu tam olarak korumak değil. Birisi onu "hacklerse" ve yönetici sayfasına giderse, sunucudan güvenli verileri almayacak, sadece bence sorun olmayan yönetici bileşenlerini görecektir. Bence bu, kabul edilen çözümden çok daha iyi. Kabul edilen çözüm, bağımlılık enjeksiyonunu ortadan kaldırır.
bucicimaci

1
Bu iyi bir çözüm ve genel AuthGuard'ımda harika çalışıyor.
SAV

4
Bu çözüm harika çalışıyor. Benim sorunum, bunun bir dolaylılık katmanına dayanması. Bu koda bakan birinin roles, kodun önceden nasıl çalıştığını bilmeden o nesnenin ve rota korumasının bağlantılı olduğunu anlamasına imkan yoktur . Angular'ın bunu daha açıklayıcı bir şekilde yapmanın bir yolunu desteklememesi berbat. (Bu bana bu mükemmel makul bir çözüm açısal bemoaning değildir temizlemek olmak.)
KhalilRavanna

1
@KhalilRavanna teşekkür ederim, evet ama bu çözümü çok önce kullandım ve başka bir çözüme geçtim. Yeni çözümüm bir RoleGaurd ve içinde Map <URL, AccessRoles> sabiti olan "access.ts" isimli bir dosya, sonra onu RoleGaurd'da kullanıyorum. Uygulamanızda erişiminizi kontrol etmek istiyorsanız, özellikle bir projede birden fazla uygulamanız olduğunda bu yeni yolun çok daha iyi olduğunu düşünüyorum.
Hasan Beheshti

12

İşte benim bu konudaki yaklaşımım ve eksik sağlayıcı sorunu için olası bir çözüm.

Benim durumumda, parametre olarak bir izin veya izin listesi alan bir korumamız var, ancak aynı şeyin bir rolü var.

Yetkilendirme korumaları ile izinli veya izinsiz ilgilenmek için bir sınıfımız var:

@Injectable()
export class AuthGuardService implements CanActivate {

    checkUserLoggedIn() { ... }

Bu, kullanıcının aktif oturumunu vb. Kontrol etmekle ilgilidir.

Aynı zamanda, aslında AuthGuardServicekendine bağlı olan, özel bir izin koruması elde etmek için kullanılan bir yöntemi de içerir.

static forPermissions(permissions: string | string[]) {
    @Injectable()
    class AuthGuardServiceWithPermissions {
      constructor(private authGuardService: AuthGuardService) { } // uses the parent class instance actually, but could in theory take any other deps

      canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        // checks typical activation (auth) + custom permissions
        return this.authGuardService.canActivate(route, state) && this.checkPermissions();
      }

      checkPermissions() {
        const user = ... // get the current user
        // checks the given permissions with the current user 
        return user.hasPermissions(permissions);
      }
    }

    AuthGuardService.guards.push(AuthGuardServiceWithPermissions);
    return AuthGuardServiceWithPermissions;
  }

Bu, yönlendirme modülümüzdeki izinler parametresine göre bazı özel korumaları kaydetmek için yöntemi kullanmamıza olanak tanır:

....
{ path: 'something', 
  component: SomeComponent, 
  canActivate: [ AuthGuardService.forPermissions('permission1', 'permission2') ] },

İlginç kısmı forPermissionise AuthGuardService.guards.push- bu temelde her zaman emin kılan forPermissionsda bu dizide depolar özel koruma sınıfı elde etmek denir. Bu aynı zamanda ana sınıfta statiktir:

public static guards = [ ]; 

Daha sonra tüm korumaları kaydetmek için bu diziyi kullanabiliriz - uygulama modülü bu sağlayıcıları kaydettiğinde rotaların tanımlandığından ve tüm koruma sınıflarının oluşturulduğundan emin olduğumuz sürece bu sorun olmaz (örn. İçe aktarma sırasını kontrol edin ve Bu sağlayıcıları listede olabildiğince düşük tutun - bir yönlendirme modülüne sahip olmak yardımcı olur):

providers: [
    // ...
    AuthGuardService,
    ...AuthGuardService.guards,
]

Bu yardımcı olur umarım.


1
Bu çözüm bana statik bir hata veriyor: HATA, sembol değerleri statik olarak çözümlenirken karşılaşıldı.
Arninja

Bu çözüm geliştirme için benim için çalıştı, ancak üretim için uygulama oluşturduğumda hata ERROR in Error during template compile of 'RoutingModule' Function calls are not supported in decorators but 'PermGuardService' was called.
veriyor

Bu, kendi yönlendirme modüllerine sahip tembel yüklü modüller ile çalışır mı?
ezmek

2

@ AluanHaddad'ın çözümü "sağlayıcı yok" hatası veriyor. İşte bunun için bir düzeltme (kirli hissettiriyor, ancak daha iyisini yapma becerisine sahip değilim).

Kavramsal olarak, bir sağlayıcı olarak, tarafından oluşturulan dinamik olarak oluşturulan her bir sınıfı kaydediyorum roleGuard.

Yani kontrol edilen her rol için:

canActivate: [roleGuard('foo')]

sahip olmalıdır:

providers: [roleGuard('foo')]

Ancak @ AluanHaddad'ın olduğu gibi çözümü roleGuard, rolesparametre aynı olsa bile, her çağrı için yeni bir sınıf oluşturacaktır . lodash.memoizeBunu kullanmak şuna benzer:

export var roleGuard = _.memoize(function forRole(...roles: string[]): Type<CanActivate> {
    return class AuthGuard implements CanActivate {
        canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
            Observable<boolean>
            | Promise<boolean>
            | boolean {
            console.log(`checking access for ${roles.join(', ')}.`);
            return true;
        }
    }
});

Unutmayın, her rol kombinasyonu yeni bir sınıf oluşturur, bu nedenle her rol kombinasyonunu bir sağlayıcı olarak kaydetmeniz gerekir . Yani eğer varsa:

canActivate: [roleGuard('foo')]ve canActivate: [roleGuard('foo', 'bar')]ikisini birden kaydetmeniz gerekecek:providers[roleGuard('foo'), roleGuard('foo', 'bar')]

Daha iyi bir çözüm, sağlayıcıları içerideki küresel bir sağlayıcı koleksiyonuna otomatik olarak kaydetmek olabilir roleGuard, ancak dediğim gibi, bunu uygulama becerilerim yok.


Bu işlevsel yaklaşımı gerçekten seviyorum, ancak DI (sınıflar) ile mixin kapanışları ek yük gibi görünüyor.
BILL
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.