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

前言

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

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

  • 普通版:将参数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
(dataStringi)

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

总结

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

  • 同一个项目中proxy代理可以设置多个

  • 微信小程序本身没有FormData对象,无法使用 new FormData()

  • 微信小程序中可以手写FormData对象

  • 在js内置对象的原型上增加属性和方法要慎重

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


原文始发于微信公众号(胖蔡话前端):由前端接入第三方实名认证引发的知识探索

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/223143.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!