Face-Server 实现人脸登录,人脸追踪,眨眨眼,摇摇头,张张嘴

Face-Server 实现人脸登录,人脸追踪,眨眨眼,摇摇头,张张嘴

官方文档

目前已经开源,点击下方连接跳转
https://faceeffet.com/

效果预览

人脸登录效果



前言

Face-server 是一款独立的人脸识别服务,通过API实现快速人脸登录,无需改动现有代码。主打功能包括人脸验证、追踪、消失与出现检测及活体验证。一键启动本地服务器,支持Windows和Linux部署,体积小功能强大。完全开源免费,提供后台配置服务,便于个性化设置人脸登录参数。适合各类项目,简化人脸登录流程,提升用户体验和安全性。


提示:以下是本篇文章正文内容,下面案例可供参考

一、效果预览

人脸登录效果

二、下载地址

百度网盘链接:
点击跳转
提取码: 8888

CSDN链接:
点击跳转

三、启用步骤

1.先解压,然后看见这3个文件

linux则运行.sh
windows 则运行windows.cmd
在这里插入图片描述

2.运行前准备

windows 已管理员模式运行

在这里插入图片描述
linux 运行前 则给当前文件设置权限

chmod 777 Linux - Mac启动.sh

’ Linux - Mac启动.sh ’ 这是整个文件名称,中间带有空格,为了引起不必要的麻烦,可以把sh脚本名称改一下

运行

./Linux - Mac启动.sh

3.启动成功

在这里插入图片描述

如果在linux或者windows执行报错

则在控制台执行以下命令

java -javaagent:face.jar -jar face.jar

上面两个脚本只是为了杀死当前重复的端口号

四、页面展示

1.启动成功后

浏览器输入:http://localhost:8999/face-server

2.配置中心

后端回调地址,人脸签到才有效果,其他可以忽略…
在这里插入图片描述

3.样式设置

在这里插入图片描述

4.其他设置

在这里插入图片描述

5.API文档

在这里插入图片描述

五、接口请求编写

1.后端接口请求编写

这是你的后端编写接口,请求人脸登录地址

参数说明:

在这里插入图片描述

2.运行流程图

添加人脸

在这里插入图片描述

登录验证

在这里插入图片描述

1.登录验证地址请求

请仔细阅读流程图

下面这个请求人脸登录地址,是通用的,你在后端你得放开这个接口,不管是登录状态还是为登录状态,得放行下面接口

最开始的时候,我们得先添加一张人脸才能验证人脸

    @ApiOperation(value = "请求人脸登录地址")
    @GetMapping("request/face/address/{userId}/{type}")
    public R getFaceAddress(@PathVariable String userId,@PathVariable String type,@RequestParam(required = false) String custom) {
        HttpClient client = HttpClients.createDefault();
        HttpGet request = new HttpGet("http://localhost:8999/face-server/request/get/login/address?token="+token+"&userId="+userId+"&type="+type+"&custom="+custom);
        try {
            HttpResponse response = client.execute(request);
            String responseBody = EntityUtils.toString(response.getEntity());
            return R.ok().data("data",responseBody);
        } catch (Exception e) {
            e.printStackTrace();
            return R.error();
        }
    }

返回示例

{
    "msg": "成功",
    "code": 200,
    "data": "http://localhost:8999/face-server/face/login/D~XbYfwbEiVkAmw3NlOefhp4WuqgQS9bF!!ZKwMCowmrtA6HHkGIMHZyX9qoE8PsLmkOLUxsfdLzYJOMqzmQkQg==?custom=null"
}

  • 前端拿到这个地址进行跳转开始认证

2.删除人脸

    @ApiOperation(value = "根据用户唯一标识删除人脸")
    @GetMapping("/delete/face/{userId}")
    public R deleteFace(@PathVariable String userId) {
        HttpClient client = HttpClients.createDefault();
        HttpGet request = new HttpGet("http://localhost:8999/face-server/delete/face/"+token+"/"+userId);
        try {
            HttpResponse response = client.execute(request);
            String responseBody = EntityUtils.toString(response.getEntity());
            return R.ok();
        } catch (Exception e) {
            e.printStackTrace();
            return R.error();
        }
    }

3.回调接口

    @ApiOperation(value = "人脸的回调数据接口")
    @PostMapping("request/face/loopback/address") // 配置中心配置的  如:http://localhost:8222/api/admin/request/face/loopback/address
    public R loopbackAddress(@RequestBody String json) {
        log.info("回调数据============{}",json);
        userService.faceUser(json);
        return R.ok();
    }

