活体检测代码提交

This commit is contained in:
袁帅
2022-10-24 17:06:53 +08:00
parent 4965600636
commit 38fd61e948
8 changed files with 753 additions and 25 deletions

View File

@ -17,6 +17,11 @@ export default {
// changeOrigin: true,
// pathRewrite: { '^': '' },
// },
'/living/api/*': {
target: 'https://ai.cubigdata.cn:5001',//连接天宫的ng
changeOrigin: true,
pathRewrite: { '/living/api': '' },
},
'/api/*': {
target: 'http://10.242.31.158:18022',//连接天宫的ng
changeOrigin: true,

View File

@ -6,6 +6,8 @@
<!-- <link href="cs.css" rel="stylesheet" type="text/css"> -->
<script src="jquery.js"></script>
<script src="jquery.webcam.min.js"></script>
<script src="jsrsasign-latest-all-min.js"></script>
<script src="living.min.js"></script>
<!-- <script src="excanvas.js"></script> -->
</head>
<body>
@ -14,6 +16,9 @@
<img id="base64image" src='' width="450" height="320" style="float: left; clear: both;"/> -->
<script type="text/javascript">
var pos = 0, ctx = null, saveCB,w = 380,h= 200, image = new Array();
var lv = null;
var action = 1,timer = 1000;
var liveDetectPhoto = null;
var canvas = document.createElement("canvas");//创建画布指定宽度和高度
canvas.setAttribute('width', 320);
canvas.setAttribute('height', 240);
@ -46,7 +51,8 @@
if (pos == 4 * 320 * 240) {
//把图像放到画布上,输出为png格式
ctx.putImageData(img, 0, 0);
window.parent.postMessage({"image": canvas.toDataURL("image/png")}, '*');
liveDetectPhoto = base64ToBlob(canvas.toDataURL("image/png"));
//window.parent.postMessage({"image": canvas.toDataURL("image/png")}, '*');
image = new Array();
pos = 0;
}
@ -59,7 +65,6 @@
// pos = 0;
// }
},
onCapture: function(data) {
webcam.save();
// Show a flash for example
@ -79,17 +84,102 @@
// $('#snapBtn').on('click', function() {
// webcam.capture();
// });
window.addEventListener('message', receiveMessageFromParent, false);
function receiveMessageFromParent ( event ) {
if(event.data == 'releaseCamera'){
location.reload();
}else if(event.data = 'capture'){
// liveDetectStart();
webcam.capture();
console.log(1,liveDetectPhoto);
}
//$('#base64image').attr('src', 'data:image/jpg;base64,' + event.data.data);
};
window.addEventListener('message', receiveMessageFromParent, false);
function setLiveDetectToken() {
let appKey= 'nkYy3g1yT0alE8pF6a1UTC4I';
let appSecrect = 'L30zHpTyAtTlY7tTCpbzdxxKCQgwWIQL';
// Header
var header = {
typ: "JWT", // 声明类型
alg: "HS256" // 声明加密的算法 通常直接使用 HMAC SHA256
};
var payload = {
iss: appKey,
iat: moment().unix(),
exp: moment()
.add(29, "minutes")
.unix()
};
var sHeader = JSON.stringify(header);
var sPayload = JSON.stringify(payload);
var sJWT = KJUR.jws.JWS.sign("HS256", sHeader, sPayload, {
utf8: appSecrect,
});
return sJWT;
}
function liveDetectStart () {
liveDetectStop();
let _lv = new Living(null, {
timer: 10000,
action : [1,3,2],
token: setLiveDetectToken(),
proxy:'/living/api',
getFacePicture:function(){
webcam.capture();
return liveDetectPhoto;
},
//开始活体检测此时token已鉴权成功
onDetectStart:function(){
//setSubmitLoading(true);
},
//每轮检测开始
onDetectActionStart:function(action){
action = action
},
//每轮检测计时
onDetectActionProgress:function(action,timer){
timer = timer;
window.parent.postMessage({"actionAndTimer": {action:action,timer:timer}}, 'detectStatus');
},
//每轮检测结束
onDetectActionFinish:function(action,data){
// this.clearCanvas()
timer = 10000;
console.log(data)
},
//活体检测完成
onDetectFinish:function(data){
window.parent.postMessage({"detectResult": data}, 'detectFinish');
}
});
//开始检测
_lv.start();
lv = _lv;
}
/**
* 停止活体检测
*/
function liveDetectStop (){
lv && lv.stop();
}
/**
* base64Url转blob
*/
function base64ToBlob(base64) {
var parts = base64.split(";base64,");
var contentType = parts[0].split(":")[1];
var raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; i += 1) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], { type: contentType });
};
});
</script>
</body>

