首先,在开始开发之前,先了解一下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.post(userServer.getRandomCode);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 && err.code) {callback({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 (request.type != 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.Socket_UK.onopen = function () {s_pnp.send(JSON.stringify({FunName: "ResetOrder"})); // 这里调用ResetOrder将计数清零,这样,消息处理处就会收到0序号的消息,通过计数及序号的方式,从而生产流程};// 在使用事件插拨时,注意,一定不要关掉Sockey,否则无法监测事件插拨s_pnp.onmessage = function (Msg) {let PnpData = JSON.parse(Msg.data);if (PnpData.type == "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 (PnpData.type == "Process") { // 如果是事件处理流程var order = PnpData.order;if (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}});}}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;}// 已插入UKEYPath = PnpData.return_value; // 获得返回的UK的路径s_pnp.send(JSON.stringify({FunName: "GetChipID",Path:Path})); // 获取锁唯一ID} else if (order == 2) { // 获取到锁IDif ( 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: PnpData.return_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: PnpData.return_value}});}}if (request.type == 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: PnpData.return_value}});commit({type: "SET_PIN_CODE",playload: {code: request.pin_code}});}}}};s_pnp.onerror = function () {console.log("连接错误");};s_pnp.onclose = function () {console.log("连接关闭");};} catch (e) {console.error(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) {console.error(err);}}
};

是不是一头雾水?别急这里就给你说明一下,首先websocket的生命周期要了解一下的:

事件 事件处理程序 描述
open Socket.onopen 连接建立时触发
message Socket.onmessage 客户端接收服务端数据时触发
error Socket.onerror 通信发生错误时触发
close Socket.onclose 连接关闭时触发

我们这里主要用到的是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"><el-input  ref="passwordInput"v-model="form.password" type="password" placeholder="密码" auto-complete="off"@keyup.enter="enterEvent"/></el-form-item></div>   <div class="sign-in-form-item"><i class="form-input-icon icon-mima1"/><el-form-item prop="pinCode"><el-input  ref="pinCodeInput"v-model="form.pinCode" type="password" placeholder="pin码" auto-complete="off"@keyup.enter="enterEvent"/></el-form-item></div><div v-if="ukey_id.length>0" class="sign-in-ukey"><span>当前UKEY的ID为:</span><span>{{ ukey_id }}</span></div></el-form></div>
</template><style lang="less">
@import "./index";
</style><script>
import logoSrc from "./images/sign-in.png";
import { mapActions, mapState, mapMutations } from "vuex";
import axios from "@/modules/axios";
import { user } from "@/modules/server-url";
import { NO_UKEY, UNDER_IE10, LOAD_UKEY_START } from "@/store/modules/ukey/constant";export default {name: "SignIn",data() {const validateAccount = (rule, value, callback) => {if (value === "") {callback(new Error("用户名不能为空"));}else {callback();}};const validateCode = (rule, value, callback) => {if (value === "") {callback(new Error("验证码不能为空"));} else if (value.length !== 4) {callback(new Error("请输入4位验证码"));} else {callback();}};const validatePinCode = (rule, value, callback) => {if (value === "") {callback(new Error("Pin码不能为空"));}else {callback();}};return {logoSrc,formName: "signInForm",// 表单数据form: {account: "",password: "",pinCode: "",randomNum: "",dataSign: "",checked: true},// 验证规则rules: {account: [{ required: true, validator: validateAccount, trigger: "blur" }],password: [{ required: true, message: "密码不能为空", trigger: "change" }],pinCode: [{ required: true, message: "pin码不能为空", trigger: "change" }]},codeForm: {smsCode: ""},codeRules: {smsCode: [{ required: true, validator: validateCode, trigger: "blur" }]},pinCodeRules: {pinCode: [{ required: true, validator: validatePinCode, trigger: "blur" }]},/** 正在登陆 */isSignIn: false,/** 或验证码冷却中 */codeIsLoading: false,/** 验证码发送中 */codeIsSending: false,/** 验证码倒计时 */countTime: 180,countId: null,codeInnerText: "重新发送",// 展示验证码输入窗口showCode: false,tips: "",codeStatus: "fail",phone: "",ukey_id: "", // ukey的唯一IDshowDownload: false, // 是否显示下载提示ukey_error: false,randomCodeLoad: true, // 签名随机数加载中showNotify: false // 是否显示右下角提示};},computed: {...mapState({user: state => state.user}),loginStatus() {if (this.randomCodeLoad) {return true;}if (this.isSignIn) {return true;} else {return false;}},loginStatusMsg() {if (this.randomCodeLoad) {return "加载中";}if (this.isSignIn) {return "登录中";} else {return "登录";}},getTips() {return this.$store.state.user.signMsg;},getIsSignedOut: state => state.user.isSignedOut,/** 是否需要短信验证 */getSmsState: state => {return {needSmsVerify: state.user.needSmsVerify,codeStatus: state.user.codeStatus};}},watch: {getIsSignedOut(isSignedOut) {/** 退出登录成功 */if (isSignedOut) {this.initForminitForm();}},/** 设置提示信息 */getTips(tips) {this.tips = tips;}},mounted() {this.initForminitForm();this.LOAD_UKEY_START({type: 0, callback: this.wesocketRes});},methods: {...mapActions([SIGN_IN,LOAD_UKEY_START]),...mapMutations([SIGN_IN_FULLFILLED]),/** 输入框初始化和聚焦 */initForminitForm() {const accountsHistory = getItem("signInHistory");if (accountsHistory) {this.form.account = accountsHistory.pop();this.focusInput("passwordInput");} else {this.focusInput("accountInput");}},/** 表单提交 */submitForm() {if (this.isSignIn) return;this.isSignIn = true;this.$refs[this.formName].validate(async valid => {if (valid) {this.LOAD_UKEY_START({type: 1, pin_code: this.form.pinCode, callback: this.wesocketRes});} else {this.isSignIn = false;return false;}});},async wesocketRes(res) {// console.log("wesocket返回值",res);if (res.err_status) {this.tips = res.msg;this.ukey_error = true;this.isSignIn = false;if (res.err_status == 2) {this.tips = res.msg;this.showDownload = true;this.ukey_id = "";this.showTipsNotify();}if (res.err_status == 6) {this.form.randomNum = res.data.random_code;}}if (res.succ_status) {this.tips = "";this.ukey_error = false;if (res.succ_status == 1) {this.ukey_id = res.data.ukey_id;}if (res.succ_status == 2) {this.showDownload = false;this.form.account = res.data.account;}if (res.succ_status == 3) { // 签名随机数this.randomCodeLoad = false;this.form.randomNum = res.data.random_code;}if (res.succ_status == 4) {this.form.dataSign = res.data.autograph;if (!this.judgeUkeyStatus()) return;// 签名成功后才能进行登录let formData = Object.assign({},this.form);delete formData.pinCode; // 不能把PIN码放在网络中传输let result = await this.SIGN_IN(formData, this.$router);this.isSignIn = false;if (result && result.needSmsVerify) {this.showCode = true;await this.$nextTick();// 如果需要验证码登陆,获取验证码this.getCode();this.phone = this.user.user.phone;const { codeStatus } = result;// 需要短信验证码  code === 6 超过短信发送次数  code === 0 正确if (+codeStatus === 0 || +codeStatus === 11) {this.codeStatus = "success";} else {this.codeStatus = "fail";}}}}},/* 检查锁状态 */judgeUkeyStatus() {if (this.ukey_error) {return false;}if (this.form.randomNum.length == 0) {return false;}if (this.form.dataSign.length == 0) {return false;}return true;},/** 重置表单 */resetForm() {if (!this.showCode && (this.form.account || this.form.password)) {if (this.$refs[this.formName] !== undefined) {this.$refs[this.formName].resetFields();}}},// 显示下载驱动提示showTipsNotify() {let that = this;if (that.showNotify) return false;that.showNotify = true;this.$notify({title: "提示",dangerouslyUseHTMLString: true,duration: 6000,position: "bottom-right",message: `<div><div style='margin-bottom: 10px;'>只有在UKEY插入并且Pin码正确后才能登陆哦。如果提示检测不到UKEY,请确认是否下载并安装了浏览器驱动。</div><div><a style='color: #03A9F4;' href='#'>立即下载驱动</a></div></div>`,onClose: () => {that.showNotify = false;}});},resetCodeButton() {this.clearCounter();this.codeIsLoading = false;},clearCounter() {this.codeIsLoading = false;if (this.counterId) {clearInterval(this.counterId);this.counterId = null;}}}
};
</script>

如代码所示,我使用了一个回调函数来处理UKEY函数的执行结果,提示信息或者认证状态。

UKEY开发,vue+websocket实现用户登录UKEY认证相关推荐

  1. html 存储登录状态,Vue中保存用户登录状态实例代码

    首先我们假设,这里的登录组件(register.vue)是App.vue组件的子组件,是通过路由进入登录组件的. 登录组件中用户点击登录后,后台会传过来一个用户名,我的App.vue组件中需要拿到这个 ...

  2. oracle用户登录的认证方式

    1.操作系统认证 对于操作系统认证,其实蛮简单的, 只需要将该用户添加到dba(针对sysdba权限)或oper(针对sysoper权限)组中, 就可以使用 "sqlplus / as sy ...

  3. 3、Vue+ElementUI制作用户登录页面

    前面两篇简单了解了一下vue和SPA,现在来用Vue+ElementUI做一个用户登录页面,ElementUI是Element出的一套针对vue的UI库,类似的UI库非常多,ElementUI只是其中 ...

  4. java web网站实例_Java Web开发: 基于HttpServlet的用户登录网站 实例

    2013-02-04 20:50 814人阅读 评论(0) 一个简单的小例子,基于httpservlet的用户登录(杂家是菜鸟刚接触javaee,只能从最简单出发了),首先看业务逻辑: 有三个文件组成 ...

  5. 统一用户登录管理认证LDAP 服务端部署

    网上找了好多关于LDAP统一账户管理的文件,好多都是粘贴复制,能用得上的少之又少,正好最近又用到这个,于是着手看了郭老师的视频,顺便把自己学习的过程记录下来,供大家学习参考. 1.实验环境: [roo ...

  6. vue router.beforeEach跳转路由验证用户登录状态

    使用vue判断验证用户登录状态 导航钩子类似于生命周期钩子,包含路由进入前,进入后,更新时,退出前等几个周期,主要用于控制导航的前进后退或跳转等. 其中router.beforeEach就是路由进入前 ...

  7. vue 不同用户登录系统拥有不同查看菜单导航的权限

    业务需求: 不同的登录用户进入系统后可以查看不同的菜单权限 比如:admin用户进入系统有四个权限:预报产品.预警产品.防灾减灾.系统管理,如下图: 而aaaa用户进入系统只有三个查看权限:预报产品. ...

  8. 【笑小枫的SpringBoot系列】【九】SpringBoot用户登录功能实现

    关于本文 其实用户登录拦截的这块不想这么早写,加个登录后面好多东西就要考虑登录状态了,我其实想把这个系列写成非必要关系,解耦性比较强的系列.但是,写完redis,总是感觉登录是对它最简单的实践,那就加 ...

  9. oracle用户登录的3种认证方式

    url:http://www.cnblogs.com/ivictor/p/4213823.html Oracle对于普通账户和超级管理员(指sysdba和sysoper)的认证机制不一样,前者是通过数 ...

  10. 技能学习:学习使用php(tp6框架) + vue.js,开发前端全栈网站-6.用户登录(二),token验证

    技能学习:学习使用php(tp6框架) + vue.js,开发前端全栈网站-6.用户登录(二),token验证 技能学习:学习使用php(tp6框架) + vue.js,开发前端全栈网站-1.工具和本 ...

最新文章

  1. xgboost重要参数1
  2. Slam十四讲(第二版):1、习题
  3. 3ds Max中的V-Ray学习
  4. 智能车竞赛技术报告 | 智能车视觉 - 西南科技大学 - 西科二队
  5. python可以自学吗-python能够自学吗
  6. 第二章:2.2 开始第一个 web Django 项目的创建和应用
  7. 从代理机制到Spring AOP,这篇给你安排的明明白白的
  8. 收藏!这些 IDE 使用技巧,你都知道吗
  9. 企业管理 为什么老板再难,也不会放弃?而员工做得不顺,就想逃走?
  10. 【数学建模】线性规划模型MATLAB求解(最优化)
  11. amazeui学习笔记--css(基本样式3)--文字排版Typography
  12. 【转】Google Maps Android API V2的使用及问题解决
  13. 高德车道级导航已上线8城 新增支持小米11 Pro
  14. 第 20 章 观察者模式
  15. Leetcode杯 第三题解答(动态规划)
  16. ffmpeg 如何音视频同步
  17. 如何利用模型对大客户风险评估——信用额度进行推测
  18. 起风了数字简谱用计算机,起风了钢琴简谱-数字双手-买辣椒也用券
  19. 计算机网络有什么部分组成,计算机网络有哪些组成部分和详细对比
  20. 解决网课作业无法粘贴问题

热门文章

  1. 【JAVA】tomcat 支持excel07下载
  2. 关于ob函数的使用和应用场景
  3. 尚硅谷redis视频
  4. linux中VScode中文乱码问题:一行代码解决
  5. 基于深度学习的实时噪声抑制——深度学习落地移动端的范例
  6. 调研报告:原型图出稿
  7. 基于MATLAB的DS证据融合代码
  8. LD_PRELOAD实现API劫持
  9. 黑马程序员_毕向东_Java基础视频教程第01天_20151010(六)
  10. matlab教程 for循环,Matlab简单教程:循环