4.前端验证调用流程

	// 请求后端人脸登录地址
    faceHandleLogin(){
      user.requestFaceAddress('0','vef').then(res=>{
        if (res.code === 20000){
          let faceData = JSON.parse(res.data.data)
          if (faceData && faceData.data){
           // 拿到地址打开,开始验证
            window.open(faceData.data,"_self")
          }
        }
      })
    },
    // 解密方法
    // data 返回的加密值
    // currentKey  你当前的token,face-server的token
    async decrypt(data, currentKey) {
      try {
        let result = decodeURIComponent(data);
        result = result.replaceAll("!!", "/");
        result = result.replaceAll("~", "+");
        const rawData = atob(result); // Base64解码
        const rawKey = new TextEncoder().encode(currentKey); // 密钥转换为Uint8Array
        const iv = new Uint8Array(16); // 初始化向量(IV),对于AES-CBC模式,通常是16个零字节
        const cryptoKey = await window.crypto.subtle.importKey(
          "raw",
          rawKey,
          { name: "AES-CBC", length: 256 },
          false,
          ["decrypt"]
        );
        const decrypted = await window.crypto.subtle.decrypt(
          {
            name: "AES-CBC",
            iv: iv,
          },
          cryptoKey,
          new Uint8Array(rawData.split('').map(char => char.charCodeAt(0))) // 将字符串转换为字节
        );
        return new TextDecoder().decode(decrypted);
      } catch (e) {
        console.error("解密失败:", e);
        return null;
      }
    },
    faceLogin(){
      let userId = this.getQueryParamValue('t')
      console.log("当前userId========",userId)
      if (userId){
      	// 根据账号查询,返回账号密码,然后解密
      	// 然后调用原有登录方法,实现一个伪登录
      	// 大多数框架是password是加密的,得更换加密规则
        user.requestFaceLogin({userId}).then(res=>{
          if (res && res.code === 20000 && res.data){
              this.decrypt(res.data.password,this.faceKey).then(resFace=>{
                this.loginForm.username  = res.data.username
                this.loginForm.password = resFace
                this.handleLogin()
            })
          }
        })
      }
    },
    // 拿到t的值,加密的值
    // 这个方法主要是拿到当前浏览器的url后面那个拼接值
    getQueryParamValue(key) {
      const fullUrl = window.location.href;
      let params = new URLSearchParams(window.location.hash.split('?')[1]);
      if (!params.has(key)) {
        const url = new URL(fullUrl);
        params = new URLSearchParams(url.search);
        if (!params.has(key) && url.hash.includes('?')) {
          const hashQuery = url.hash.substring(url.hash.indexOf('?') + 1);
          params = new URLSearchParams(hashQuery);
        }
      }
      return params.has(key) ? params.get(key) : null;
    },
	// 你自己的登录方法
	// 下面只是一个示例,具体替换为你自己的方法,
    handleLogin() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
         // debugger
          this.loading = true
          this.$store.dispatch('Login', this.loginForm).then(() => {
            this.loading = false
            this.$router.push('/dashboard')
          }).catch(() => {
            this.loading = false
          })
        } else {
          console.log('error submit!!')
          return false
        }
      })
    }
5.后端加密工具类

可以把默认框架加密那套规则调整如下,用当前工具类加密

CryptoRequestUtil.encrypt(“你的密码”,“face-server 的token”)
加密完成后存入到数据
前端使用那个decrypt进行解密

public class CryptoRequestUtil {

    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";

    /**
     * Encrypts the given data using AES/CBC/PKCS5Padding.
     *
     * @param data the data to encrypt
     * @param key the encryption key
     * @return the encrypted data encoded in Base64
     * @throws Exception if an encryption error occurs
     */
    public static String encrypt(String data, String key) throws Exception {
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM);
        IvParameterSpec iv = new IvParameterSpec(new byte[16]); // zero initialization vector for simplicity
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);
        byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encrypted).replace("/", "!!").replace("+", "~");
    }

    /**
     * Decrypts the given Base64-encoded data using AES/CBC/PKCS5Padding.
     *
     * @param data the data to decrypt
     * @param key the decryption key
     * @return the decrypted string
     * @throws Exception if a decryption error occurs
     */
    public static String decrypt(String data, String key) throws Exception {
        data = data.replace("!!", "/").replace("~", "+");
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM);
        IvParameterSpec iv = new IvParameterSpec(new byte[16]); // zero initialization vector for simplicity
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv);
        byte[] original = cipher.doFinal(Base64.getDecoder().decode(data));
        return new String(original, StandardCharsets.UTF_8);
    }

    /**
     * Decodes and decrypts URL-encoded and Base64-encoded data.
     *
     * @param data the data to decrypt
     * @param key the decryption key
     * @return the decrypted string or null if an error occurs
     */
    public static String decryptFromUrl(String data, String key) {
        try {
            data = data.replace("%20", "+").replace("+", "~");
            String decoded = URLDecoder.decode(data, StandardCharsets.UTF_8.name());
            return decrypt(decoded, key);
        } catch (Exception e) {
            return null;
        }
    }
}