File diff suppressed because one or more lines are too long

1
public/faceLoginIE/living.min.js vendored Normal file
View File

@ -0,0 +1 @@
/*Obfuscated by JShaman.com*/import _0x4e5570 from'axios';const service=_0x4e5570['create']({'withCredentials':!![],'timeout':0x493e0});service['interceptors']['response']['use'](_0x4576b6=>{const _0x2cf0d2=_0x4576b6['data'];if(_0x2cf0d2['code']!=='0'){return Promise['reject'](_0x2cf0d2);}else{return _0x2cf0d2;}},_0x39797a=>{console['log']('err'+_0x39797a);return Promise['reject'](_0x39797a);});const Living=function(_0x484981,_0x4b85f7){this['page']=_0x484981;this['token']=_0x4b85f7['token'];this['proxy']=_0x4b85f7['proxy'];this['timer']=_0x4b85f7['timer']||0x1c557^0x1e247;this['action']=_0x4b85f7['action']||[];this['onDetectStart']=_0x4b85f7['onDetectStart']&&typeof _0x4b85f7['onDetectStart']==='function'?_0x4b85f7['onDetectStart']:()=>{};this['onDetectActionProgress']=_0x4b85f7['onDetectActionProgress']&&typeof _0x4b85f7['onDetectActionProgress']==='function'?_0x4b85f7['onDetectActionProgress']:()=>{};this['onDetectActionStart']=_0x4b85f7['onDetectActionStart']&&typeof _0x4b85f7['onDetectActionStart']==='function'?_0x4b85f7['onDetectActionStart']:()=>{};this['onDetectActionFinish']=_0x4b85f7['onDetectActionFinish']&&typeof _0x4b85f7['onDetectActionFinish']==='function'?_0x4b85f7['onDetectActionFinish']:()=>{};this['onDetectFinish']=_0x4b85f7['onDetectFinish']&&typeof _0x4b85f7['onDetectFinish']==='function'?_0x4b85f7['onDetectFinish']:()=>{};this['getFacePicture']=_0x4b85f7['getFacePicture']&&typeof _0x4b85f7['getFacePicture']==='function'?_0x4b85f7['getFacePicture']:()=>{};this['requestId']='';this['t']=null;};Living['prototype']={'start'(){this['getRequestId'](_0x44585a=>{this['requestId']=_0x44585a;this['checking']();this['onDetectStart']();});},'getRequestId'(_0x218dee){service({'url':this['proxy']+'/openapi/livingDetect/v1/getRequestId','method':'get','headers':{'Authorization':'Bearer\x20'+this['token']}})['then'](_0x2f4ef4=>{_0x218dee&&_0x218dee(_0x2f4ef4['data']['requestId']);})['catch'](_0x353d53=>{});},'checkAction'(_0x59c5df){this['timeLoop'](this['action'][_0x59c5df])['then'](_0x54c88a=>{this['stopLoop']();this['onDetectActionFinish'](this['action'][_0x59c5df],{'code':0x1,'msg':'动作检测成功'});if(this['action'][_0x59c5df+(0xdcb67^0xdcb66)]){this['checkAction'](_0x59c5df+(0x74e70^0x74e71));}if(_0x59c5df===this['action']['length']-(0xb950a^0xb950b)){this['stop']();this['onDetectFinish']({'code':0x1,'msg':'活体检测成功','file':_0x54c88a});}},()=>{this['stop']();this['onDetectActionFinish'](this['action'][_0x59c5df],{'code':0x0,'msg':'动作检测失败'});this['onDetectFinish']({'code':0x0,'msg':'活体检测失败'});});},'checking'(){if(!this['action']['length']){return;}this['checkAction'](0xa3a2b^0xa3a2b);},'timeLoop'(_0x32ae49){this['onDetectActionStart'](_0x32ae49,this['timer']);let _0x39ad80=0xd6f06^0xd6f07;return new Promise((_0x303f4c,_0x52626b)=>{let _0x1b2e0e=this['timer'];this['t']=setInterval(()=>{if(_0x1b2e0e===(0x9c1ab^0x9c1ab)){this['stopLoop']();_0x52626b();return;}_0x1b2e0e-=0xc84fd^0xc8499;this['onDetectActionProgress'](_0x32ae49,_0x1b2e0e);this['upload'](_0x39ad80++,_0x32ae49,_0xe84666=>{_0x303f4c(_0xe84666);},()=>{_0x52626b();});},0x55152^0x55136);});},'upload'(_0x5545cb,_0x467698,_0x50c8e0,_0x476b10){let _0x1fb3e5=this['getFacePicture']();var _0x220463=new FormData();_0x220463['append']('picFile',_0x1fb3e5['file']);_0x220463['append']('requestId',this['requestId']||'');_0x220463['append']('actionType',_0x467698);_0x220463['append']('picSerialNumber',_0x5545cb+'');service({'url':this['proxy']+'/openapi/livingDetect/v1/start?time='+new Date()['getTime'](),'method':'post','headers':{'Authorization':'Bearer\x20'+this['token'],'Content-Type':'multipart/form-data'},'data':_0x220463})['then'](_0x40809d=>{if(_0x40809d['data']['isLiving']==='1'){_0x50c8e0&&_0x50c8e0({'file':_0x1fb3e5['file']});}})['catch'](_0x41359d=>{console['log'](_0x41359d);_0x476b10&&_0x476b10();});},'stopLoop'(){clearInterval(this['t']);},'stop'(){this['stopLoop']();this['requestId']='';}};export default Living;

