antd pro に基づく SMS 認証コード ログイン機能 (プロセス分析)

antd pro に基づく SMS 認証コード ログイン機能 (プロセス分析)

まとめ

最近、antd pro を使用してプロジェクトを開発しているときに、ユーザー名とパスワードなどの以前のログイン方法を使用する代わりに、ログイン インターフェイスで SMS 検証コードを使用してログインするという新しい要件に遭遇しました。

この方法では SMS 料金が追加されますが、セキュリティが大幅に向上します。Antd にはカウントダウン ボタンが組み込まれていません。
ただし、antd pro の ProForm コンポーネントは、SMS 検証コードに関連するコンポーネントを提供します。
コンポーネントの説明は、https://procomponents.ant.design/components/form で確認できます。

全体的なプロセス

SMS 認証コードによるログインのプロセスは非常に簡単です。

  1. SMS認証コードのリクエスト(クライアント)
  2. SMS認証コードを生成し、認証コードの有効期限を設定する(サーバー側)
  3. 確認コードを送信するにはSMSインターフェースを呼び出します(サーバー側)
  4. SMSで受信した認証コードを使用してログインします(クライアント)
  5. 携帯電話番号とSMS認証コードを検証し、検証後にjwtトークンを発行する(サーバー側)

フロントエンド

ページコード

React をインポートし、{useState} を 'react' から取得します。
  'umi' から connect をインポートします。
   'antd' から { message } をインポートします。
  '@ant-design/pro-form' から ProForm、{ ProFormText、ProFormCaptcha } をインポートします。
 '@ant-design/icons' から { MobileTwoTone、MailTwoTone } をインポートします。
  '@/services/login' から { sendSmsCode } をインポートします。
 
 const ログイン = (props) => {
    const [countDown, handleCountDown] = useState(5);
    const { ディスパッチ } = props;
    const [フォーム] = ProForm.useForm();
    戻る (
      <div
        スタイル={{
          幅: 330,
          マージン: 'auto'、
        }}
      >
        <プロフォーム
          フォーム={フォーム}
          提出者={{
            検索設定: {
              送信テキスト: 'ログイン',
            },
            レンダリング: (_, dom) => dom.pop(),
            送信ボタンプロパティ: {
              サイズ: 「大」、
              スタイル: {
                幅: '100%'、
              },
            },
            onSubmit: 非同期() => {
              const fieldsValue = form.validateFields() を待機します。
              console.log(フィールド値);
              ディスパッチを待つ({
                タイプ: 'ログイン/ログイン'、
                ペイロード: { ユーザー名: fieldsValue.mobile、 SMS コード: fieldsValue.code },
              });
            },
          }}
        >
          <ProFormText
            フィールドプロパティ={{
              サイズ: 「大」、
              プレフィックス: <MobileTwoTone />,
            }}
            名前="モバイル"
            placeholder="電話番号を入力してください"
            ルール={[
              {
                必須: true、
                メッセージ: 「電話番号を入力してください」
              },
              {
                パターン: 新しい正規表現(/^1[3-9]\d{9}$/, 'g'),
                メッセージ: 「電話番号の形式が正しくありません」
              },
            ]}
          />
          <ProFormCaptcha
            フィールドプロパティ={{
              サイズ: 「大」、
              プレフィックス: <MailTwoTone />,
            }}
            countDown={countDown}
            キャプチャプロパティ={{
              サイズ: 「大」、
            }}
            名前="コード"
            ルール={[
              {
                必須: true、
                メッセージ: '確認コードを入力してください! '、
              },
            ]}
            placeholder="確認コードを入力してください"
            onGetCaptcha={非同期(モバイル) => {
              (!form.getFieldValue('モバイル')){
                message.error('まず電話番号を入力してください');
                戻る;
              }
              m = form.getFieldsError(['mobile']); とします。
              (m[0].errors.length > 0)の場合{
                メッセージ.エラー(m[0].エラー[0]);
                戻る;
              }
              応答を待機します。sendSmsCode(mobile);
              if (response.code === 10000) message.success('確認コードが正常に送信されました!');
              それ以外の場合は、message.error(response.message);
            }}
          />
        </プロフォーム>
      </div>
    );
  };
  
  デフォルトの connect()(Login) をエクスポートします。

検証コードとログイン サービスをリクエストする (src/services/login.js)

'@/utils/request' からリクエストをインポートします。

  非同期関数login(params)をエクスポートします。
  リクエストを返します('/api/v1/login', {
     メソッド: 'POST'、
     データ: パラメータ、
   });
 }
 
  非同期関数 sendSmsCode(mobile) をエクスポートします。
    リクエストを返します(`/api/v1/send/smscode/${mobile}`, {
      メソッド: 'GET'、
    });
  }

ログインを処理するモデル (src/models/login.js)

