Node Js의 역할 기반 인증

파트 1:역할과 권한이란 무엇입니까?

역할 권한 구현은 모든 소프트웨어의 중요한 부분입니다. 역할은 책임을 지는 위치이며 모든 책임은 그들에게 주어진 권리를 향유합니다. 몇 가지 역할 사이에 일부 공통 권한이 있을 수 있으며 일부 권한은 특정 역할에 엄격하게 속할 수 있습니다.

권한은 역할이 액세스할 수 있는 URL입니다. 따라서 역할에 대한 권한 정보를 저장하는 DB에 모음을 생성해야 합니다. 역할 수집 스키마는 다음과 같습니다.

const mongoose = require('mongoose');  
const Schema = mongoose.Schema;  
const RoleSchema = new Schema({  
roleId:{  
type:String,  
unique:true,  
required:[true,"Role Id required"]  
},  
type:{  
type:String,  
unique:true,  
required:[true,"Role type is required"]  
},  
rights:[{  
name: String,  
path: String,  
url: String  
}]  
});  
module.exports = Role = mongoose.model('role',RoleSchema);


이제 존재해야 하는 모든 역할이 역할 컬렉션에 있고 위의 스키마 유형임을 기억하십시오.

개체의 스키마 권한 배열에서 개체에 키가 있음을 알 수 있습니다.
  • name("set-username"과 같은 URL의 이름)
  • 경로(기본 경로 히트 "/users/"의 경우)
  • url(요청한 URL 또는 전체 경로 "/users/set-username")

  • 따라서 사용자 역할을 가진 사용자가 사용자 이름을 변경할 권한이 있는 경우 URL /users/set-username을 누를 수 있습니다. 그러나 방랑자는 이 URL에 액세스할 수 없습니다. admin 및 superadmin과 같은 상위 역할은 모든 하위 역할 권한(URL)에 논리적으로 액세스할 수 있어야 합니다.

    실제 애플리케이션에서의 역할은 다음과 같습니다.
  • Wanderer (당사 사이트를 막 방문하는 사람. 모든 공개 경로에 액세스할 수 있어야 합니다. 모든 사람이 액세스할 수 있는 단순 URL/공개 URL은 인증된 권한이 아니므로 이에 대한 별도의 역할을 만들 필요가 없습니다.)
  • 게스트(등록했지만 확인하지 않은 사람은 이메일이 확인되지 않았다고 말합니다).
  • 사용자(확인된 이메일이 있는 사람)
  • Admin (SuperAdmin이 확인 후 관리자로 지정. 대부분의 권한을 가짐)
  • Superadmin (응용 프로그램의 마스터입니다. 그는 좀 더 정교한 권리를 즐깁니다. 관리자보다 더 많은 권리를 가집니다)

  • 지금까지 우리는 정확히 무엇이 옳은지 그리고 그것이 역할에 어떻게 매핑되는지 이해했습니다.

    파트 1.5: 등록된 URL/구성 URL

    여기에 다음과 같은 registeredUrls.js이라는 파일이 있습니다.

    module.exports = {  
        // globally accessible 
        simple: {  
            "/":[""],  
            '/users/':["login","register"],  
        },  
        auth:{  
            //admin level enpoint
    
            '/admin/':   ['load-users' , 'set-new-password', 'delete-user'],  
            '/teacher/':  ["add-teacher", "delete-teacher", "edit-teacher"],  
            '/student/':  [
                "add-student", 
                "delete-student", 
                "edit-student", 
                "test-result"
            ],
    
           // common user enpoint
    
            '/test/':  ["view-test", "submit-test"],  
            '/profile/': [
                'change-username', 
                'update-profile-data',  
                'set-new-password', 
                'upload-pic', 
                'update-social-links'
             ],  
            '/teacher/':['load-teacher'],  
            '/student/':['load-student']
    }
    


    마찬가지로 confgUrls.js

    const configUrls= {  
        '/roles/': [
            'get-rights', 
            'create', 
            'update-rights', 
            'load', 
            'delete', 
            'assign'
         ]  
    }  
    module.exports = configUrls;
    


    파트 2:SuperAdmin 만들기

    이것은 응용 프로그램의 가장 중요한 부분입니다. 서버가 처음 시작되거나 다시 시작/재부팅될 때마다 이 단계가 발생합니다. config/init.js에서 다음 절차를 따르십시오.
  • 모든 단순 URL(공용) 및 인증 URL(관리자 및 사용자) 및 최고 관리자 관련 URL을 superAdminRights[]로 로드합니다.
  • 그런 다음 존재하지 않는 경우 역할이 superadmin인 사용자를 생성하는 기능을 실행합니다.
  • 유형의 역할 가져오기:"superadmin"발견된 경우: 해당 권한을 새 권한(superAdminRights)으로 교체합니다.

  • 이 함수 호출이 끝나면 응용 프로그램에 모든 정교한 URL/권한이 초기화된 수퍼 관리자가 있음을 항상 확신합니다.

    파트 3: 최고 관리자 전용 URL

    최고관리자만이 누릴 수 있는 권한으로 등록된 URL 파일과 병행하여 별도의 파일로 유지되어야 합니다. 여기에는 슈퍼 관리자만 사용하는 경로를 매핑하는 URL 권한이 포함됩니다. 여기에는 역할을 생성하고, 역할을 로드하고, roleId에 대한 권한을 얻고, roleId/역할 유형에 대한 업데이트 권한을 부여하고, 사용자에게 역할을 할당하고, 역할을 삭제하는 경로가 있습니다.

    코드의 각 사용자에 대해 게스트에서 사용자로 역할을 변경해야 합니다(예: 이메일 확인 후). 또는 할당-역할 URL을 사용하여 슈퍼 관리자가 게스트/사용자를 관리자로 전환합니다. 그런 다음 경로 업데이트 권한을 사용하여 관리자 권한을 업데이트합니다.

    이 프로세스는 모든 역할에 컬렉션 문서가 있고 거기에 채워진 권한이 있는지 확인합니다.

    파트 4:인증자 미들웨어

    RBACS 논리의 핵심입니다. 여기서는 프로세스를 따르는 미들웨어를 사용합니다.

    // get all the URLs/endpoints in the system
    
    
    const URLS = require("./registeredUrls");
    // special endpoints
    const CONFIG_URLS = require("./configUrls");
    
    // create array of all endpoints and separate them by auth flows
    
    // simple urls doesn't need any auth flow
    const SIMPLE_URLS = [];
    
    // AUTH_URL and SUPER_URL need auth flow
    const AUTH_URLS = [];
    const SUPER_URLS = [];
    
    // the data-structure is { [rootURL]: [...subURLs..] }
    
    // traverse all registered paths of simple URLs
    // and make complete paths i.e `rootURL/subURL`
    for (const rootURL in URLS.simple) {
      const subURLs = URLS.simple[rootURL];
      for (const subURL of subURLs) {
        // register all these in SIMPLE_URLS
          SIMPLE_URLS.push([rootURL, subURL].join("/"));
      }
    }
    
    // same with AUTH...register as AUTH_URLS
    for (const rootURL in URLS.auth) {
      const subURLs = URLS.auth[rootURL];
      for (const subURL of subURLs) {
          AUTH_URLS.push([rootURL, subURL].join("/"));
      }
    }
    
    // same with CONFIG_URLS...register as SUPER_URLS
    for (const rootURL in CONFIG_URLS) {
      const subURLs = CONFIG_URLS[rootURL];
      for (const subURL of subURLs) {
          SUPER_URLS.push([rootURL, subURL].join("/"));
          // push super URLS in AUTH_URLS also as they need auth flow
          AUTH_URLS.push([rootURL, subURL].join("/"));
      }
    }
    
    
    // By now we have an array of URLs
    // 1. Simple URLs don't need authentication flow SIMPLE_URLS
    // 2. Authentication required URLs need auth-token
    // 3. Config URLs are the highest roles URLs typically super admin
    // and have the same flow as Auth URL
    
    // in the node.js middleware callback
    const middleware = (req, res, next) => {
      // URL or endpoint or path requested
      const reqURL = req.url;
    
      // check where URL is
      const isAuthURL = AUTH_URLS.includes(reqURL);
      const isSimpleURL = SIMPLE_URLS.includes(reqURL);
    
      // auth URLs need auth flows
    
      if (isAuthURL) {
    
        // get token from header
        const token = getToken(req);
    
        // validate 
        const isValidJWTToken = validateJWT(token);
        if (!token || !isValidJWTToken) {
          // send failed authentication response
          // !token missing token required login
          // !isValidJWTToken token is invalid or expired
          return;
        }
    
       // token is valid but we have to check if the session exists in DB
       const user_session = getUserSessionData(token);
    
       // If no session in DB means this token may be mischievously generated
        if (!user_session) {
          // the user token might be compromised
          return;
        } 
    
       // now check if user_session.rights [] has requested URL
       // if requested URL is assigned is in array means 
       // it is assigned that right/path 
    
         const hasRightToPath = user_session.rights.includes(reqURL);
        if (!hasRightToPath) {
          // user doesn't have access to this path
          return;
        }
        // user has right to this path/endpoint/URL so let them do the thing
        return next();
      }
    
      if (isSimpleURL) {
        return next(); // simple URL doesn't need any flow simply pass it down
      }
    
      // if it matched none means it isn't a registered path in the system
      return;
    }
    

    좋은 웹페이지 즐겨찾기