uniapp指纹识别(纯前端实现)

0 阅读8分钟

UniApp 指纹识别登录功能完整实现指南

项目背景

在移动应用开发中,用户登录体验是影响应用留存率的关键因素之一。传统的账号密码登录方式虽然安全,但每次都需要输入繁琐的凭证,用户体验不佳。随着生物识别技术的普及,指纹识别已成为提升登录体验的重要手段。

本文将详细介绍如何在 UniApp 项目中实现完整的指纹识别登录功能,包括设备支持检测、指纹验证、凭证存储等核心功能。

技术栈

  • 前端框架: Vue 3 + TypeScript
  • UI 框架: Wot Design Uni
  • 状态管理: Pinia
  • 开发平台: UniApp
  • 生物识别: UniApp SOTER API
  • 加密算法: SHA-256

核心功能架构

1. 功能模块划分

指纹识别登录功能主要包含以下模块:

  • 指纹认证模块 (fingerprintAuth.ts): 负责设备支持检测、指纹验证、登录逻辑
  • 凭证存储模块 (fingerprint.ts): 负责用户凭证的安全存储和管理
  • 登录页面模块 (Newlogin.vue): 负责登录界面和用户交互

2. 架构设计原则

  • 安全性: 凭证加密存储,密码修改自动清除
  • 用户体验: 智能显示指纹登录入口,错误提示友好
  • 兼容性: 完善的设备支持检测和错误处理
  • 可维护性: 模块化设计,职责清晰

核心实现详解

1. 指纹认证模块

设备支持检测
checkSupport(): Promise<boolean> {
  return new Promise(resolve => {
    uni.checkIsSupportSoterAuthentication({
      success: (res: any) => {
        const supportFinger = res.supportMode.includes('fingerPrint')
        resolve(supportFinger)
      },
      fail: () => {
        resolve(false)
      }
    })
  })
}

实现要点:

  • 使用 uni.checkIsSupportSoterAuthentication 检测设备生物识别能力
  • 检查返回的 supportMode 数组是否包含 'fingerPrint'
  • 失败时返回 false,确保代码健壮性
指纹验证
async authenticate(): Promise<{ success: boolean; error?: string }> {
  return new Promise(resolve => {
    uni.startSoterAuthentication({
      requestAuthModes: ['fingerPrint'],
      challenge: Date.now().toString(),
      authContent: '请验证指纹',
      success: () => {
        resolve({ success: true })
      },
      fail: (err: any) => {
        let errorMsg = '指纹验证失败'
        // 详细的错误码处理
        if (err.errCode === 90001) {
          errorMsg = '设备不支持指纹识别'
        } else if (err.errCode === 90002) {
          errorMsg = '用户未授权指纹识别'
        }
        // ... 更多错误码处理
        resolve({ success: false, error: errorMsg })
      }
    })
  })
}

实现要点:

  • 使用 uni.startSoterAuthentication 启动指纹验证
  • challenge 参数使用时间戳确保唯一性
  • 完善的错误码处理,提供友好的用户提示
  • 错误码范围: 90001-90016,覆盖各种异常情况
指纹登录流程
async loginWithFingerprint(): Promise<{ success: boolean; username?: string; password?: string; error?: string }> {
  const fingerprintStore = useFingerprintStore()
  
  // 1. 检查设备支持
  const isSupported = await this.checkSupport()
  if (!isSupported) {
    return {
      success: false,
      error: '设备不支持指纹识别'
    }
  }

  // 2. 检查是否有存储的凭证
  if (!fingerprintStore.hasCredentials()) {
    return {
      success: false,
      error: '首次登录请使用账号密码登录'
    }
  }

  // 3. 执行指纹验证
  const authResult = await this.authenticate()
  if (!authResult.success) {
    return {
      success: false,
      error: authResult.error || '指纹验证失败'
    }
  }

  // 4. 返回存储的凭证
  const credentials = fingerprintStore.getCredentials()
  return {
    success: true,
    username: credentials.username,
    password: credentials.password
  }
}

实现要点:

  • 三步验证流程:设备支持 → 凭证存在 → 指纹验证
  • 每一步都有明确的错误处理
  • 验证通过后返回存储的加密凭证
密码修改处理
handlePasswordChanged(): void {
  const fingerprintStore = useFingerprintStore()
  fingerprintStore.clearCredentials()
}

handlePasswordError(): { success: boolean; error: string } {
  this.handlePasswordChanged()
  return {
    success: false,
    error: '密码已经修改,请使用账号密码进行登录'
  }
}

实现要点:

  • 密码修改后自动清除存储的凭证
  • 强制用户重新使用账号密码登录
  • 确保安全性,防止使用过期凭证

2. 凭证存储模块

Pinia Store 设计
import { defineStore } from 'pinia'
import { ref } from 'vue'

type FingerprintCredentials = {
  username: string
  password: string
}

const initFingerprintState = {
  username: '',
  password: ''
}

export const useFingerprintStore = defineStore(
  'fingerprint',
  () => {
    const credentials = ref<FingerprintCredentials>({ ...initFingerprintState })

    const setCredentials = (val: FingerprintCredentials): void => {
      credentials.value = { ...credentials.value, ...val }
    }

    const clearCredentials = (): void => {
      credentials.value = { ...initFingerprintState }
    }

    const getCredentials = () => {
      return credentials.value
    }

    const hasCredentials = () => {
      return !!(credentials.value.username && credentials.value.password)
    }

    return {
      credentials,
      setCredentials,
      clearCredentials,
      getCredentials,
      hasCredentials
    }
  },
  {
    persist: true  // 持久化存储
  }
)

实现要点:

  • 使用 Pinia 的 Composition API 风格
  • 开启 persist: true 实现自动持久化
  • 提供完整的 CRUD 操作方法
  • hasCredentials 方法用于判断是否显示指纹登录入口

3. 登录页面集成

指纹登录入口显示
<view
  class="fingerprint-login"
  @click="handleFingerprintLogin"
  v-if="fingerprintStore.hasCredentials() && !showCaptcha"
>
  <wd-text text="使用指纹登录" type="warning" decoration="underline" />
</view>

显示条件:

  • 已存储凭证 (fingerprintStore.hasCredentials())
  • 不需要验证码 (!showCaptcha)
  • 确保安全性和用户体验
指纹登录处理
const handleFingerprintLogin = async () => {
  try {
    loading.value = true

    // 1. 调用指纹登录
    const result = await fingerprintAuth.loginWithFingerprint()

    if (!result.success) {
      toast.error(result.error || '指纹登录失败')
      return
    }

    // 2. 调用登录API
    await loginApi({
      username: result.username,
      password: result.password
    })

    // 3. 更新凭证存储
    fingerprintStore.setCredentials({
      username: result.username,
      password: result.password
    })

    toast.success('登录成功')

    // 4. 跳转处理
    const store = useUserStore()
    const roles = store.userInfo.role || []
    const ADMIN_ROLE_ID = '24'
    const isAdmin = roles.some((role: any) => role.roleId === ADMIN_ROLE_ID)

    setTimeout(() => {
      if (isAdmin) {
        uni.navigateTo({
          url: `/pages/xfzs/transition?examId=${store.userInfo.fdtid}`
        })
      } else {
        resetVersionCheck()
        uni.$emit('loginSuccess')
        const userStore = useUserStore()
        userStore.setEncryptedPassword(result.password)
        uni.switchTab({
          url: '/pages/index/index'
        })
      }
    }, 1500)
  } catch (error: any) {
    console.error('指纹登录失败:', error)

    // 5. 密码错误处理
    if (error.message && error.message.includes('账号不存在')) {
      const errorResult = fingerprintAuth.handlePasswordError()
      messageBox.alert({
        msg: errorResult.error,
        title: '提示'
      })
    } else {
      toast.error(error.message || '指纹登录失败')
    }
  } finally {
    loading.value = false
  }
}