总结

Face-server是一款独立的人脸识别服务,通过API实现快速人脸登录,无需改动现有代码。它具备人脸验证、实时追踪用户人脸位置、检测人脸出现和消失,以及通过眨眼、摇头、张嘴等动作进行活体验证等功能,防止照片和视频攻击,保证登录安全。Face-server支持一键启动本地服务器,适用于Windows和Linux系统,安装简单快捷。其体积小巧但功能强大,资源占用少,适合各类项目应用。完全开源免费,提供灵活的后台配置,便于个性化设置人脸登录参数。Face-server通过提供全面而便捷的人脸识别服务,极大地简化了人脸登录的实现过程,提高了用户体验和系统安全性。

支持H5,手机端,兼容各种浏览器在这里插入图片描述

报错解决

如果运行报错:

在这里插入图片描述
这是Java8 加密规则的一个缺陷,需要替换jdk里面的文件

找到jdk的位置

我的位置在这里,一般有jdk和jre文件

在这里插入图片描述

替换jdk下面相关文件

替换 local_policy.jar
替换 US_export_policy.jar

注意!!记得备份原有的,我是改了名 加了 ‘-a’

在这里插入图片描述

替换jre下面相关文件

jre跟jdk文件路径不同,

下面是我的Java jdk安装路径,默认选择你们的路径

jdk具体替换路径为:C:\Program Files\Java\jdk1.8.0_144\jre\lib\security

jre具体替换路径为:C:\Program Files\Java\jre1.8.0_144\lib\security
在这里插入图片描述

上面操作版本具体为 jdk1.8,版本之间可能存在微小差异,可能某些小伙伴路径并不是这样的,jdk下面只有一个文件夹,但是可以采用在当前jdk下面文件夹下面全局搜索, local_policy.jar,US_export_policy.jar 然后开始替换

文件下载

local_policy.jar,US_export_policy.jar
可以去oracel 官网下载

同时也可以去我的百度网盘下载

US_export_policy.jar 下载跳转

local_policy.jar 下载跳转

实现调用摄像头进行人脸识别,可以使用WebRTC技术,它是一种支持浏览器之间实时通信的开放标准。在这个过程中,需要使用到一些JavaScript库,如Face-api.js和TensorFlow.js等。 在使用Face-api.js进行人脸识别时,可以通过检测关键点的位置来判断眼、张嘴或摇头等动作。比如,检测眼时可以根据眼睛关键点的位置变化来计算眼睛的开合程度,当眼睛开合程度超过一定阈值时就可以判断为眼。 以下是一个简单的示例代码,用于检测眼动作: ```javascript const video = document.getElementById("video"); Promise.all([ faceapi.nets.tinyFaceDetector.loadFromUri("/models"), faceapi.nets.faceLandmark68Net.loadFromUri("/models"), faceapi.nets.faceRecognitionNet.loadFromUri("/models"), faceapi.nets.faceExpressionNet.loadFromUri("/models") ]).then(startVideo); function startVideo() { navigator.getUserMedia( { video: {} }, stream => video.srcObject = stream, err => console.error(err) ) } video.addEventListener('play', () => { const canvas = faceapi.createCanvasFromMedia(video); document.body.append(canvas); const displaySize = { width: video.width, height: video.height }; faceapi.matchDimensions(canvas, displaySize); setInterval(async () => { const detections = await faceapi.detectAllFaces(video, new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks().withFaceExpressions(); const resizedDetections = faceapi.resizeResults(detections, displaySize); canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height); faceapi.draw.drawDetections(canvas, resizedDetections); faceapi.draw.drawFaceLandmarks(canvas, resizedDetections); const leftEye = resizedDetections[0].landmarks.getLeftEye(); const rightEye = resizedDetections[0].landmarks.getRightEye(); const eyeAspectRatio = (faceapi.euclideanDistance(leftEye[1], leftEye[5]) + faceapi.euclideanDistance(leftEye[2], leftEye[4])) / (2 * faceapi.euclideanDistance(leftEye[0], leftEye[3])); if (eyeAspectRatio < 0.2) { console.log("Blink detected!"); } }, 100); }); ``` 在上述代码中,我们使用了Face-api.js库来进行人脸识别。首先,我们加载了必要的模型文件,然后通过调用`startVideo`函数来启动摄像头。在视频播放时,我们使用`detectAllFaces`函数来检测视频中出现的所有人脸,并计算出每个人脸的关键点。然后,我们根据左右眼的关键点位置计算出眼睛的开合程度,当开合程度小于一定阈值时,就可以判断为眼动作。 类似地,检测张嘴或摇头动作也可以使用类似的方法,通过计算关键点位置的变化来判断。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来自上海的这位朋友

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值