Skip to content

利用 socket.io 实现端外不用重复登录功能

翼支付的项目分为端内和端外。端内就是在翼支付 app 内打开的应用,容器是翼支付。端外就是投放到外部浏览器的,可以直接在浏览器中输入 url 访问。

在开发端外项目时,每过一段时间就需要重新登录,非常麻烦。有没有一种办法可以在开发时只需要登录一次呢?可以使用 socket.io 来达到目的。

首先我们规定应用开发时请求的接口路径中含有 /jsb/。

js
async function jsbCall(params, apiStr = '') {
	let [module, method] = apiStr.split('.');
	let options = { module, method };
	const res = await axios.post('', {
		params,
		options
	}, {
		baseURL: `http://localhost:3000/jsb/`,
		headers: {
			'content-type': 'application/json'
		}
	})
	const { code, data } = res.data;
	switch (code) {
		case 0:
			return data
		case 1:
			return {
				type: 'fail',
				data: data
			}
		case 2:
			return {
				type: 'cancel',
				data: data
			}
	}
}

定义这个 jsb 方法的作用是对于某些方法,我们需要利用 socket.io 使用端内的方法,来获取我们当前在翼支付 app 里用户的登录信息。

比如我们想要获取用户的手机号:

js
// 获取用户的手机号
const getProductNo = () => {
	return new Promise((resolve) => {
		jsbCall(
			{
				noAutoLogin: true
			},
			'User.getProductNo'
		)
			.then((res) => {
				resolve(res);
			})
			.catch(() => {
				resolve('');
			});
	});
};

接下来就是写服务端了。

当 post 请求路径中含有 /jsb/,就会走 app.post('/jsb/*')。

js
const express = require("express")
const app = express()
const cors = require('cors');
const http = require('http')
const path = require('path')
const server = http.createServer(app)
const {Server} = require("socket.io")
const io = new Server(server)

app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(express.static(path.join(__dirname, '/static')))
// 允许跨域
app.use(cors());

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, '/static/index.html'))
})

let cbId = 0
const cbStore = {}
app.post('/jsb/*', async (req, res) => {
  // const params = {type: "barCodeAndQrCode"}
  // const options = {module: "Device", method: "OCR"}
  const {params, options} = req.body
  io.emit('jsb-req', params, options, cbId)
  cbStore[cbId] = (result) => {
    res.json(result)
  }
  cbId ++
})


io.on('connection', (socket) => {
  // 客户端和服务端连接成功
  socket.on('jsb-res', (res,  cbId) => {
    const callback = cbStore[cbId]
    callback && callback(res)
  })
})

const port = 3000
server.listen(port, () => {
  console.log(`已开启 socket.io 服务,地址 localhost:${port}`)  
})

我们还需要在翼支付 app 里访问我们启动的 socket 服务。接下来我们在浏览器里打开的应用获取的用户信息就是我们在端内登录的用户了,而端内的用户信息是会缓存很久的,所以不用频繁登录。

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="/socket.io/socket.io.js"></script>
  <script src="./mgssdk1-1-4.min.js"></script>
  <!-- BestpayHtml5 JSBridge -->
  <script type="text/javascript" src="https://h5.bestpay.cn/common/js/bestpay-html5-3.0.js"></script>
  <!-- 定义了两个方法 mgsCallH5、mgsCallNa 方法,判断是否是 mPass 平台,以决定 window.request 被赋值哪个方法 -->
  <!-- 是 mPass 平台就使用 mgsCallNa 方法,这个方法内部是使用了 AlipayJSBridge 来请求 mgs 接口 -->
  <!-- 不是 mPass 平台就使用 mgsCallH5 方法,这个方法内部是使用了 MP.MGS.call 来请求 mgs 接口  -->
  <script src="./config.js"></script>
  <!-- 里面包含两个方法,分别封装了 BestpayHTML5、AlipayJSBridge 两个 js 桥方法。 -->
  <!-- 以此来调用原生能力,比如扫码,拍照,人脸识别等 -->
  <script src="./jsb.js"></script>
  <!-- 引入 vconsole -->
  <script src="./vconsole.min.js"></script>
  <script>
    new window.VConsole();
  </script>
  <body>
    <button id="button1">按钮</button>
  </body>
  <script>
    const socket = io()

    button1.addEventListener('click', () => {
      // console.log('hello');
    })

    socket.on('jsb-req', async (params, options, cbId) => {
      // 下面两种 JS 桥方法都可以调起端内方法,有什么区别?
      // Bestpay JS 桥方法(有些方法请求会抛错)
      // const res = jsbH5Call(params, options)
      // 阿里 JS 桥方法
      const res = await jsbCall(params, options)
      socket.emit('jsb-res', res, cbId)
    })
  </script>
</head>
<body>
  
</body>
</html>

最后,这只是利用 socket.io 实现端外应用不用登录的一个简单的服务工具。可以在这基础上进行改进优化,以进一步提高开发效率。

Released under the MIT License.