不同用户具有不同的权限 如果管理员用户被别人知晓了怎么办? 为了解决该用户 账号密码泄露导致的误操作。
前端思想是获取摄像头权限 通过canvas 获取base64图片 然后转为二进制文件流 通过FormData(form表单提交)和后端交互 的方式进行识别
创建一个sql 里面存一些有权限的人员信息包含照片等 用上传的照片和sql里面的数据进行对比人脸识别 进而知道进入当前系统的人是否具有某些权限 当然为了界面友好还需要有权限人员库用于展示
中间遇到了低版本浏览器无法使用canvas的问题 所以此方法只支持最新的浏览器
上传至服务器时出现http的谷歌因为安全设置问题 导致无法获取摄像机资源 需用https解决 或通过配置chrome安全限制解决 详情可以看我的下个文章
调用方法
living-validation>
data设置如下
livingValidationResponse:{
isShowDialog: false,
title: "活体检测",
callbackName:"",
item: {}
},
就在下面了 哈哈
<template>
<div>
<add-dialog :response="response" v-if="response.isShowDialog" ref="addDialogRef" @closeDialog="closeDialog">
<div slot="libBox" class="libBox">
<div class="auto">
<video id="video" width="480" height="320" autoplay>video>
<canvas id="canvas" width="480" height="320" style="display: none;">canvas>
<img src="/static/images/body_default.png" id="img" width="480" height="320" style="margin-left: 20px;">
<div>
<button id="capture" title="点击进行拍照" @click="takePhoto">拍照button>
div>
<div>
<button id="sure" title="是否用这张图片进行验证" @click="sure">确认button>
div>
div>
div>
add-dialog>
<loading :loadingStatus="loadingStatus">loading>
div>
template>
<script>
import loading from "src/biz_components/loading/loading.vue";
import addDialog from "biz_components/dialog.vue";
export default {
name: "livingvalidation",
props: ["response"],
/* authuor wenbin by 2019-03-26
response:{
isShowDialog: false, //是否展示
title: "活体检测", //标题
item: {} 需要传入数据用于子组件使用
},*/
components: {
addDialog: addDialog,
loading:loading
},
data: function() {
return {
File:"", //上传图片文件
streamPicture:"",
loadingStatus: {
isShowDialogMark: false,
loadingTitle: "等待"
},
}
},
created:function(){
this.$message.success('先进行人脸识别 识别成功方可进行新增布控');
},
mounted: function() {
this.initCamera();
},
methods: {
//初始化摄像头 获取摄像头权限
initCamera(){
let self = this;
navigator.getUserMedia = navigator.getUserMedia ||navigator.webkitGetUserMedia ||navigator.mozGetUserMedia;
if (navigator.getUserMedia) {
//调用用户媒体设备, 访问摄像头
navigator.getUserMedia({video : {width: 480, height: 320}},function(stream) {
var video = document.querySelector('video');
video.srcObject = stream;
self.streamPicture = stream; //关闭摄像头需要用
video.onloadedmetadata = function(e) {
video.play();
};
}, function(err) {
console.log("访问用户媒体设备失败: " + err.name);
})
} else {
self.$message.error('不支持访问用户媒体');
}
},
//拍照点击
takePhoto(){
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
context.drawImage(video, 0, 0, 480, 320);
// 获取图片base64链接
var image = canvas.toDataURL('image/png');
// 定义一个img
var img = document.getElementById("img");
//设置属性和src
//img.id = "imgBoxxx";
img.src = image;
//将图片添加到页面中
//document.body.appendChild(img);
function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {type: mime});
}
this.File = dataURLtoFile(image, 'aa.png');
//console.log(dataURLtoFile(image, 'aa.png'));
return dataURLtoFile(image, 'aa.png');
},
//确认图片上传 人脸识别 确定权限
sure(){
debugger;
var self = this;
//console.log(this.File);
if(this.File == ""){ //如果不点击拍照
self.$message.warning(`请先拍照再点击确定`);
return;
}
this.loadingStatus.isShowDialogMark = true;
var formData = new FormData();
formData.append("file",this.File);
$.ajax({
type: "POST", // 数据提交类型
url: "/gateway/disposition-service/face_verification", // 发送地址
data: formData, //发送数据
async: true, // 是否异步
processData: false, //processData 默认为false,当设置为true的时候,jquery ajax 提交的时候不会序列化 data,而是直接使用data
contentType: false,
success:function(data){
if(data.data){
self.$refs.addDialogRef.closeDialog();//关闭 dialog 组件 和 摄像机流
self.$emit('realAddNewTask'); //触发父组件realAddNewTask方法 弹出新建布控窗口
self.$message.success(`${data.message}`);
}else{
self.$message.warning(`${data.message}`);
}
self.loadingStatus.isShowDialogMark = false;
},
error:function(e){
self.loadingStatus.isShowDialogMark = false;
self.$message.warning(`${e}`);
//console.log("不成功"+e);
}
});
},
closeDialog(){
//console.log("closeDialog");
if(this.streamPicture){
this.streamPicture.getTracks()[0].stop();//如果是活体检测界面 关闭摄像机
}
}
},
}
script>
<style lang="less" scoped>
@import "../less/ixwpre.less";
/* 样式 写的比较随意 感觉不好看 可以自行修改*/
.libBox{
width:100%;
height:100%;
background: url("/static/images/searchIdentity.png") no-repeat;
#capture{
position: absolute;
right: 190px;
bottom: -40px;
}
#video{
position: absolute;
right: 0;
top: 0;
}
#img{
position: absolute;
left: 0;
top: 0;
}
.auto{
position: absolute;
left: 50%;
top: 50%;
height: 320px;
margin-top: -160px;
}
#sure{
position: absolute;
left: 210px;
bottom: -40px;
}
button{
cursor: pointer;
margin: 0 auto;
border: 1px solid #f0f0f0;
background: #5CACEE;
color: #FFF;
width: 100px;
height: 36px;
line-height: 36px;
border-radius: 8px;
text-align: center;
/*禁止选择*/
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently not supported by any browser */
}
}
style>
基于组件 dialog 不同的dialog不尽相同 接下来是我写的dialog
<template>
<transition name="dialog-frame" :css="response.isUseTransition">
<div class="model-dialog" :style="setFullScreenStyle" v-if="response.isShowDialog">
<iframe scroll="none" src="about:blank" class="model-dialog-iframe" >iframe>
<div class="dialog-main">
<p class="dialog-title-box"><span class="dialog-title">{{response.title}}span>
<a href="javascript:void(0)" class="fr icon-dialog-close" @click="closeDialog">a>
p>
<div style="position:absolute;left:0;right:0; top: 30px; bottom:0; overflow-y: auto; overflow-x: hidden;" class="model-dialog-content">
<slot name="libBox">
slot>
div>
div>
div>
transition>
template>
<script>
export default {
name: "newDialog",
props: ["response", "closeFn", "isFullScreen"],
data: function() {
return {
modelBg: false
}
},
computed: {
setFullScreenStyle: function() {
if(this.isFullScreen) {
return {'top': '0'};
} else {
return {'top': '50px'};
}
}
},
watch:{
response:function(data){
console.log("transition",data.isUseTransition)
}
},
methods: {
/**
* 关闭弹层
* @return {[type]} [description]
*/
closeDialog: function() {
//console.log("closeDialog11");
$('#frequentViolationsId').css('overflow','');
var self = this;
//console.log(self.response);
// if(self.response.streamPicture){
// self.response.streamPicture.getTracks()[0].stop();//如果是活体检测界面 关闭摄像机
// }
self.$emit('closeDialog');
if(self.closeFn) self.closeFn();
setTimeout(function() { //添加延迟 先让ocx先消失
self.response.isShowDialog = false;
}, 90);
}
}
}
script>
<style lang="less">
@imgPath: "../assets/images/dialog";
.dialog-frame-enter {
opacity: 0;
transform: translateY(800px);
-webkit-transform: translateY(800px);
}
.dialog-frame-enter-active {
opacity: 1;
transition: all .2s ease;
}
.dialog-frame-leave{
opacity: 1;
}
.dialog-frame-leave-active {
opacity: 0;
transition: all .5s ease;
transform: translateY(800px);
-webkit-transform: translateY(800px);
}
.icon-dialog-close {
position: absolute;
right: 0;
top: 0;
width: 30px;
height: 30px;
background: url("@{imgPath}/close-btn.png") no-repeat;
&:hover {
background-position: -30px 0;
}
}
.model-dialog {
position: fixed;
top: 50px;
bottom: 0;
left: 0;
right: 0;
z-index: 950;
}
.model-dialog-iframe{
position: absolute;
width: 100%;
height: 100%;
}
.dialog-main {
position: absolute;
height: 100%;
width: 100%;
bottom: 0px;
left: 0px;
background: #fff;
z-index: 900;
overflow: hidden;
}
.dialog-title-box {
position: relative;
height: 30px;
background:#303845;
color: #fff;
text-align: left;
padding-left: 30px;
font-size: 14px;
line-height: 30px;
}
.dialog-title {
position: relative;
}
style>