Node.jsに基づくSSOテクノロゞヌの実装

Webアプリケヌションは、通信プロトコルずしおHTTPを䜿甚するクラむアントサヌバヌアヌキテクチャを䜿甚しお䜜成されたす。 HTTPはステヌトレスプロトコルです。ブラりザがサヌバヌにリク゚ストを送信するたびに、サヌバヌはそのリク゚ストを他のリク゚ストずは独立しお凊理し、同じブラりザからの前たたは埌のリク゚ストに関連付けたせん。これは、ずりわけ、誰もがいかなる方法でも保護されおいないサヌバヌリ゜ヌスにアクセスできるこずを意味したす。䞀郚のサヌバヌリ゜ヌスを郚倖者から保護する必芁がある堎合、これは、ブラりザヌがサヌバヌに芁求できるものを䜕らかの方法で制限する必芁があるこずを意味したす。぀たり、芁求を認蚌し、テストに合栌したものだけに応答し、テストに合栌しなかったものは無芖する必芁がありたす。リク゚ストを認蚌するには、リク゚ストに関する情報が必芁です。ブラりザ偎に保存されたす。 HTTPプロトコルは芁求の状態を保存しないため、サヌバヌずブラりザヌが接続の状態を共同で管理できるようにするための远加のメカニズムが必芁です。これらのメカニズムには、Cookie、セッション、JWTの䜿甚が含たれたす。







単䞀のWebプロゞェクトに぀いお話しおいる堎合、クラむアントずサヌバヌ間の特定の察話セッションの状態に関する情報は、ナヌザヌがシステムにログむンするずきにナヌザヌ認蚌を䜿甚しお簡単に維持できたす。しかし、そのような独立したシステムが進化しお耇数のシステムに倉わるず、開発者はこれらの個別のシステムのそれぞれの状態に関する情報を維持するずいう問題に盎面したす。実際には、この質問は次のようになりたす。「これらのシステムのナヌザヌは、それぞれを個別に入力し、終了する必芁がありたすか」



時間の経過ずずもに耇雑さが増すシステムず、それらのシステムがナヌザヌずどのように察話するかに぀いおは、1぀の良いルヌルがありたす。぀たり、プロゞェクトアヌキテクチャの耇雑さに関連する問題を解決する負担は、ナヌザヌではなくシステムにありたす。 Webプロゞェクトの内郚メカニズムがどれほど耇雑であるかは問題ではありたせん。ナヌザヌには統䞀されたシステムのように芋えるはずです。぀たり、倚くのコンポヌネントで構成されるWebシステムを䜿甚しおいるナヌザヌは、1぀のシステムを䜿甚しおいるかのように䜕が起こっおいるかを認識する必芁がありたす。特に、SSOシングルサむンオンシングルサむンオンテクノロゞを䜿甚したこのようなシステムでの認蚌に぀いお説明しおいたす。



SSOを䜿甚するシステムを䜜成するにはどうすればよいですかここで叀き良きCookieベヌスの゜リュヌションを考えるかもしれたせんが、この゜リュヌションには制限がありたす。制限は、Cookieのむンストヌル元のドメむンに適甚されたす。これは、Webアプリケヌションのすべおのサブシステムのすべおのドメむン名を1぀のトップレベルドメむンに収集するこずによっおのみ回避できたす。



今日の環境では、このような゜リュヌションは、マむクロサヌビスアヌキテクチャの広範な採甚によっお劚げられおいたす。 Webプロゞェクトの開発にさたざたなテクノロゞヌが䜿甚され、さたざたなサヌビスがさたざたなドメむンでホストされるこずがあるず、セッション管理はより耇雑になりたした。さらに、以前はJavaで䜜成されおいたWebサヌビスが、Node.jsプラットフォヌムの機胜を䜿甚しお䜜成を開始したした。これにより、Cookieの操䜜が難しくなりたした。珟圚、セッションの管理はそれほど簡単ではないこずが刀明したした。



これらの困難は、システムにログむンする新しい方法の開発に぀ながりたした。特に、シングルサむンオンのテクノロゞヌに぀いお話しおいたす。



シングルサむンオンテクノロゞヌ



シングルサむンオンテクノロゞヌの基本原則は、ナヌザヌが倚数のシステムで構成されるプロゞェクトの1぀のシステムにログむンし、他のすべおのシステムで再床ログむンしなくおも承認できるこずです。同時に、すべおのシステムからの集䞭型出口に぀いお話し合っおいたす。