'querystring' から { stringify } をインポートします。
 'umi' から { history } をインポートします。
  '@/services/login' から { login } をインポートします。
 '@/utils/utils' から getPageQuery をインポートします。
 'antd' から { message } をインポートします。
  'md5' から md5 をインポートします。
 
  定数モデル = {
   名前空間: 'ログイン',
    状態: ''、
    ログインタイプ: ''、
    州: {
      トークン: ''、
    },
    効果:
      *ログイン({ ペイロード }, { 呼び出し、配置 }) {
        ペイロード.client = 'admin';
        // ペイロード.パスワード = md5(ペイロード.パスワード);
        const response = yield call(ログイン、ペイロード);
        (レスポンスコード!== 10000)の場合{
          メッセージ.エラー(応答.メッセージ);
          戻る;
        }
  
        // トークンをローカルストレージに設定する
        (window.localStorage)の場合{
          window.localStorage.setItem('jwt-token', response.data.token);
        }
  
        イールドプット({
          タイプ: 'changeLoginStatus'、
          ペイロード: { データ: response.data、ステータス: response.status、ログインタイプ: response.loginType },
        }); // ログインに成功しました
  
        const urlParams = 新しい URL(window.location.href);
        定数パラメータ = getPageQuery();
        { リダイレクト } = パラメータ;
  
        console.log(リダイレクト);
        if (リダイレクト) {
          const redirectUrlParams = 新しい URL(リダイレクト);
  
          (redirectUrlParams.origin === urlParams.origin)の場合{
            リダイレクト = redirect.substr(urlParams.origin.length);
  
            リダイレクトマッチ(/^\/.*#/)の場合
              リダイレクト = redirect.substr(redirect.indexOf('#') + 1);
            }
          } それ以外 {
            window.location.href = '/home';
          }
        }
        history.replace(リダイレクト || '/home');
      },
  
      ログアウト() {
        const { redirect } = getPageQuery(); // 注意: セキュリティ上の問題がある可能性がありますので、ご注意ください
  
        window.localStorage.removeItem('jwt-token');
        if (window.location.pathname !== '/user/login' && !redirect) {
          履歴を置き換える({
            パス名: '/user/login',
            検索: 文字列化({
              リダイレクト: window.location.href、
            })、
          });
        }
      },
    },
    リデューサー: {
      changeLoginStatus(状態、{ペイロード}) {
        戻る {
          ...州、
          トークン: payload.data.token、
          ステータス: ペイロードステータス、
          ログインタイプ: ペイロード.ログインタイプ、
        };
      },
    },
  };
  デフォルトモデルをエクスポートします。

後部

バックエンドには主に 2 つのインターフェースがあり、1 つは SMS 検証コードを送信するためのもので、もう 1 つはログイン検証のためのものです。

ルーティング コード スニペット:

apiV1.POST("/ログイン", authMiddleware.LoginHandler)
 apiV1.GET("/send/smscode/:mobile", コントローラー.SendSmsCode)

SMS認証コードの処理

  1. SMS 認証コードを処理する際には、いくつか注意すべき点があります。
  2. ランダムな固定長の番号を生成し、SMS インターフェイスを呼び出して確認コードを送信します。確認コードは将来の確認のために保存します。
  3. 固定長の数値を生成する

次のコードは 6 桁の数字を生成します。乱数が 6 桁未満の場合は、先頭に 0 を追加します。

r := rand.New(rand.NewSource(time.Now().UnixNano()))
 コード:= fmt.Sprintf("%06v", r.Int31n(1000000))

SMS APIを呼び出す

簡単です。購入したSMSインターフェースの指示に従って呼び出すだけです。

確認のために確認コードを保存してください

ここで注意すべき点は、認証コードには有効期限があり、1 つの認証コードを常に使用できるわけではないということです。
一時保存用の検証コードは、データベース、または Redis などの KV ストレージに配置できます。簡単にするために、検証コードはマップ構造を使用してメモリに直接保存されます。

