個人ブログのToken検証実践(ノードベース)


前言
  • なぜtoken
  • を使うのか
    HTTPは無状態のプロトコルであり,つまりHTTPはクライアントの情報を保存することができず,要求ごとの違いを区別することができない.
    このようなシナリオを想定すると,AとBは同時に個人の文章を修正し,サーバは同時に2つのpostリクエストを受信するが,ブラウザはどのリクエストがAでどのリクエストがBであるかを知らず,一連の情報をマークし,リクエスト時に持参する識別子(token)が必要である.
  • tokenとは何ですか
  • Tokenは、クライアント要求のトークンとしてサーバが生成する一連の文字です.最初にログインすると、サーバはTonken文字列をクライアントに配布します.その後の要求では,クライアントがこのTokenを持参するだけで,サーバはそのユーザのアクセスであることを知ることができる.
    個人的な理解は、サーバによって暗号化された個人情報の列です.例えば、次のようなものです.
    acess_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZWRhMTBjOTI0NThmNDAwMmFjZDEyMTAiLCJuaWNrX25hbWUiOiLlsI_osaoxOTk2IiwiY3JlYXRlZF90aW1lIjoiMjAyMC0wNi0wNVQwOTozMDo0OS42MThaIiwidXBkYXRlZF90aW1lIjoiMjAyMC0wNi0wNVQwOToyOToyMC4wNzlaIiwiaWF0IjoxNTkxMzQ5NDY4LCJleHAiOjE1OTE5NTQyNjh9.GmUJRXHed7M1xJyPaFFgaQKJoS-w8-l3N_PQFPiwwTE

    サーバは、鍵を復号して現在のリクエスト者の情報を取得します.
    テクノロジースタック
  • フロントエンド:vue+ssr
  • バックエンド:egg(nodeフレーム)+ts
  • データベース:redis+mongo
  • 導入:Docker
  • 構築:Jenkins
  • blogはほぼ完成し、ssrでレンダリングされました
    本文は主にtoken検証について述べ,その他はもう述べない.
    token設計の検証
    私のblogはgetタイプの要求に対してtoken検証をしないで、その他資源の要求を修正することができます例えばPOST、PUTはtoken検証をします
    以下の3つのステップに分解可能
  • クライアントユーザはログインし、サービス側はユーザ情報に基づいてtokenを生成し、クライアント永続化ストレージ
  • に格納.
  • クライアント要求token
  • サービス側はtokenを検証し、失敗すると直接エラー状態
  • に戻る.
    1.フロントエンド(vue.js)
    axiosライブラリを使用し、requestブロッキングでtokenをリクエストヘッダヘッダヘッダにプラグし、responseブロッキングでエラーステータスコードを一括してグローバルプロンプトします.
    ログインに成功したらtokenをブラウザキャッシュに書き込み、リクエストごとに
    import Vue from 'vue';
    import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
    
    axios.interceptors.request.use(
      async (config: AxiosRequestConfig) => {
        const acess_token = await Vue.prototype.$getCacheData('acess_token'); #      token
        if (acess_token) {
          config.headers.acess_token = acess_token;
        }
        return config;
      },
      (err: any) => Promise.reject(err),
    );
    
    axios.interceptors.response.use(
      (response: AxiosResponse) => {
        if (response.data.ret === 200) {
          return response;
        } else {
          Vue.prototype.$global_fail(response.data.content);
          return Promise.reject(response);
        }
      },
      (err: any) => {
        console.log(err);
        if (err.code === 'ECONNABORTED' && err.message.indexOf('timeout') !== -1) {
          Vue.prototype.$global_error('    ,      ');
        }
        if (err.response) {
          Vue.prototype.$global_error(decodeURI(err.response.data.msg || err.response.data.message));
        }
        return Promise.reject(err);
      },
    );

    フロントエンドキャッシュについては、ここではlocalForageライブラリをお勧めします.実用的です.
    localForageはJavaScriptライブラリで、文字列だけでなく多くのタイプのデータを格納できます.localForageには優雅な降格ポリシーがあり、ブラウザがIndexedDBまたはWebSQLをサポートしていない場合はlocalStorageを使用します.
    しかし、その操作は非同期で、自分でカプセル化して同期に変更することができます.
    import Vue from 'vue';
    import localForage from 'localforage';
    
    Vue.prototype.$setCacheData = async (key: string, data: any): Promise => await localForage.setItem(key, data);
    Vue.prototype.$getCacheData = async (key: string): Promise => await localForage.getItem(key) || null;
    Vue.prototype.$clearCache = () => localForage.clear();

    2.バックエンド(egg.js)
  • token
  • を生成する
    ユーザーログイン、jsonwebtokenを使用してtokenを生成
    jsonwebtokenの詳細はnode-jsonwebtokenをクリックしてください
    暗号解読の使用も簡単で、secretが鍵であり、tokenの有効期限を設定できるUserServiceメソッドが直接与えられます.
    # /app/service/user.ts
    import * as jwt from 'jsonwebtoken';
    
    export default class UserService extends Service {
    
      private secret = 'Hello__World'; #   
      async createToken(user: User): Promise {
        const payload = {
          _id: user._id,
          nick_name: user.nick_name,
          created_time: user.created_time,
          updated_time: user.updated_time,
        };
        return jwt.sign(payload, this.secret, { expiresIn: '7d' }); #     
      }
    
      checkToken(token: string): User {
        try {
          #       token
          return jwt.verify(token, this.secret);
        } catch (e) {
          throw '   token';
        }
      }
    
    }

    ミドルウェアでtoken検証を行い、失敗したら直接返します.
  • ミドルウェア検証
  • verifyミドルウェアをオンにし、特定のPOST要求のみを検証します.
    オープンミドルウェア
    # /config/config.default.ts
    config.middleware = ['verify'];
    config.verify = {
      enable: true,
      #   POST     
      match(ctx) {
        return ctx.request.method === 'POST';
      },
    };
    

    verifyでcheckTokenメソッドを呼び出してtokenを検証する
    # /app/middleware/verify.ts
    module.exports = () => {
      return async (ctx, next) => {
        if (ctx.path.startsWith('/api/user/login') || ctx.path.startsWith('/api/user/sendCode') || ctx.path.startsWith('/api/user/register')) {
          return await next();
        }
        try {
          const acess_token: string = ctx.request.header.acess_token;
          if (!acess_token) {
            throw '   ';
          } else {
            await ctx.service.user.checkToken(acess_token); #   token
            return await next();
          }
        } catch (e) {
          # token         ,        
          console.log(e);
          ctx.body = {
            ret: 304,
            content: `${e}`,
          };
        }
      };
    };

    これでtoken検証は終わりますが、不足があれば、ご指摘ください.
    END