前言
最近,项目中需要接入第三方实名认证。
看了第三方提供的开发文档后,得知实名认证分为三种:普通版、签名版、有效期版。
-
普通版:将参数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;
在项目中尝试用了一下:
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内置对象的原型上增加属性和方法要慎重
嗯,又是收获满满的一天!
原文始发于微信公众号(胖蔡话前端):由前端接入第三方实名认证引发的知识探索
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/223143.html