实现要点:

  • 完整的错误处理流程
  • 密码错误时自动清除凭证
  • 登录成功后的角色判断和页面跳转
  • Loading 状态管理
密码登录成功后存储凭证
await loginApi(loginParams)

uni.showToast({ title: '登录成功', icon: 'success' })

// 保存指纹登录凭证
fingerprintStore.setCredentials({
  username: formData.username.trim(),
  password: encryptedPassword
})

实现要点:

  • 密码使用 SHA-256 加密后存储
  • 登录成功后自动保存凭证
  • 下次登录可直接使用指纹

4. 配置文件设置

manifest.json 配置
{
  "app-plus": {
    "modules": {
      "Fingerprint": {}
    }
  }
}

配置要点:

  • app-plus.modules 中添加 Fingerprint 模块
  • 启用指纹识别功能
  • Android 和 iOS 平台都支持

技术亮点

1. 安全性设计

  • 密码加密: 使用 SHA-256 算法加密密码
  • 凭证隔离: 专门的 Pinia Store 管理指纹凭证
  • 自动清除: 密码修改后自动清除存储的凭证
  • 条件显示: 需要验证码时隐藏指纹登录入口

2. 用户体验优化

  • 智能显示: 根据凭证存储状态动态显示指纹登录入口
  • 友好提示: 详细的错误码处理和用户提示
  • 无缝切换: 支持密码登录和指纹登录无缝切换
  • 加载状态: 完整的 loading 状态管理

3. 代码质量

  • 模块化设计: 职责清晰,易于维护
  • 类型安全: 完整的 TypeScript 类型定义
  • 错误处理: 完善的异常处理机制
  • 代码复用: 工具类封装,提高复用性

错误码说明

错误码说明处理建议
90001设备不支持指纹识别提示用户使用密码登录
90002用户未授权指纹识别引导用户在系统设置中授权
90003指纹识别失败提示用户重试或使用密码登录
90004指纹识别次数过多提示用户稍后重试
90005指纹识别已取消用户主动取消,无需特殊处理
90006指纹识别超时提示用户重试
90007指纹识别服务不可用提示用户检查设备设置
90008指纹识别参数错误检查代码实现
90009指纹识别系统错误提示用户联系技术支持
90010指纹识别设备错误提示用户检查设备
90011指纹识别数据错误提示用户重试
90012指纹识别网络错误提示用户检查网络连接
90013指纹识别权限错误检查应用权限配置
90014指纹识别配置错误检查 manifest.json 配置
90015指纹识别版本错误更新应用版本
90016指纹识别未知错误提示用户联系技术支持

最佳实践建议

1. 安全性

  • 始终使用加密算法处理敏感信息
  • 密码修改后立即清除存储的凭证
  • 在需要额外验证(如验证码)时禁用指纹登录
  • 定期审查和更新安全策略

2. 用户体验

  • 提供清晰的错误提示和解决建议
  • 在合适的时机显示指纹登录入口
  • 保持指纹登录和密码登录的一致性
  • 考虑添加面容识别等其他生物识别方式

3. 性能优化

  • 使用防抖处理设备支持检测
  • 合理使用缓存减少重复请求
  • 异步操作避免阻塞主线程
  • 及时清理不再需要的资源

4. 兼容性

  • 充分测试不同设备和系统版本
  • 处理各种边界情况和异常场景
  • 提供降级方案(如密码登录)
  • 关注平台差异和更新

总结

本文详细介绍了在 UniApp 项目中实现指纹识别登录功能的完整方案。通过模块化设计、完善的错误处理和友好的用户体验,我们实现了一个安全、可靠的指纹登录系统。

核心要点:

  • 使用 SOTER API 实现设备检测和指纹验证
  • Pinia Store 实现凭证的安全存储和管理
  • 完善的错误处理和用户提示
  • 智能的显示控制和条件判断

这个实现方案不仅提升了用户体验,还保证了系统的安全性,可以作为类似项目的参考和借鉴。

参考资料