View File

@ -14,9 +14,11 @@ export default class FrameFaceLogin extends React.Component {
componentDidMount() {
// 接收Iframe传递的数据
window.addEventListener("message", (e) => {
const { image } = e.data || {}; //传递的数据
if (image) {
this.props.faceCompareEvent(image);
const{detectResult,actionAndTimer} = e.data|| {};
if(e.data == 'detectFinish'){
this.props.faceCompareEvent(detectResult);
}else if(e.data == 'detectStatus'){
this.props.faceDetectStatusEvent(actionAndTimer.action, actionAndTimer.timer);
}
});
}

View File

@ -1,15 +1,17 @@
import React, { useEffect, useState, useRef } from 'react';
import { Form, Button, Input, Row, Col, Modal, Spin, message, Tabs } from 'antd';
import { UserOutlined, LockOutlined, SafetyCertificateOutlined, VideoCameraOutlined } from '@ant-design/icons';
import { UserOutlined, LockOutlined, SafetyCertificateOutlined } from '@ant-design/icons';
import './style.less';
import { changePass, rgbToBase64 } from './service';
import { changePass } from './service';
import logo from '@/images/login/logoPic.png';
import { refreshTokenApi, ZjfakeAccountLogin, ZjfakeFaceLogin } from '@/services/login';
import { history } from 'umi';
import cookie from 'react-cookies';
import moment from 'moment';
import FrameFaceLogin from '../faceLogin/FrameFaceLogin';
import { fromPairs } from 'lodash';
import Living from './living.min.js';
import { _KJUR } from './jsrsasign-latest-all-min';
import { time } from 'echarts';
const layout = {
labelCol: { span: 7 },
@ -35,7 +37,147 @@ const Index: React.FC<{}> = () => {
const mediaStreamTrack = useRef<any>();
const { TabPane } = Tabs;
const [submitLoading, setSubmitLoading] = useState<boolean>(false);
const [faceLoginDisable, setFaceLoginDisable] = useState<boolean>(false);
const [faceLoginDisable, setFaceLoginDisable] = useState<boolean>(false);
const lv = useRef<Living>();
const [timerShow, setTimeShow] = useState<boolean>(false);
const [itemShow, setItemShow] = useState<boolean>(false);
const [action, setAction] = useState<number>(1);
const [timer, setTimer] = useState<number>(10000);
/**
* 设置活体检测token
*/
const setLiveDetectToken = () =>{
let appKey= 'nkYy3g1yT0alE8pF6a1UTC4I'
let appSecrect = 'L30zHpTyAtTlY7tTCpbzdxxKCQgwWIQL'
// Header
var header = {
typ: "JWT", // 声明类型
alg: "HS256" // 声明加密的算法 通常直接使用 HMAC SHA256
};
var payload = {
iss: appKey,
iat: moment().unix(),
exp: moment()
.add(29, "minutes")
.unix()
};
var sHeader = JSON.stringify(header);
var sPayload = JSON.stringify(payload);
var sJWT = _KJUR.jws.JWS.sign("HS256", sHeader, sPayload, {
utf8: appSecrect,
});
console.log(sJWT);
return sJWT;
};
/**
* 开始人脸识别(活体和比对)
*/
const liveDetectStart = () => {
liveDetectStop();
let _lv = new Living(null, {
timer: 10000,
action : [1,3,2],
token: setLiveDetectToken(),
proxy:'/living/api',
getFacePicture:()=>{
return getLiveDetectFile()
},
//开始活体检测此时token已鉴权成功
onDetectStart:()=>{
setSubmitLoading(true);
},
//每轮检测开始
onDetectActionStart:(action:number)=>{
setAction(action);
// this.drawCanvas()
},
//每轮检测计时
onDetectActionProgress:(action:number,timer:number)=>{
setTimer(timer);
},
//每轮检测结束
onDetectActionFinish:(action:number,data:any)=>{
// this.clearCanvas()
setTimer(10000);
console.log(data)
},
//活体检测完成
onDetectFinish:(data:any)=>{
if(data.code == 1){
setAction(4);
setTimeShow(false);
//人脸比对
hanleFaceSubmit(data.file.file, null);
}else{
message.error('活体检测失败');
setSubmitLoading(false);
}
// data.code === 1? message.success('活体检测通过'):message.error('活体检测失败')
}
});
//开始检测
_lv.start();
lv.current = _lv;
}
/**
* 停止活体检测
*/
const liveDetectStop = () => {
lv.current && lv.current.stop();
}
/**
* 给活体检测提供照片
* @returns 活体照片
*/
const getLiveDetectFile = () => {
const canvas = document.createElement('canvas');
canvas.setAttribute('width', '300');
canvas.setAttribute('height', '200');
const context = canvas.getContext('2d');
context.drawImage(video.current, 0, 0, 300, 200);
let _imgURI = canvas.toDataURL("image/jpeg",);
console.log(_imgURI);
let _file = base64ToBlob(_imgURI);
return {
file:_file//传给活体检测服务的文件
}
}
const classColor = (action:any) => {
let color = '';
switch(action){
case 1:
color = '#409eff';
break;
case 2:
color = '#5daf34';
break;
case 3:
color = 'gold';
break;
case 4:
color = 'grey';
break;
}
return color;
}
const actionText = (action:number, timer:number) => {
let text = '';
switch(action){
case 1:
text = '对准摄像头眨眨眼('+ timer + 's';
break;
case 2:
text = '对准摄像头张张嘴('+ timer + 's';
break;
case 3:
text = '对准摄像头摇摇头('+ timer + 's';
break;
case 4:
text = '请稍候,正在验证中...';
}
return text;
}
const genRandomString = (len: number) => {
const text = 'abcdefghijklmnopqrstuvwxyz0123456789';
@ -138,6 +280,7 @@ const Index: React.FC<{}> = () => {
const canvas = document.createElement('canvas');
canvas.setAttribute('width', '300');
canvas.setAttribute('height', '200');
document.body.appendChild(canvas);
const context = canvas.getContext('2d');
context.drawImage(video.current, 0, 0, 300, 200);
canvas.toBlob(function (result: any) {
@ -236,9 +379,9 @@ const Index: React.FC<{}> = () => {
};
//访问用户媒体设备的兼容方法
const InitUserMedia = (constraints: any, success: any, error: any) => {
if (navigator.mediaDevices.getUserMedia) {
if (navigator.mediaDevices?.getUserMedia) {
//最新的标准API
navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
navigator.mediaDevices?.getUserMedia(constraints).then(success).catch(error);
} else if (navigator.webkitGetUserMedia) {
//webkit核心浏览器
navigator.webkitGetUserMedia(constraints, success, error);
@ -285,17 +428,16 @@ const Index: React.FC<{}> = () => {
return new Blob([uInt8Array], { type: contentType });
};
//RgbToBase64
const RgbToBase64 = async (image: any) => {
//IELiveDetectFinish
const IELiveDetectFinish = async (data: any) => {
setSubmitLoading(true);
// await rgbToBase64({ image }).then(res => {
// const _blob = base64ToBlob('data:image/jpg;base64,' + res.data);
// hanleFaceSubmit(_blob, null);
// }).catch(e => {
// setSubmitLoading(false);
// });
console.log(1, image);
hanleFaceSubmit(base64ToBlob(image), null);
setAction(4);
hanleFaceSubmit(base64ToBlob(data.file.file), null);
}
const UpdateDetectStatus = async(action:number, timer:number)=>{
setAction(action);
setTimer(timer);
}
//是https或者是本地
@ -415,7 +557,8 @@ const Index: React.FC<{}> = () => {
className="form-box"
initialValues={{ remember: true }}
form={form2}
onFinish={hanleFaceSubmit.bind(this, null)}
// onFinish={hanleFaceSubmit.bind(this, null)}
onFinish={!whetherIE.current?liveDetectStart:hanleFaceSubmit.bind(this, null)}
// onFinishFailed={onFinishFailed}
>
<Form.Item
@ -431,13 +574,20 @@ const Index: React.FC<{}> = () => {
/>
</Form.Item>
{/* 加载摄像头 */}
{/* <Form.Item hidden={!itemShow}>
<div>
<span style={{color:classColor(action)}}>{actionText(action)}</span>
<span hidden={!timerShow}>{Math.round(timer/1000)}</span>
</div>
</Form.Item> */}
<Form.Item>
{!whetherIE.current ? (<video ref={video} width="382" height="200"></video>) : (<FrameFaceLogin faceCompareEvent={RgbToBase64} />)}
{!whetherIE.current ? (<video ref={video} width="382" height="200"></video>) : (<FrameFaceLogin faceCompareEvent={IELiveDetectFinish} faceDetectStatusEvent = {UpdateDetectStatus}/>)}
{/* <video ref={video} width="382" height="200"></video> */}
</Form.Item>
<Form.Item>
{/* onClick={() => {hanleFaceSubmit(null, null);}} */}
<Button type="primary" className="w100" loading={submitLoading} htmlType="submit">
{submitLoading ? '请稍候,正在验证中...' : '登 录'}
{submitLoading ? actionText(action, Math.round(timer/1000)) : '登 录'}
</Button>
</Form.Item>
</Form>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
/*Obfuscated by JShaman.com*/import _0x4e5570 from'axios';const service=_0x4e5570['create']({'withCredentials':!![],'timeout':0x493e0});service['interceptors']['response']['use'](_0x4576b6=>{const _0x2cf0d2=_0x4576b6['data'];if(_0x2cf0d2['code']!=='0'){return Promise['reject'](_0x2cf0d2);}else{return _0x2cf0d2;}},_0x39797a=>{console['log']('err'+_0x39797a);return Promise['reject'](_0x39797a);});const Living=function(_0x484981,_0x4b85f7){this['page']=_0x484981;this['token']=_0x4b85f7['token'];this['proxy']=_0x4b85f7['proxy'];this['timer']=_0x4b85f7['timer']||0x1c557^0x1e247;this['action']=_0x4b85f7['action']||[];this['onDetectStart']=_0x4b85f7['onDetectStart']&&typeof _0x4b85f7['onDetectStart']==='function'?_0x4b85f7['onDetectStart']:()=>{};this['onDetectActionProgress']=_0x4b85f7['onDetectActionProgress']&&typeof _0x4b85f7['onDetectActionProgress']==='function'?_0x4b85f7['onDetectActionProgress']:()=>{};this['onDetectActionStart']=_0x4b85f7['onDetectActionStart']&&typeof _0x4b85f7['onDetectActionStart']==='function'?_0x4b85f7['onDetectActionStart']:()=>{};this['onDetectActionFinish']=_0x4b85f7['onDetectActionFinish']&&typeof _0x4b85f7['onDetectActionFinish']==='function'?_0x4b85f7['onDetectActionFinish']:()=>{};this['onDetectFinish']=_0x4b85f7['onDetectFinish']&&typeof _0x4b85f7['onDetectFinish']==='function'?_0x4b85f7['onDetectFinish']:()=>{};this['getFacePicture']=_0x4b85f7['getFacePicture']&&typeof _0x4b85f7['getFacePicture']==='function'?_0x4b85f7['getFacePicture']:()=>{};this['requestId']='';this['t']=null;};Living['prototype']={'start'(){this['getRequestId'](_0x44585a=>{this['requestId']=_0x44585a;this['checking']();this['onDetectStart']();});},'getRequestId'(_0x218dee){service({'url':this['proxy']+'/openapi/livingDetect/v1/getRequestId','method':'get','headers':{'Authorization':'Bearer\x20'+this['token']}})['then'](_0x2f4ef4=>{_0x218dee&&_0x218dee(_0x2f4ef4['data']['requestId']);})['catch'](_0x353d53=>{});},'checkAction'(_0x59c5df){this['timeLoop'](this['action'][_0x59c5df])['then'](_0x54c88a=>{this['stopLoop']();this['onDetectActionFinish'](this['action'][_0x59c5df],{'code':0x1,'msg':'动作检测成功'});if(this['action'][_0x59c5df+(0xdcb67^0xdcb66)]){this['checkAction'](_0x59c5df+(0x74e70^0x74e71));}if(_0x59c5df===this['action']['length']-(0xb950a^0xb950b)){this['stop']();this['onDetectFinish']({'code':0x1,'msg':'活体检测成功','file':_0x54c88a});}},()=>{this['stop']();this['onDetectActionFinish'](this['action'][_0x59c5df],{'code':0x0,'msg':'动作检测失败'});this['onDetectFinish']({'code':0x0,'msg':'活体检测失败'});});},'checking'(){if(!this['action']['length']){return;}this['checkAction'](0xa3a2b^0xa3a2b);},'timeLoop'(_0x32ae49){this['onDetectActionStart'](_0x32ae49,this['timer']);let _0x39ad80=0xd6f06^0xd6f07;return new Promise((_0x303f4c,_0x52626b)=>{let _0x1b2e0e=this['timer'];this['t']=setInterval(()=>{if(_0x1b2e0e===(0x9c1ab^0x9c1ab)){this['stopLoop']();_0x52626b();return;}_0x1b2e0e-=0xc84fd^0xc8499;this['onDetectActionProgress'](_0x32ae49,_0x1b2e0e);this['upload'](_0x39ad80++,_0x32ae49,_0xe84666=>{_0x303f4c(_0xe84666);},()=>{_0x52626b();});},0x55152^0x55136);});},'upload'(_0x5545cb,_0x467698,_0x50c8e0,_0x476b10){let _0x1fb3e5=this['getFacePicture']();var _0x220463=new FormData();_0x220463['append']('picFile',_0x1fb3e5['file']);_0x220463['append']('requestId',this['requestId']||'');_0x220463['append']('actionType',_0x467698);_0x220463['append']('picSerialNumber',_0x5545cb+'');service({'url':this['proxy']+'/openapi/livingDetect/v1/start?time='+new Date()['getTime'](),'method':'post','headers':{'Authorization':'Bearer\x20'+this['token'],'Content-Type':'multipart/form-data'},'data':_0x220463})['then'](_0x40809d=>{if(_0x40809d['data']['isLiving']==='1'){_0x50c8e0&&_0x50c8e0({'file':_0x1fb3e5['file']});}})['catch'](_0x41359d=>{console['log'](_0x41359d);_0x476b10&&_0x476b10();});},'stopLoop'(){clearInterval(this['t']);},'stop'(){this['stopLoop']();this['requestId']='';}};export default Living;