UKEY开发,vue+websocket实现⽤户登录UKEY认证
⾸先,在开始开发之前,先了解⼀下UKEY的⽤户登录流程,我前⾯整理了⼀些登录的流程:
点这⾥查看登录流程:
OK,了解了登录流程,我们来开始看看在vue中是怎么样进⾏实际的开发的。
⾸先你需要在导航收尾中初始化websocket的连接:
router.beforeEach((to, from, next) => {
// 初始化后后能够监听UKEY拔插事件
store.dispatch({
type: "startUkey"
});
}
补充说明:为了安全性,我们的需求是这样的:⽤户只有在UKEY插⼊的情况下才能够登录后台,⽤户拔出UKEY后就注销该⽤户。所以需要在导航守卫中初始化UKEY。
接下来,我们需要编写websocket逻辑处理,我将所有的websocket处理都放在vuex的action⾥⾯,下⾯是action的全部代码:
import { SIGN_OUT } from "@/store/modules/user/constant";
import axios from "@/modules/axios";
import route from "@/router";
import { user as userServer } from "@/modules/server-url";
var s_pnp = "";
if (!s_pnp) {
s_pnp = new WebSocket("ws://127.0.0.1:4006/xxx","usbkey-protocol");
}
const getRandomCode = async (commit,callback) => {
try {
// 获取签名使⽤的随机数
const data = await axios.RandomCode);
commit({
type: "SET_RANDOM_CODE",
playload: {
code: data
}
});
callback({
succ_status: 3,
msg: "获取签名随机数成功",
data: {
random_code_status: true,
random_code: data
}
});
} catch (err) {
if (err && de) {
callback({
err_status: 6,
err_status: 6,
msg: "获取签名随机数失败",
data: {
random_code_status: false,
random_code: ""
}
})
}
}
};
const listenUkey = (dispatch, commit, state, request = { type: 0, pin_code: "", callback: () => {}}) => {
try {
var Path = ""; // 路径
var insert_status = 0; // ukey的拔插事件会执⾏两次,防⽌第⼆次执⾏
if (pe != 0) { // 不是初始化流程
let socketStatus = s_pnp.GetWebsocketStatus();
if (socketStatus == 0) {
setTimeout(() => {
s_pnp.send(JSON.stringify({FunName: "ResetOrder"}));
},500);
} else {
s_pnp.send(JSON.stringify({FunName: "ResetOrder"}));
}
}
s_pnp.pen = function () {
s_pnp.send(JSON.stringify({FunName: "ResetOrder"})); // 这⾥调⽤ResetOrder将计数清零,这样,消息处理处就会收到0序号的消息,通过计数及序号的⽅式,    };
// 在使⽤事件插拨时,注意,⼀定不要关掉Sockey,否则⽆法监测事件插拨
ssage = function (Msg) {
let PnpData = JSON.parse(Msg.data);
if (pe == "PnpEvent") { // 如果是插拨事件处理消息
if (PnpData.IsIn) { // 监听到插⼊
if (insert_status === 1) return;
console.log("ukey插⼊");
insert_status = 1;
s_pnp.send(JSON.stringify({FunName: "ResetOrder"}));
} else { // 监听到拔出
if (insert_status === 2) return;
console.log("ukey拔出");
insert_status = 2;
if (typeof request.callback == "function") {
request.callback({
err_status: 2,
msg: NO_UKEY
});
}
if (route.history.current.path == "/") return false;
// 检测到UKEY拔出,退出登录
return dispatch(SIGN_OUT);
}
}
if (pe == "Process") { // 如果是事件处理流程
var order = der;
websocket和socketif (state.serve_random_code.length == 0) {
getRandomCode(commit,request.callback);
} else {
if (typeof request.callback == "function") {
request.callback({
succ_status: 3,
msg: "获取签名随机数成功",
data: {
random_code_status: true,
random_code: state.serve_random_code
random_code: state.serve_random_code
}
});
}
}
if (order == 0) {
s_pnp.send(JSON.stringify({FunName: "FindPort",start: start})); // 查加密锁
} else if (order == 1) {
if ( PnpData.LastError != 0 ) {
if (typeof request.callback == "function") {
request.callback({
err_status: 2,
msg: "未检测到UKEY"
});
}
return false;
}
// 已插⼊UKEY
Path = urn_value; // 获得返回的UK的路径
s_pnp.send(JSON.stringify({FunName: "GetChipID",Path:Path})); // 获取锁唯⼀ID        } else if (order == 2) { // 获取到锁ID
if ( PnpData.LastError != 0 ) {
if (typeof request.callback == "function") {
request.callback({
err_status: 3,
msg: "获取锁ID失败"
});
}
return false;
}
if (typeof request.callback == "function") {
request.callback({
succ_status: 1,
msg: "获取锁ID成功。",
data: {
ukey_id: urn_value
}
});
}
// 返回设置在锁中的⽤户名
s_pnp.send(JSON.stringify({FunName: "GetSm2UserName",Path:Path}));
} else if (order == 3) { // 获取到⽤户⾝份
if ( PnpData.LastError != 0 ) {
if (typeof request.callback == "function") {
request.callback({
err_status: 4,
msg: "获取⽤户名失败。"
});
}
request.callback({
err_status: 4,
msg: "获取⽤户名失败。"
});
return false;
}
if (typeof request.callback == "function") {
request.callback({
succ_status: 2,
msg: "获取⽤户⾝份成功。",
data: {
account: urn_value
}
});
}
}
}
if (pe == 1) { // 验证Pin码
if (order == 3) {
// 对数据进⾏签名,验证pin码,在内部会验证pin码,验证正确后才能够签名,验证错误后则pin码错误              s_pnp.send(JSON.stringify({FunName: "YtSign",SignMsg:state.SignMsg,Pin:state.Pin,Path:Path}));          } else if (order == 4) {
if ( PnpData.LastError != 0 ) {
request.callback({
err_status: 5,
msg: "Pin码验证失败。"
});
return false;
}
request.callback({
succ_status: 4,
msg: "签名成功",
data: {
autograph: urn_value
}
});
commit({
type: "SET_PIN_CODE",
playload: {
code: request.pin_code
}
});
}
}
}
};
r = function () {
console.log("连接错误");
};
lose = function () {
console.log("连接关闭");
};
} catch (e) {
<(e.name + ": " + e.message);
return false;
}
};
export default {
startUkey({ dispatch, commit, state }, request = { type: 0, callback: (res) => {} }) {
// 不兼容IE10以下的浏览器
if (navigator.userAgent.indexOf("MSIE") > 0 && !navigator.userAgent.indexOf("opera") > -1) {
commit({
type: "SET_IE10_UNDER",
playload: {
status: true,
msg: UNDER_IE10
}
});
request.callback({
err_status: 1,
msg: UNDER_IE10
});
return false;
}
try {
listenUkey(dispatch, commit, state, request);
} catch (err) {
<(err);
}
}
};
是不是⼀头雾⽔?别急这⾥就给你说明⼀下,⾸先websocket的⽣命周期要了解⼀下的:
事件事件处理程序描述
pen连接建⽴时触发
ssage客户端接收服务端数据时触发
r通信发⽣错误时触发
lose连接关闭时触发
我们这⾥主要⽤到的是message事件,在我的理解中message事件就是⼀个监听,⽽⽬标返回⼀次信息,就执⾏⼀次message事件,⽽UKEY是以轮询的⽅式进⾏通讯的,所以每次执⾏send函数后,都会触发message事件,每次都触发相同的函数时我们就需要根据状态来区分流程了,UKEY⾃⾝就有⼀套
流程的记录,也就是上⾯代码中的order属性了,每执⾏⼀个send都会创建⼀个流程,order就会加⼀。
因为登录是需要⽤户输⼊Pin码的,不能⼀套流程直接⾛完,需要中途⽤户触发验证来进⾏验证Pin码的流程,所以这⾥我通过type来标识是不是⽤户主动触发的验证Pin码流程。
⽤户触发验证Pin码的代码如下:
<template>
<div ref="signInDom" class="sign-in" >
<el-form
:show-message="true"
:model="form"
:rules="rules"
:ref="formName"
label-width="15px"
class="sign-in-form"
@submit.native.prevent="submitForm">
<div class="sign-in-logo">
<img :src="logoSrc" alt="">
</div>
<div class="sign-in-info">
<span>{{ tips }}</span>
</div>
<div class="sign-in-form-item">
<i class="form-input-icon icon-tubiao211"/>
<el-form-item prop="account">
<el-input
ref="accountInput"
v-model="form.account"
type="text"
placeholder="⽤户名"
disabled="disabled"
auto-complete="off" />
</el-form-item>
</div>
<div class="sign-in-form-item">
<i class="form-input-icon icon-mima1"/>
<el-form-item prop="password">

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。