Commit df42fae3 authored by liyan's avatar liyan

9月分享:由前端接入第三方实名认证引发的知识探索

parent bb8b5d15
---
title: 完美的兼容性好的前端文件下载方式探讨
date: 2022/9/26 09:07:20
tags:
- 前端
- JavaScript
author: 李燕
---
### 前言
最近,项目中需要接入第三方实名认证。
看了第三方提供的开发文档后,得知实名认证分为三种:普通版、签名版、有效期版。
- 普通版:将参数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方式提交请求。
结合项目业务场景及网络请求的安全性,决定采用签名版实名认证。
<!-- more -->
### 签名实现
本以为,这个签名版实名认证的实现难点在于如何得到签名。后来一查,发现很简单,只需安装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) {
    }  
  })
},
```
然后执行代码,结果报错如下:
![1.jpg](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e666dc7694f74511a3781e099cbc694b~tplv-k3u1fbpfcp-watermark.image?)
再一查,才知道,微信本身没有`FormData`对象,无法使用 `new FormData`
无耐用普通对象试了一下,结果第三方接口不接收。
![2.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/934a8f0b43fe489a8f4311db832bfe6d~tplv-k3u1fbpfcp-watermark.image?)
从上面的错误信息们可以看到,大概就是说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文件》](https://blog.csdn.net/qq_45301392/article/details/118961493?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22118961493%22%2C%22source%22%3A%22weixin_53959748%22%7D)
在项目中尝试用了一下:
```
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) {
    }
  })
},
```
然后执行代码,显示第三方实名认证接口接入成功了。
本以为到此可以结束了。然而,但是,又出现了新问题。
### 隐藏的问题
实名认证功能开发完成后,开始了自测,在自测过程中,忽然发现一个页面的数据不能正常渲染了,控制台报错如下:
![3.jpg](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4ae25bdbe8e349b59ab7d850baf088a3~tplv-k3u1fbpfcp-watermark.image?)
本想着这个问题是页面历史遗留问题,遂找写此代码的小伙伴一起看一下。结果,小伙伴查看后告诉我,他那边可以正常显示。
然后,我切换到上一个版本,也能正常显示,而这个版本只有新增实名认证功能代码,那么只能是这些新写的代码影响了。
但是,实名认证和上面报错的页面完全没关系,为什么会影响呢?实在有点匪夷所思。
后来,经过多番排查,终于找到原因了。是被下面一段代码影响了。
```
// 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值的打印:
![4.jpg](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0acf23f4b3c04d0fb68f0d2618188fd4~tplv-k3u1fbpfcp-watermark.image?)
发现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内置对象的原型上增加属性和方法要慎重
嗯,又是收获满满的一天!
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment