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

So wenden Sie TypeScript-Klassen in Vue-Projekten an

Inhaltsverzeichnis 1. Einleitung 2. Verwendung 1....

MySQL-Grundlagen-Tutorial: Detaillierte Erklärung der DML-Anweisungen

Inhaltsverzeichnis DML-Anweisungen 1. Datensätze ...

JS tiefe und flache Kopierdetails

Inhaltsverzeichnis 1. Was bedeutet „Shallow Copy“...

Vollständiger Schrittbericht zur Vue-Kapselung allgemeiner Tabellenkomponenten

Inhaltsverzeichnis Vorwort Warum müssen wir die T...

So setzen Sie das MySQL-Root-Passwort zurück

Inhaltsverzeichnis 1. Ich habe das Root-Passwort ...

Gemeinsame Eigenschaften des Framesets (Unterteilung von Frames und Fenstern)

Ein Frame ist ein Webseitenbildschirm, der in mehr...

Mysql implementiert drei Funktionen zum Feldspleißen

Beim Exportieren von Daten in Operationen ist das...

So verwenden Sie html2canvas, um HTML-Code in Bilder umzuwandeln

Konvertieren Sie Code in ein Bild mit html2canvas...