パッケージ ユーティリティ

 輸入 (
    「fmt」
   「数学/ランド」
   「同期」
  "時間"
  )

  loginItem構造体型{
    SMSコード文字列
    smsコード有効期限 int64
  }
  
  LoginMap構造体型{
    m map[文字列]*ログイン項目
    同期ミューテックス
  }
  
  var lm *ログインマップ
  
  InitLoginMap関数(resetTime int64、loginTryMax int) {
    lm = &ログインマップ{
      m: make(map[文字列]*ログイン項目)、
    }
  }
  
  func GenSmsCode(キー文字列) 文字列 {
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    コード:= fmt.Sprintf("%06v", r.Int31n(1000000))
  
    _の場合、ok := lm.m[key]; !ok {
      lm.m[キー] = &ログインアイテム{}
    }
  
    v := lm.m[キー]
    v.smsCode = コード
    v.smsCodeExpire = time.Now().Unix() + 600 // 認証コードは10分で期限切れになります return code
  }
  
  func CheckSmsCode(キー、コード文字列) エラー {
    _の場合、ok := lm.m[key]; !ok {
      return fmt.Errorf("検証コードは送信されませんでした")
    }
  
    v := lm.m[キー]
  
    // 認証コードは期限切れですか? if time.Now().Unix() > v.smsCodeExpire {
      return fmt.Errorf("検証コード (%s) の有効期限が切れています", code)
    }
  
    // 検証コードは正しいか if code != v.smsCode {
      return fmt.Errorf("検証コード (%s) が正しくありません", code)
    }
  
    nilを返す
  }

ログイン認証

ログイン検証コードは比較的単純で、まず上記の CheckSmsCode メソッドを呼び出して、それが正当かどうかを確認します。
検証後、携帯電話番号に基づいてユーザー情報を取得し、jwt トークンを生成してクライアントに返します。

よくある質問

Antd バージョンの問題

antd pro の ProForm を使用するには、最新バージョンの antd (できれば v4.8 以上) を使用する必要があります。そうでない場合、フロントエンド コンポーネントに互換性のないエラーが発生します。

最適化できるポイント

上記の実装は比較的大まかであり、次の側面をさらに最適化できます。

確認コードは、あまり頻繁に送信する必要はありません。結局のところ、SMS メッセージを送信するにはコストがかかります。確認コードはメモリ内に直接保存されるため、システムを再起動すると失われます。redis などのストレージに保存することを検討できます。

antd pro に基づく SMS 認証コード ログイン機能 (プロセス分析) に関するこの記事はこれで終わりです。 antd pro 認証コード ログインに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。 今後とも 123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • AntDesign Pro + .NET CoreはJWTベースのログイン認証機能を実装します
  • ReactとAntdのFormコンポーネントを組み合わせてログイン機能を実装する方法を詳しく説明します

<<:  Linux のファイル権限とグループ変更コマンドの詳細な説明

>>:  JDBC を MySQL 5.7 に接続する方法

推薦する

さまざまな解像度やブラウザでウェブページを適切に表示する方法

キーコードは次のとおりです。コードをコピーコードは次のとおりです。 html{高さ:100%; }コ...

Python 仮想環境のインストールとアンインストールの方法と発生する問題

Ubuntu16.04 のインストールとアンインストール pip実験環境Ubuntu 16.04; ...

JavaScript の新しい要素トラバーサルプロパティを使用して子要素をトラバースする方法を学びます

目次1. ChildNodes属性のトラバーサル2. 要素シリーズ属性のトラバーサル以前は、chil...

すべてのブラウザとの完全な互換性を実現するために最適なプリセットを選択してください

各ブラウザの select タグのプロパティと各ブラウザのサポートが多少異なるため、各ブラウザでの選...

PrometheusはGrafanaディスプレイを使用してMySQLを監視します

目次Prometheusはエクスポーターを介してMySQLを監視し、Grafanaチャートで表示しま...

process.env.NODE_ENV 本番環境モードを設定する方法

始める前に、process.env.NODE_ENV にはデフォルトで開発と本番の 2 つの状態しか...

Vue のスロットスコープの詳細な理解(初心者向け)

Baidu には slot-scope に関する記事が既にたくさんありますが、以前よく学習しておら...

内部 IP アクセスのみを許可する Nginx プロキシ設定を追加する方法

位置 / { インデックス index.jsp; proxy_next_upstream http...

MySQL での重複キー更新時の replace into と insert into の使用法と相違点の分析

この記事では、MySQL での重複キー更新時の replace into と insert into...

HTML における rel="nofollow" の役割と rel 属性の使用を分析する

リンクに rel="nofollow" 属性を追加すると、検索エンジンにこの接続...

MySQL 8.0はJSONを扱えるようになりました

目次1. 概要2. JSON基本ツール3. JSONパス式4. JSONを検索して変更する序文:長い...

Linuxにおけるselinuxの基本設定チュートリアルの詳細な説明

selinux ( Security-Enhanced Linux)は、Linux カーネル モジュ...

Linux に nginx をインストールする方法

Nginx は C 言語で開発されており、Linux で実行することをお勧めします。もちろん、Win...

HTML テーブルタグチュートリアル (21): 行の境界線の色属性 BORDERCOLOR

テーブルを美しくするために、行ごとに異なる境界線の色を設定できます。基本的な構文<TR 境界線...

Windows7 での Mysql5.7 my.ini ファイルの読み込みパスとデータの場所の変更方法

更新: MySQL の公式 Web サイトにアクセスして MySQL インストーラーをインストールし...