SMS-Bestätigungscode-Anmeldefunktion basierend auf Antd Pro (Prozessanalyse)

SMS-Bestätigungscode-Anmeldefunktion basierend auf Antd Pro (Prozessanalyse)

Zusammenfassung

Kürzlich stieß ich bei der Entwicklung eines Projekts mit Antd Pro auf eine neue Anforderung: Die Anmeldung muss über einen SMS-Bestätigungscode auf der Anmeldeoberfläche erfolgen, anstatt die bisherige Anmeldemethode wie Benutzername und Kennwort zu verwenden.

Obwohl bei dieser Methode zusätzliche SMS-Gebühren anfallen, verbessert sie die Sicherheit erheblich. Antd verfügt nicht über eine integrierte Countdown-Schaltfläche.
Allerdings stellen die ProForm-Komponenten von antd pro Komponenten bereit, die sich auf SMS-Bestätigungscodes beziehen.
Die Komponentenbeschreibung finden Sie unter: https://procomponents.ant.design/components/form

Gesamtprozess

Der Anmeldevorgang per SMS-Bestätigungscode ist ganz einfach:

  1. SMS-Bestätigungscode anfordern (Client)
  2. SMS-Bestätigungscode generieren und Ablaufzeit des Bestätigungscodes festlegen (serverseitig)
  3. SMS-Schnittstelle aufrufen um Bestätigungscode zu senden (serverseitig)
  4. Melden Sie sich mit dem per SMS erhaltenen Bestätigungscode an (Client)
  5. Überprüfen Sie die Mobiltelefonnummer und den SMS-Bestätigungscode und stellen Sie nach der Überprüfung ein JWT-Token aus (serverseitig).

Frontend

Seitencode

importiere React, { useState } von 'react';
  importiere { connect } von „umi“;
   importiere {Nachricht} von „antd“;
  importiere ProForm, { ProFormText, ProFormCaptcha } aus '@ant-design/pro-form';
 importiere { MobileTwoTone, MailTwoTone } aus '@ant-design/icons';
  importiere { sendSmsCode } von '@/services/login';
 
 const Login = (Eigenschaften) => {
    const [countDown, handleCountDown] = useState(5);
    const { dispatch } = Requisiten;
    const [form] = ProForm.useForm();
    zurückkehren (
      <div
        Stil={{
          Breite: 330,
          Rand: "auto",
        }}
      >
        <ProForm
          form={form}
          Einsender={{
            Suchkonfiguration: {
              submitText: 'Anmelden',
            },
            rendern: (_, dom) => dom.pop(),
            SubmitButtonProps: {
              Größe: 'groß',
              Stil: {
                Breite: '100%',
              },
            },
            beim Senden: async () => {
              const fieldsValue = warte auf form.validateFields();
              console.log(FelderWert);
              warte auf Versand({
                Typ: 'login/login',
                Nutzlast: { Benutzername: FelderWert.mobile, SMS-Code: FelderWert.code },
              });
            },
          }}
        >
          <ProFormText
            FeldProps={{
              Größe: 'groß',
              Präfix: <MobileTwoTone />,
            }}
            Name = "Mobiltelefon"
            Platzhalter="Bitte geben Sie Ihre Telefonnummer ein"
            Regeln={[
              {
                erforderlich: wahr,
                Nachricht: 'Bitte geben Sie Ihre Telefonnummer ein',
              },
              {
                Muster: neuer RegExp(/^1[3-9]\d{9}$/, 'g'),
                Meldung: „Das Telefonnummernformat ist falsch“,
              },
            ]}
          />
          <ProFormCaptcha
            FeldProps={{
              Größe: 'groß',
              Präfix: <MailTwoTone />,
            }}
            countDown={countDown}
            captchaProps={{
              Größe: 'groß',
            }}
            Name = "Code"
            Regeln={[
              {
                erforderlich: wahr,
                Meldung: ,,Bitte geben Sie den Bestätigungscode ein! ',
              },
            ]}
            Platzhalter="Bitte geben Sie den Bestätigungscode ein"
            onGetCaptcha={async (mobile) => {
              wenn (!form.getFieldValue('mobile')) {
                message.error('Bitte geben Sie zuerst Ihre Telefonnummer ein');
                zurückkehren;
              }
              let m = form.getFieldsError(['mobile']);
              wenn (m[0].errors.length > 0) {
                Nachricht.Fehler(m[0].Fehler[0]);
                zurückkehren;
              }
              let response = warte auf sendSmsCode(mobile);
              if (response.code === 10000) message.success('Bestätigungscode erfolgreich gesendet!');
              sonst Nachricht.Fehler(Antwort.Nachricht);
            }}
          />
        </ProForm>
      </div>
    );
  };
  
  exportiere Standard connect()(Anmelden);

Bestätigungscode und Login-Dienst anfordern (src/services/login.js)

Importiere die Anfrage von „@/utils/request“;

  exportiere asynchrone Funktion login(params) {
  Rückgabeanforderung('/api/v1/login', {
     Methode: 'POST',
     Daten: Parameter,
   });
 }
 
  exportiere asynchrone Funktion sendSmsCode(mobile) {
    Anfrage zurückgeben(`/api/v1/send/smscode/${mobile}`, {
      Methode: 'GET',
    });
  }