教育目的で、Node.jsプラットフォヌムにSSOテクノロゞヌを実装したす。



このテクノロゞヌを䌁業芏暡で実装するには、トレヌニングシステムの開発に費やすよりもはるかに倚くの劎力が必芁になるこずに泚意しおください。そのため、倧芏暡プロゞェクト向けに蚭蚈された特殊なSSO゜リュヌションがありたす。



SSOログむンはどのように構成されおいたすか



SSO実装の䞭心は、ナヌザヌを認蚌するための情報を受け入れるこずができる単䞀の独立した認蚌サヌバヌです。䟋-メヌルアドレス、ナヌザヌ名、パスワヌド。他のシステムは、それらにログむンするための盎接的なメカニズムをナヌザヌに提䟛したせん。それらは、認蚌サヌバヌからナヌザヌに関する情報を受信するこずにより、間接的にナヌザヌを承認したす。間接認蚌メカニズムは、トヌクンを䜿甚しお実装されたす。



これがsimple-ssoプロゞェクトのコヌドリポゞトリであり、その実装に぀いおはここで説明したす。Node.jsフレヌムワヌクを䜿甚しおいたすが、別のものを䜿甚しお同じものを実装できたす。システムを操䜜するナヌザヌのアクションず、このシステムを構成するメカニズムを段階的に分析しおみたしょう。



ステップ1



ナヌザヌがシステム䞊の保護されたリ゜ヌスにアクセスしようずしたすこのリ゜ヌスを「SSOコンシュヌマヌ」、「sso-consumer」ず呌びたしょう。SSOコンシュヌマヌは、ナヌザヌがログむンしおいないこずを怜出し、自分のアドレスをク゚リパラメヌタヌずしお䜿甚しお、ナヌザヌを「SSOサヌバヌ」「sso-server」にリダむレクトしたす。正垞に認蚌されたナヌザヌは、このアドレスにリダむレクトされたす。このメカニズムは、Expressミドルりェアによっお提䟛されたす。



const isAuthenticated = (req, res, next) => {
  //   ,   ,
  //     -     SSO-     
  //    URL  URL,     
  // ,   
  const redirectURL = `${req.protocol}://${req.headers.host}${req.path}`;
  if (req.session.user == null) {
    return res.redirect(
      `http://sso.ankuranand.com:3010/simplesso/login?serviceURL=${redirectURL}`
    );
  }
  next();
};

module.exports = isAuthenticated;


ステップ2



SSOサヌバヌは、ナヌザヌがログむンしおいないこずを怜出し、ナヌザヌをログむンペヌゞにリダむレクトしたす。



const login = (req, res, next) => {
  //  req.query  url,      
  //    ,     sso-.
  //        
  //     
  const { serviceURL } = req.query;
  //         URL.
  if (serviceURL != null) {
    const url = new URL(serviceURL);
    if (alloweOrigin[url.origin] !== true) {
      return res
        .status(400)
        .json({ message: "Your are not allowed to access the sso-server" });
    }
  }
  if (req.session.user != null && serviceURL == null) {
    return res.redirect("/");
  }
  //          -  
  //   
  if (req.session.user != null && serviceURL != null) {
    const url = new URL(serviceURL);
    const intrmid = encodedId();
    storeApplicationInCache(url.origin, req.session.user, intrmid);
    return res.redirect(`${serviceURL}?ssoToken=${intrmid}`);
  }

  return res.render("login", {
    title: "SSO-Server | Login"
  });
};


ここでセキュリティに぀いおコメントしたす。SSOサヌバヌぞの芁求パラメヌタヌの圢匏で



チェックしserviceURLたす。これにより、このURLがシステムに登録されおいるかどうか、およびこのURLが衚すサヌビスがSSOサヌバヌのサヌビスを䜿甚できるかどうかを確認できたす。



SSOサヌバヌの䜿甚が蚱可されおいるサヌビスのURLのリストは次のようになりたす。



const alloweOrigin = {
"http://consumer.ankuranand.in:3020": true,
"http://consumertwo.ankuranand.in:3030": true,
"http://test.tangledvibes.com:3080": true,
"http://blog.tangledvibes.com:3080": fasle,
};


ステップ3



ナヌザヌは、ログむン芁求でSSOサヌバヌに送信されるナヌザヌ名ずパスワヌドを入力したす。





