胖蔡说技术
随便扯扯

由前端接入第三方实名认证引发的知识探索

前言

最近,项目中需要接入第三方实名认证。

看了第三方提供的开发文档后,得知实名认证分为三种:普通版、签名版、有效期版。

  • 普通版:将参数appId、appKey、name、idNum以multipart/form-data方式提交请求。
  • 签名版:将参数appId、name、idNum、sign以multipart/form-data方式提交请求。其中参数sign的值由前三个参数和值拼接并使用hmacSHA1签名后经Base64编码得到。
  • 有效期版:将参数appId、appKey、name、idNum、startTime、endTime以multipart/form-data方式提交请求。

结合项目业务场景及网络请求的安全性,决定采用签名版实名认证。

签名实现

本以为,这个签名版实名认证的实现难点在于如何得到签名。后来一查,发现很简单,只需安装crypto-js插件,然后按如下实现即可。

import CryptoJS from 'crypto-js'
import hmacSha1 from 'crypto-js/hmac-sha1'
import Base64 from 'crypto-js/enc-base64'
​
 const sign = Base64.stringify(hmacSha1('appIdidNumname', appKey))

Vue项目接入

因为当前web端项目采用的是Vue框架,网络请求用的是axios。

所以一开始,用如下方法尝试请求第三方接口。

import axios from 'axios'
​
realNameCertify() {
  const formData = new FormData()
  formData.append('appId', appId)
  formData.append('name', name)
  formData.append('idNum', idNum)
  formData.append('sign', sign)
  const url = 'https://api.253.com/open/idcard/id-card-auth-valid'
  return axios.post(url,{data: formData},{
    headers: {
        'Content-Type': 'multipart/form-data',
     }
  })   
} 

总感觉哪里不对,果然一执行代码,报错跨域了。

vue.config.js文件中已设置了代理公司后端服务的地址,那第三方的地址如何代理呢?

一查,才发现可以设置多个代理。

 proxy: {
   '/api': {     
      target: '', 
      ws: true,
      pathRewrite: {
         '^/api': '',
      },
    },
    '/a': {
       target: 'https://api.253.com/', 
       ws: true,
       pathRewrite: {
          '^/a': '',
     },
    },

然后将网路请求处的代码改造如下:

import axios from 'axios'
import qs from 'qs'
​
const service = axios.create({
  baseURL: '/a',
  timeout: 20000, // request timeout
  paramsSerializer: (params) => qs.stringify(params, { indices: false }),
})
​
realNameCertify() {
  const formData = new FormData()
  formData.append('appId', appId)
  formData.append('name', name)
  formData.append('idNum', idNum)
  formData.append('sign', sign)
  const url = 'https://api.253.com/open/idcard/id-card-auth-valid'
  return service({
    url: 'open/idcard/id-card-auth/vs',
    method: 'POST',
    data: formData,
     headers: {
        'Content-Type': 'multipart/form-data',
     }
  }) 
} 

再一执行代码,第三方实名认证接口终于请求成功了。

微信小程序接入

想象着,小程序的接入应该比Vue项目的接入更容易,因为不需要设置代理,不需要axios,签名同样可以用crypto-js插件得到,所以写下了如下代码:

​
realNameCertify() {
    const that = this
    const formData = new FormData()
    formData.append('appId', appId)
    formData.append('name', name)
    formData.append('idNum', idNum)
    formData.append('sign', sign)
    const headerRequest = {
      'content-type': 'multipart/form-data',
    }
    wx.request({
      url: 'https://api.253.com/open/idcard/id-card-auth/vs',
      data:formData,
      header: headerRequest,
      method: 'POST',
      success: function (res) {
      },
      fail: function (msg) {
      }   
    }) 
  },
​

然后执行代码,结果报错如下:

再一查,才知道,微信本身没有FormData对象,无法使用 new FormData

无耐用普通对象试了一下,结果第三方接口不接收。

从上面的错误信息们可以看到,大概就是说multipart少了对参数boundary的初始化,其实就是boundary这个参数没有给值。遂将Context-type的值调整为multipart/form-data;boundary=ebf9f03029db4c2799ae16b5428b06bd1,其中boundary里的值可以随便改。

再一执行代码,还是报上述同样的错。

看来,第三方实名认证接口只能接收FormData对象数据。

手写FormData对象

那么,只能想办法然手动搞一个FormData对象了。怎么手写一个FormData对象,毫无头绪,只能求助于网络大佬了。果然,网上真有。

// formData.js
​
const mimeMap = require('./mimeMap') 
 
function FormData(){
 //此处省略n行代码
 ...
 ...
 ...
 
String.prototype.utf8CodeAt = function(i) {
  var str = this;
  ...
  ...
  ....
};
 
module.exports =  FormData;
​
const mimeMap = {
  "0.001": "application/x-001",
  "0.323": "text/h323",
  "0.907": "drawing/907",
  ...
  ...
  ...
}
module.exports = mimeMap;
​

详细写法可参考博文《微信小程序怎样创建formdata对象,并通过 wx.request 发送file文件》

在项目中尝试用了一下:

const hmacSha1 = require('../../../../utils/crypto-js/hmac-sha1')
const Base64 = require('../../../../utils/crypto-js/enc-base64')
const FormData =  require('../../../../utils/formData/formData')
​
 realNameCertify() {
    const that = this
    const sign = Base64.stringify(hmacSha1('appIdidNumname', appKey))
    let formData = new FormData();
    formData.append('appId', appId)
    formData.append('name', that.data.formData.nickName)
    formData.append('idNum', that.data.formData.idCard)
    formData.append('sign', sign)
    const data = formData.getData();
    const headerRequest = {
      'content-type': data.contentType,
    }
    wx.request({
      url: 'https://api.253.com/open/idcard/id-card-auth/vs',
      data:data.buffer,
      header: headerRequest,
      method: 'POST',
      success: function (res) {
      },
      fail: function (msg) {
      }
    })
  },

然后执行代码,显示第三方实名认证接口接入成功了。

本以为到此可以结束了。然而,但是,又出现了新问题。

隐藏的问题

实名认证功能开发完成后,开始了自测,在自测过程中,忽然发现一个页面的数据不能正常渲染了,控制台报错如下:

本想着这个问题是页面历史遗留问题,遂找写此代码的小伙伴一起看一下。结果,小伙伴查看后告诉我,他那边可以正常显示。

然后,我切换到上一个版本,也能正常显示,而这个版本只有新增实名认证功能代码,那么只能是这些新写的代码影响了。

但是,实名认证和上面报错的页面完全没关系,为什么会影响呢?实在有点匪夷所思。

后来,经过多番排查,终于找到原因了。是被下面一段代码影响了。

// formData.js
String.prototype.utf8CodeAt = function(i) {
  ...
  ...
};

原来,在formData.js中,在String原型中,增加了一个方法,而报错的页面正好循环了字符串,并调用了String内置的方法。

 for (let i in inputValue) {
     let val = inputValue[i];
     console.log('val--',val)
     let name = arrSearch(val, PinYinObj);
      if (reg.test(val)) {
         pinYinCode += val.toUpperCase(); // 报错行
      } else if (name !== false) {
         pinYinCode += name;
      }
 }

我们来看一下inputValue和val值的打印:

发现val值多了一个方法,而这个方法再调用字符串的toUpperCase()方法时当然会造成报错。

问题找到了,那怎么解决呢?

改造formData.js

既然在String.prototype上增加utf8CodeAt方法会造成问题,那么我们就只能想办法将utf8CodeAt方法从String.prototype上剥离出来,且不能影响功能实现。

所以我们可以这样改写:

// 改写前:
String.prototype.utf8CodeAt = function(i) {
    var str = this
};
dataString.utf8CodeAt(i)
​
​
// 改写后
function utf8CodeAt(str,i)  {
}
utf8CodeAt(dataString,i)

改写后,执行代码,之前报错的页面终于可以正常渲染数据了。

总结

虽然过程比较曲折,但经此一役,咱的知识库又更新了:

  • 同一个项目中proxy代理可以设置多个
  • 微信小程序本身没有FormData对象,无法使用 new FormData()
  • 微信小程序中可以手写FormData对象
  • 在js内置对象的原型上增加属性和方法要慎重

嗯,又是收获满满的一天!

赞(1) 打赏
转载请附上原文出处链接:胖蔡说技术 » 由前端接入第三方实名认证引发的知识探索
分享到: 更多 (0)

请小编喝杯咖啡~

支付宝扫一扫打赏

微信扫一扫打赏