You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Angular 5应用中启用Google Authentication?实现OTP设置及登录验证

在Angular 5中实现Google身份验证与OTP二次验证功能

我之前在Angular 5项目里落地过一模一样的需求,给你梳理下完整的实现步骤,都是经过实际验证的:

1. 安装必要依赖

Google Authenticator基于TOTP(基于时间的一次性密码)标准,我们需要两个库来处理密钥生成、验证码验证和二维码生成:

npm install speakeasy qrcode --save
# Angular 5用TypeScript,需要安装类型定义
npm install @types/speakeasy @types/qrcode --save-dev

2. 封装OTP核心处理服务

创建一个AuthService来统一处理OTP的密钥生成和验证码验证逻辑,这样可以复用在多个组件中:

import { Injectable } from '@angular/core';
import * as speakeasy from 'speakeasy';
import * as qrcode from 'qrcode';

@Injectable()
export class AuthService {
  // 生成绑定Google Authenticator的密钥和二维码
  generateOTPSecret(userId: string): { secret: string, qrCodeUrl: Promise<string> } {
    const secretData = speakeasy.generateSecret({
      length: 20, // 密钥长度,越长越安全
      name: '你的应用名称', // 显示在Google Authenticator里的应用名
      account: userId // 关联用户ID,方便用户识别
    });

    // 生成二维码的Data URL,用户直接扫描就能绑定
    const qrCodePromise = qrcode.toDataURL(secretData.otpauth_url);

    return {
      secret: secretData.base32, // 要存储到后端的密钥(base32格式)
      qrCodeUrl: qrCodePromise
    };
  }

  // 验证用户输入的OTP验证码是否正确
  verifyOTP(secret: string, token: string): boolean {
    return speakeasy.totp.verify({
      secret: secret,
      encoding: 'base32',
      token: token,
      window: 1 // 允许前后1个时间步长(约30秒)的误差,提升容错性
    });
  }
}

3. 实现OTP设置页面

用户登录后,通过这个页面生成密钥、扫描二维码绑定,并验证验证码后保存密钥到后端:

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../services/auth.service';
import { UserService } from '../services/user.service'; // 假设你有用户数据服务
import { Router } from '@angular/router';

@Component({
  selector: 'app-otp-setup',
  template: `
    <div class="otp-setup-container" *ngIf="qrCodeUrl">
      <h3>绑定Google Authenticator</h3>
      <p>打开Google Authenticator应用,扫描下方二维码</p>
      <img [src]="qrCodeUrl" alt="OTP绑定二维码" class="qr-code">
      <div class="token-input">
        <label>输入6位验证码:</label>
        <input type="text" [(ngModel)]="token" maxlength="6" placeholder="123456">
        <button (click)="verifyAndSaveSecret()" [disabled]="!token">验证并保存</button>
      </div>
    </div>
  `,
  styles: [`
    .qr-code { width: 200px; height: 200px; margin: 20px 0; }
    .token-input { margin-top: 20px; }
    input { padding: 8px; width: 120px; margin: 0 10px; }
  `]
})
export class OtpSetupComponent implements OnInit {
  qrCodeUrl: string;
  secret: string;
  token: string;
  userId: string = '当前登录用户ID'; // 替换成实际的用户ID(从登录状态中获取)

  constructor(
    private authService: AuthService,
    private userService: UserService,
    private router: Router
  ) {}

  ngOnInit() {
    // 生成密钥和二维码
    const otpData = this.authService.generateOTPSecret(this.userId);
    this.secret = otpData.secret;
    otpData.qrCodeUrl.then(url => this.qrCodeUrl = url);
  }

  verifyAndSaveSecret() {
    if (this.authService.verifyOTP(this.secret, this.token)) {
      // 验证通过,把密钥保存到后端用户表中
      this.userService.updateUserOTPSecret(this.userId, this.secret).subscribe(() => {
        alert('OTP设置成功!下次登录需要输入验证码');
        this.router.navigate(['/dashboard']);
      });
    } else {
      alert('验证码不正确,请重试');
    }
  }
}

4. 改造登录流程,加入OTP验证环节

登录时先验证用户名密码,若用户已开启OTP,则跳转到验证码输入界面,否则直接登录成功:

import { Component } from '@angular/core';
import { AuthService } from '../services/auth.service';
import { UserService } from '../services/user.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-login',
  template: `
    <div *ngIf="!showOtpVerify" class="login-form">
      <label>用户名:</label>
      <input type="text" [(ngModel)]="username" placeholder="请输入用户名">
      <label>密码:</label>
      <input type="password" [(ngModel)]="password" placeholder="请输入密码">
      <button (click)="submitLogin()">登录</button>
    </div>

    <div *ngIf="showOtpVerify" class="otp-verify-form">
      <h3>请输入Google Authenticator验证码</h3>
      <input type="text" [(ngModel)]="otpToken" maxlength="6" placeholder="123456">
      <button (click)="verifyOtp()">验证登录</button>
    </div>
  `
})
export class LoginComponent {
  username: string;
  password: string;
  showOtpVerify = false;
  otpToken: string;
  currentUserSecret: string;

  constructor(
    private authService: AuthService,
    private userService: UserService,
    private router: Router
  ) {}

  submitLogin() {
    // 第一步:验证用户名密码
    this.userService.validateCredentials(this.username, this.password).subscribe(user => {
      if (user.otpSecret) {
        // 用户已开启OTP,显示验证码输入界面
        this.showOtpVerify = true;
        this.currentUserSecret = user.otpSecret;
      } else {
        // 未开启OTP,直接登录成功
        this.router.navigate(['/dashboard']);
      }
    }, error => {
      alert('用户名或密码错误');
    });
  }

  verifyOtp() {
    // 第二步:验证OTP验证码
    if (this.authService.verifyOTP(this.currentUserSecret, this.otpToken)) {
      this.router.navigate(['/dashboard']);
    } else {
      alert('验证码不正确,请重试');
    }
  }
}

关键注意事项

  • 密钥存储安全:绝对不能把OTP密钥存在前端本地存储或cookie里,必须存储在后端数据库中,前端仅在验证环节临时获取使用
  • 时间同步:TOTP依赖时间同步,服务器和用户设备的时间差不能超过1分钟,否则会验证失败,建议服务器开启NTP时间同步
  • 备用方案:建议给用户生成几个一次性备用验证码,当用户无法使用Google Authenticator时,可以用备用码登录
  • 版本兼容:Angular 5版本较老,安装依赖时注意版本适配,speakeasy建议选v2.x,qrcode建议选v1.x,避免版本冲突

内容的提问来源于stack exchange,提问作者Hamid Aijaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:54:37