मार्ग रक्षक में पैरामीटर पारित करें


103

मैं एक ऐसे ऐप पर काम कर रहा हूं जिसमें बहुत सारी भूमिकाएँ हैं जिन्हें मुझे उन भूमिकाओं के आधार पर ऐप के कुछ हिस्सों में नौसेना को ब्लॉक करने के लिए गार्ड का उपयोग करने की आवश्यकता है। मुझे लगता है कि मैं प्रत्येक भूमिका के लिए अलग-अलग गार्ड कक्षाएं बना सकता हूं, लेकिन एक वर्ग होगा कि मैं किसी भी तरह एक पैरामीटर पास कर सकता हूं। दूसरे शब्दों में, मैं इसके समान कुछ करने में सक्षम होना चाहूंगा:

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

लेकिन जब से आप पास होते हैं, आपके गार्ड का नाम होता है, ऐसा करने का कोई तरीका नहीं सोच सकता। क्या मुझे बस गोली मार देनी चाहिए और प्रति व्यक्ति अलग-अलग अभिभावक वर्ग लिखना चाहिए और एकल पैरामीटर वाले टाइप के बजाय मेरे लालित्य के भ्रम को दूर करना चाहिए?

जवाबों:


220

उपयोग करने के बजाय forRole(), आप यह कर सकते हैं:

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

और अपने रोलगार्ड में इसका उपयोग करें

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

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

साथ ही बढ़िया विकल्प, धन्यवाद। मुझे अलुआन की फैक्ट्री पद्धति अच्छी लगती है, हालांकि यह एक ट्रेस बिट बेहतर है, लेकिन संभावनाओं पर मेरे दिमाग का विस्तार करने के लिए धन्यवाद!
ब्रायन नॉयस

7
मुझे लगता है कि इस डेटा की गोपनीयता अप्रासंगिक है। आपको सर्वर साइड पर प्रमाणीकरण और प्राधिकरण का उपयोग करना होगा। मुझे लगता है कि गार्ड की बात पूरी तरह से आपके आवेदन की रक्षा करने के लिए नहीं है। यदि कोई इसे "हैक" करता है और व्यवस्थापक पृष्ठ पर नेविगेट करता है, तो उसे सर्वर से सुरक्षित डेटा नहीं मिलेगा, केवल आपको वे घटक दिखाई देंगे जो मेरे विचार में ठीक हैं। मुझे लगता है कि यह स्वीकार किए जाते हैं बेहतर समाधान है। स्वीकृत समाधान निर्भरता इंजेक्शन को तोड़ता है।
बुसीकीमासी

1
यह अच्छा समाधान है और यह मेरे जेनेरिक AuthGuard में बहुत अच्छा काम करता है।
SAV

3
यह समाधान महान काम करता है। मेरा मुद्दा यह है कि यह अप्रत्यक्ष की एक परत पर निर्भर करता है। इस कोड को देखने का कोई तरीका नहीं है कि उस rolesवस्तु और मार्ग रक्षक को यह पता चले कि बिना कोड कैसे समय से पहले काम करता है। यह बेकार है कि कोणीय एक अधिक घोषणात्मक तरीके से ऐसा करने का समर्थन नहीं करता है। (स्पष्ट होने के लिए यह मुझे कोणीय है, यह पूरी तरह से उचित समाधान नहीं है।)
खलीलरावण

1
@KhalilRavanna आपको धन्यवाद, हां, लेकिन मैंने इस समाधान का बहुत पहले इस्तेमाल किया था और मैं दूसरे समाधान में चला गया। मेरा नया समाधान एक रोलगार्ड है और एक फ़ाइल "access.ts" नाम के साथ मैप <URL, AccessRoles> के साथ स्थिर है, फिर मैं इसे रोलगार्ड में उपयोग कर रहा हूं। यदि आप अपने ऐप में अपनी पहुंच को नियंत्रित करना चाहते हैं, तो मुझे लगता है कि यह नया तरीका विशेष रूप से बेहतर है जब आपके पास एक प्रोजेक्ट में एक से अधिक ऐप हैं।
हसन बेहेशती

11

यहां मेरा इस पर ध्यान देना और अनुपलब्ध प्रदाता समस्या का एक संभावित समाधान है।

मेरे मामले में, हमारे पास एक गार्ड है जो पैरामीटर के रूप में अनुमति की अनुमति या सूची लेता है, लेकिन यह एक ही बात है जिसमें एक भूमिका है।

हमारे पास अनुमति के साथ या बिना सुरक्षा गार्ड से निपटने के लिए एक वर्ग है:

@Injectable()
export class AuthGuardService implements CanActivate {

    checkUserLoggedIn() { ... }

यह उपयोगकर्ता सक्रिय सत्र की जाँच आदि से संबंधित है।

इसमें कस्टम अनुमति गार्ड प्राप्त करने के लिए उपयोग की जाने वाली विधि भी शामिल है, जो वास्तव में AuthGuardServiceस्वयं पर निर्भर है

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

यह हमारे राउटिंग मॉड्यूल में अनुमतियों के पैरामीटर के आधार पर कुछ कस्टम गार्ड को पंजीकृत करने के लिए विधि का उपयोग करने की अनुमति देता है:

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

का दिलचस्प हिस्सा forPermissionहै AuthGuardService.guards.push- यह मूल रूप से यह सुनिश्चित करता है कि किसी भी समय forPermissionsएक कस्टम गार्ड वर्ग प्राप्त करने के लिए कहा जाता है यह इस सरणी में भी संग्रहीत करेगा। यह मुख्य वर्ग पर भी स्थिर है:

public static guards = [ ]; 

तब हम सभी गार्ड को पंजीकृत करने के लिए इस सरणी का उपयोग कर सकते हैं - यह ठीक है जब तक हम यह सुनिश्चित कर लेते हैं कि जब तक ऐप मॉड्यूल इन प्रदाताओं को पंजीकृत नहीं करता है, तब तक मार्गों को परिभाषित किया गया था और सभी गार्ड कक्षाएं बनाई गई थीं (उदाहरण के लिए आयात आदेश और इन प्रदाताओं को सूची में यथासंभव कम रखें - एक राउटिंग मॉड्यूल मदद करता है):

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

उम्मीद है की यह मदद करेगा।


1
यह समाधान मुझे एक स्थैतिक त्रुटि देता है: त्रुटि में त्रुटि प्रतीक को सांकेतिक रूप से हल करने में त्रुटि का सामना करना पड़ा।
अरिनिजा

इस समाधान ने मेरे लिए विकास के लिए काम किया, लेकिन जब मैं थ्रो एरर में उत्पादन के लिए एप्लिकेशन का निर्माण करता हूंERROR in Error during template compile of 'RoutingModule' Function calls are not supported in decorators but 'PermGuardService' was called.
kpacn

क्या यह आलसी लोड किए गए मॉड्यूल के साथ काम करता है जिनके पास अपने स्वयं के रूटिंग मॉड्यूल हैं?
क्रश करें

2

@ AluanHaddad का समाधान "कोई प्रदाता नहीं" त्रुटि दे रहा है। यहां इसके लिए एक फिक्स है (यह गंदा लगता है, लेकिन मुझे बेहतर बनाने के लिए कौशल की कमी है)।

वैचारिक रूप से, मैं एक प्रदाता के रूप में, प्रत्येक गतिशील रूप से उत्पन्न वर्ग द्वारा पंजीकृत करता हूं roleGuard

इसलिए जाँच की गई प्रत्येक भूमिका के लिए:

canActivate: [roleGuard('foo')]

आपको होना चाहिए:

providers: [roleGuard('foo')]

हालाँकि, @ AluanHaddad का समाधान जैसा कि प्रत्येक कॉल के लिए नया वर्ग उत्पन्न करेगा roleGuard, भले ही rolesपैरामीटर समान हो। lodash.memoizeइसका उपयोग इस तरह दिखता है:

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

ध्यान दें, भूमिकाओं के प्रत्येक संयोजन ताकि आप एक प्रदाता के रूप में रजिस्टर करने की आवश्यकता, एक नया वर्ग उत्पन्न करता है हर भूमिकाओं का संयोजन। यानी अगर आपके पास है:

canActivate: [roleGuard('foo')]और canActivate: [roleGuard('foo', 'bar')]आपको दोनों को पंजीकृत करना होगा:providers[roleGuard('foo'), roleGuard('foo', 'bar')]

एक बेहतर समाधान प्रदाताओं को अपने आप को वैश्विक प्रदाताओं के संग्रह में अपने आप में पंजीकृत करना होगा roleGuard, लेकिन जैसा कि मैंने कहा, मुझे इसे लागू करने के लिए कौशल की कमी है।


मुझे वास्तव में यह कार्यात्मक दृष्टिकोण पसंद है लेकिन DI (कक्षाएं) के साथ मिक्सिन क्लोजर ओवरहेड जैसा दिखता है।
बिल करें
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.