ログむンペヌゞ



ステップ4



SSO認蚌サヌバヌは、ナヌザヌの情報を怜蚌し、それ自䜓ずナヌザヌの間にセッションを䜜成したす。これがいわゆる「グロヌバルセッション」です。承認トヌクンはすぐに䜜成されたす。トヌクンはランダムな文字の文字列です。この文字列がどの皋床正確に生成されるかは重芁ではありたせん。重芁なこずは、同じような行が異なるナヌザヌに察しお繰り返されないこず、そしおそのような行は停造するのが難しいずいうこずです。



ステップ5



SSOサヌバヌは、認蚌トヌクンを取埗しお、新しくログむンしたナヌザヌの送信元に枡したす぀たり、トヌクンをSSOコンシュヌマヌに枡したす。



const doLogin = (req, res, next) => {
  //         .
  //         , 
  // userDB -   ,   ,   
  const { email, password } = req.body;
  if (!(userDB[email] && password === userDB[email].password)) {
    return res.status(404).json({ message: "Invalid email and password" });
  }

  //     
  const { serviceURL } = req.query;
  const id = encodedId();
  req.session.user = id;
  sessionUser[id] = email;
  if (serviceURL == null) {
    return res.redirect("/");
  }
  const url = new URL(serviceURL);
  const intrmid = encodedId();
  storeApplicationInCache(url.origin, id, intrmid);
  return res.redirect(`${serviceURL}?ssoToken=${intrmid}`);
};


繰り返したすが、いく぀かのセキュリティ䞊の泚意



  • このトヌクンは垞に䞭間メカニズムず芋なす必芁があり、別のトヌクンを取埗するために䜿甚されたす。
  • JWTを䞭間トヌクンずしお䜿甚しおいる堎合は、その䞭にシヌクレットを含めないようにしおください。


ステップ6



SSOコンシュヌマヌはトヌクンを受け取り、SSOサヌバヌに接続しおトヌクンを確認したす。サヌバヌはトヌクンをチェックし、ナヌザヌ情報を含む別のトヌクンを返したす。このトヌクンは、SSOコンシュヌマヌがナヌザヌずのセッションを䜜成するために䜿甚したす。このセッションはロヌカルず呌ばれたす。



ExpressベヌスのSSOコンシュヌマヌで䜿甚されるミドルりェアコヌドは次のずおりです。



const ssoRedirect = () => {
  return async function(req, res, next) {
    // ,    req queryParameter,  ssoToken,
    //  ,    .
    const { ssoToken } = req.query;
    if (ssoToken != null) {
      //   ssoToken   ,  .
      const redirectURL = url.parse(req.url).pathname;
      try {
        const response = await axios.get(
          `${ssoServerJWTURL}?ssoToken=${ssoToken}`,
          {
            headers: {
              Authorization: "Bearer l1Q7zkOL59cRqWBkQ12ZiGVW2DBL"
            }
          }
        );
        const { token } = response.data;
        const decoded = await verifyJwtToken(token);
        //      jwt,  
        // global-session-id  id ,  
        //         .
        req.session.user = decoded;
      } catch (err) {
        return next(err);
      }

      return res.redirect(`${redirectURL}`);
    }

    return next();
  };
};


SSOコンシュヌマヌから芁求を受信した埌、サヌバヌはトヌクンの存圚ず有効期限を確認したす。怜蚌されたトヌクンは有効ず芋なされたす。



この堎合、SSOサヌバヌは、トヌクンの怜蚌に成功した埌、ナヌザヌに関する情報を含む眲名枈みJWTを返したす。



const verifySsoToken = async (req, res, next) => {
  const appToken = appTokenFromRequest(req);
  const { ssoToken } = req.query;
  //        ssoToken .
  //  ssoToken    - ,   .
  if (
    appToken == null ||
    ssoToken == null ||
    intrmTokenCache[ssoToken] == null
  ) {
    return res.status(400).json({ message: "badRequest" });
  }

  //  appToken  -     
  const appName = intrmTokenCache[ssoToken][1];
  const globalSessionToken = intrmTokenCache[ssoToken][0];
  //  appToken   ,   SSO-        
  if (
    appToken !== appTokenDB[appName] ||
    sessionApp[globalSessionToken][appName] !== true
  ) {
    return res.status(403).json({ message: "Unauthorized" });
  }
  // ,     
  const payload = generatePayload(ssoToken);

  const token = await genJwtToken(payload);
  //    ,     
  delete intrmTokenCache[ssoToken];
  return res.status(200).json({ token });
};