Modell, das die Anmeldung handhabt (src/models/login.js)

importiere { stringify } aus 'Abfragezeichenfolge';
 importiere { Verlauf } von „umi“;
  importiere { login } von '@/services/login';
 importiere { getPageQuery } aus '@/utils/utils';
 importiere {Nachricht} von „antd“;
  importiere md5 von „md5“;
 
  const Modell = {
   Namespace: "Anmelden",
    Status: '',
    Anmeldetyp: '',
    Zustand: {
      Token: '',
    },
    Effekte:
      *login({ Nutzlast }, { aufrufen, setzen }) {
        Nutzlast.Client = "Administrator";
        // Nutzlast.Passwort = md5(Nutzlast.Passwort);
        const Antwort = Ertragsanruf (Anmeldung, Nutzlast);
        wenn (Antwortcode !== 10000) {
          Nachricht.Fehler(Antwort.Nachricht);
          zurückkehren;
        }
  
        // Token im lokalen Speicher festlegen
        wenn (window.localStorage) {
          window.localStorage.setItem('jwt-token', response.data.token);
        }
  
        Rendite setzen({
          Typ: 'changeLoginStatus',
          Nutzlast: {Daten: Antwort.Daten, Status: Antwort.Status, Anmeldetyp: Antwort.Anmeldetyp},
        }); // Anmeldung erfolgreich
  
        const urlParams = neue URL(window.location.href);
        const params = getPageQuery();
        let { umleiten } = Parameter;
  
        console.log(Umleitung);
        wenn (Umleitung) {
          const redirectUrlParams = neue URL(Umleitung);
  
          wenn (redirectUrlParams.origin === urlParams.origin) {
            Umleitung = Umleitung.substr(urlParams.origin.length);
  
            wenn (redirect.match(/^\/.*#/)) {
              Umleitung = Umleitung.substr(Umleitung.indexOf('#') + 1);
            }
          } anders {
            Fenster.Standort.href = "/home";
          }
        }
        Verlauf.Ersetzen(Umleitung || '/home');
      },
  
      abmelden() {
        const { redirect } = getPageQuery(); // Hinweis: Es können Sicherheitsprobleme auftreten. Bitte beachten Sie
  
        window.localStorage.removeItem('jwt-token');
        wenn (Fenster.Standort.Pfadname !== '/Benutzer/Anmeldung' && !Umleitung) {
          Verlauf.ersetzen({
            Pfadname: '/user/login',
            Suche: stringify({
              Umleitung: window.location.href,
            }),
          });
        }
      },
    },
    Reduzierstücke: {
      changeLoginStatus(status, { nutzlast }) {
        zurückkehren {
          ...Zustand,
          Token: Nutzlast.Daten.Token,
          Status: Nutzlast.Status,
          Anmeldetyp: payload.Anmeldetyp,
        };
      },
    },
  };
  Standardmodell exportieren;

hinteres Ende

Das Backend verfügt im Wesentlichen über zwei Schnittstellen, eine zum Versenden von SMS-Bestätigungscodes und eine zur Login-Bestätigung.

Routing-Code-Ausschnitt:

apiV1.POST("/login", authMiddleware.LoginHandler)
 apiV1.GET("/send/smscode/:mobile", controller.SendSmsCode)

Verarbeitung des SMS-Bestätigungscodes

  1. Bei der Verarbeitung von SMS-Verifizierungscodes sind einige Punkte zu beachten:
  2. Generieren Sie eine zufällige Nummer mit fester Länge und rufen Sie die SMS-Schnittstelle auf, um den Bestätigungscode zu senden. Speichern Sie den Bestätigungscode für zukünftige Überprüfungen.
  3. Generieren von Zahlen mit fester Länge

Der folgende Code generiert eine 6-stellige Zahl. Wenn die Zufallszahl weniger als 6 Ziffern hat, fügen Sie davor eine 0 hinzu.

r := rand.Neu(rand.NeueQuelle(Zeit.Jetzt().UnixNano()))
 Code := fmt.Sprintf("%06v", r.Int31n(1000000))

SMS-API anrufen

Dies ist ganz einfach. Rufen Sie es einfach gemäß der Anleitung der erworbenen SMS-Schnittstelle auf.

Prüfcode zur Verifizierung speichern

Hierbei ist zu beachten, dass der Verifizierungscode ein Ablaufdatum haben muss und ein Verifizierungscode nicht immer wieder verwendet werden kann.
Der Bestätigungscode für die temporäre Speicherung kann in der Datenbank oder in einem KV-Speicher wie Redis abgelegt werden. Der Einfachheit halber wird der Bestätigungscode mithilfe einer Kartenstruktur direkt im Speicher gespeichert.

Paket-Dienstprogramm

 importieren (
    "fmt"
   "Mathematik/Rand"
   "Synchronisieren"
  "Zeit"
  )

  Typ loginItem-Struktur {
    smsCode-Zeichenfolge
    smsCodeExpire int64
  }
  
  Typ LoginMap-Struktur {
    m Karte [Zeichenfolge] * LoginItem
    synchronisieren.Mutex
  }
  
  var lm *LoginMap
  
  Funktion InitLoginMap(resetTime int64, loginTryMax int) {
    lm = &LoginMap{
      m: make(map[string]*loginItem),
    }
  }
  
  func GenSmsCode(Schlüsselzeichenfolge) Zeichenfolge {
    r := rand.Neu(rand.NeueQuelle(Zeit.Jetzt().UnixNano()))
    Code := fmt.Sprintf("%06v", r.Int31n(1000000))
  
    wenn _, ok := lm.m[Schlüssel]; !ok {
      lm.m[Schlüssel] = &loginItem{}
    }
  
    v := lm.m[Schlüssel]
    v.smsCode = Code
    v.smsCodeExpire = time.Now().Unix() + 600 // Der Bestätigungscode läuft in 10 Minuten ab Rückgabecode
  }
  
  func CheckSmsCode(Schlüssel, Code-String) Fehler {
    wenn _, ok := lm.m[Schlüssel]; !ok {
      return fmt.Errorf("Bestätigungscode nicht gesendet")
    }
  
    v := lm.m[Schlüssel]
  
    // Ist der Bestätigungscode abgelaufen? if time.Now().Unix() > v.smsCodeExpire {
      return fmt.Errorf("Verifizierungscode (%s) ist abgelaufen", Code)
    }
  
    // Ist der Bestätigungscode korrekt, wenn Code != v.smsCode {
      return fmt.Errorf("Verifizierungscode (%s) falsch", Code)
    }
  
    Rückgabe Null
  }

Anmeldeüberprüfung

Der Anmeldebestätigungscode ist relativ einfach: Rufen Sie zuerst die oben genannte Methode CheckSmsCode auf, um zu überprüfen, ob er zulässig ist.
Nach der Überprüfung erhalten Sie Benutzerinformationen basierend auf der Mobiltelefonnummer, generieren ein JWT-Token und geben es an den Client zurück.

Häufig gestellte Fragen

Antd-Versionsproblem

Um ProForm von Antd Pro zu verwenden, müssen Sie die neueste Version von Antd verwenden, vorzugsweise >= v4.8, da sonst Inkompatibilitätsfehler in den Front-End-Komponenten auftreten.

Optimierbare Punkte

Die obige Implementierung ist relativ grob und die folgenden Aspekte können weiter optimiert werden:

Der Bestätigungscode muss seltener gesendet werden. Schließlich kostet das Senden von SMS-Nachrichten Geld. Der Bestätigungscode befindet sich direkt im Speicher und geht nach dem Neustart des Systems verloren. Sie können erwägen, ihn in einem Speicher wie Redis abzulegen.

Dies ist das Ende dieses Artikels über die SMS-Bestätigungscode-Anmeldefunktion (Prozessanalyse) basierend auf Antd Pro. Weitere relevante Antd Pro-Bestätigungscode-Anmeldeinhalte finden Sie in den vorherigen Artikeln von 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • AntDesign Pro + .NET Core implementiert eine JWT-basierte Login-Authentifizierungsfunktion
  • Detaillierte Erklärung zur Implementierung der Anmeldefunktion durch Kombination von React mit der Formularkomponente von Antd

<<:  Detaillierte Erläuterung der Linux-Dateiberechtigungen und Befehle zur Gruppenänderung

>>:  So verbinden Sie JDBC mit MySQL 5.7

Artikel empfehlen

JavaScript generiert dynamisch eine Tabelle mit Zeilenlöschfunktion

In diesem Artikelbeispiel wird der spezifische Co...

vue-pdf realisiert Online-Dateivorschau

In diesem Artikelbeispiel wird der spezifische Co...

Zusammenfassung der relevanten Wissenspunkte zu Ajax in jQuery

Vorwort Studenten, die JavaScript lernen, wissen,...

So stellen Sie Solidity-Smart-Contracts mit ethers.js bereit

Wenn Sie DApps auf Ethereum entwickelt haben, hab...

Eine kurze Diskussion über die Leistungsprobleme des MySQL-Paging-Limits

MySQL-Paging-Abfragen werden normalerweise über L...

MySQL-Datenbank löscht doppelte Daten und behält nur eine Methodeninstanz bei

1. Problemeinführung Nehmen Sie ein Szenario an, ...

Designtheorie: Menschenorientiertes Designkonzept

<br />Als sich das Denken in Ost und West sp...

So entwickeln Sie Uniapp mit vscode

Da ich immer vscode zur Entwicklung von Front-End...

Erfahren Sie mehr über die am häufigsten verwendeten JavaScript-Ereignisse

Inhaltsverzeichnis JavaScript-Ereignisse: Häufig ...

Detaillierte Schritte zur Installation von MySQL auf CentOS 7

Wenn wir in CentOS7 MySQL installieren, wird Mari...

Ein Artikel zum Verständnis von Operatoren in ECMAScript

Inhaltsverzeichnis Unäre Operatoren Boolesche Ope...