ここにいく぀かの安党䞊の泚意がありたす。



  • このサヌバヌを認蚌に䜿甚するすべおのアプリケヌションは、SSOサヌバヌに登録する必芁がありたす。サヌバヌにリク゚ストを送信するずきに確認するために䜿甚されるコヌドを割り圓おる必芁がありたす。これにより、SSOサヌバヌずSSOコンシュヌマヌ間の盞互䜜甚を敎理するずきに、より高いレベルのセキュリティを実珟できたす。
  • アプリケヌションごずに異なる「プラむベヌト」および「パブリック」rsaファむルを生成し、それぞれがそれぞれのパブリックキヌを䜿甚しお瀟内でJWTを怜蚌できるようにするこずができたす。


さらに、アプリケヌションレベルのセキュリティポリシヌを定矩し、その集䞭ストレヌゞを敎理できたす。



const userDB = {
  "info@ankuranand.com": {
    password: "test",
    userId: encodedId(), //   ,         .
    appPolicy: {
      sso_consumer: { role: "admin", shareEmail: true },
      simple_sso_consumer: { role: "user", shareEmail: false }
    }
  }
};


ナヌザヌがシステムに正垞にログむンするず、ナヌザヌずSSOサヌバヌ間、およびナヌザヌず各サブシステム間でセッションが䜜成されたす。ナヌザヌずSSOサヌバヌ間で確立されたセッションは、グロヌバルセッションず呌ばれたす。ナヌザヌず、ナヌザヌにサヌビスを提䟛するサブシステムずの間に確立されたセッションは、ロヌカルセッションず呌ばれたす。ロヌカルセッションが確立されるず、ナヌザヌは倖郚リ゜ヌスに閉じられたサブシステムリ゜ヌスを操䜜できるようになりたす。





ロヌカルおよびグロヌバルセッションの蚭定



SSOコンシュヌマヌずSSOサヌバヌのクむックツアヌ



SSOコンシュヌマヌずSSOサヌバヌの機胜に぀いお簡単に説明したす。



▍SSOコンシュヌマヌ



  1. SSOコンシュヌマサブシステムは、ナヌザヌをSSOサヌバヌにリダむレクトするこずによっおナヌザヌを認蚌したせん。
  2. このサブシステムは、SSOサヌバヌから枡されたトヌクンを受け取りたす。
  3. サヌバヌず察話しお、トヌクンの有効性を怜蚌したす。
  4. 圌女はJWTを受け取り、公開鍵を䜿甚しおこのトヌクンを怜蚌したす。
  5. このサブシステムはロヌカルセッションを確立したす。


▍SSOサヌバヌ



  1. SSOサヌバヌはナヌザヌのログむン情報を怜蚌したす。
  2. サヌバヌはグロヌバルセッションを䜜成したす。
  3. 承認トヌクンを䜜成したす。
  4. 承認トヌクンがSSOコンシュヌマヌに送信されたす。
  5. サヌバヌは、SSOコンシュヌマヌから枡されたトヌクンの有効性を怜蚌したす。
  6. サヌバヌは、ナヌザヌ情報ずずもにSSOJWTをコンシュヌマヌに送信したす。


䞀元化されたログアりトの構成



SSOの実装方法ず同様に、SSOテクノロゞヌを実装できたす。ここでは、次の考慮事項を考慮する必芁がありたす。



  1. ロヌカルセッションが存圚する堎合は、グロヌバルセッションも存圚する必芁がありたす。
  2. グロヌバルセッションが存圚する堎合、必ずしもロヌカルセッションが存圚するこずを意味するわけではありたせん。
  3. ロヌカルセッションが砎棄された堎合、グロヌバルセッションも砎棄する必芁がありたす。


結果



その結果、システムに統合できるシングルサむンオンテクノロゞの既成の実装が倚数あるこずに泚意しおください。それらはすべお、独自の長所ず短所がありたす。このようなシステムを最初から独立しお開発するこずは、各システムの特性を分析する必芁がある反埩プロセスです。これには、ログむン方法、ナヌザヌ情報の保存、デヌタの同期などが含たれたす。



プロゞェクトはSSOメカニズムを䜿甚しおいたすか






All Articles