百度AI实现Web端人脸识别登陆-Springboot-Vue/element-ui(若依)

3.8 人脸识别登陆Controller

1、效果图

 后台获取的数据

{"face_token":"efb5996c1707850f81e884ef190c984b","user_list":[{"score":91.080505371094,"group_id":"group1","user_id":"1","user_info":""}]}

2、注册百度ai

百度ai地址https://login.bce.baidu.com/?redirect=https%3A%2F%2Fconsole.bce.baidu.com%2F%3Ffromai%3D1#/aip/overviewhttps://login.bce.baidu.com/?redirect=https%3A%2F%2Fconsole.bce.baidu.com%2F%3Ffromai%3D1#/aip/overview

2.1 、使用免费百度ai步骤

1、点击人脸识别:跳转到服务

 2、其间会有一些验证,创建应用

3、查看人脸库

 4、新建组、用户

 5、添加人脸照片

 3、代码实现逻辑

基于Springboot框架搭建的,前端使用Vue,通过摄像机拍下照片后,请求后端人脸识别登陆服务,后台调用百度API人脸识别接口,进入百度
大脑搜索人脸识别即可获取官网的Secret Key,将前端获取的人脸信息的base64信息和你本地数据库里的人脸信息传到百度人脸识别的接口
进行人脸比对,返回一个json数据,result参数 带别人脸相似度, result可自己定义,从而实现人脸识别登录。

核心代码块

3.1、导入Maven依赖

 <dependency>
     <groupId>com.baidu.aip</groupId>
     <artifactId>java-sdk</artifactId>
     <version>4.15.1</version>
 </dependency>

 3.2、 创建FaceUtil,获取ApiFace【单例】

package com.ruoyi.common.utils.face.utils;
import com.baidu.aip.face.AipFace;
import com.baidu.aip.util.Base64Util;
/**
 * 人脸识别工具
 */
public class FaceUtil {

    private static final String APP_ID = "25236971";
    private static final String APP_KEY = "zLIZGY15wiEQh2utjwAGGW9C";
    private static final String SECRET_KEY = "Pzi4Ew6eWfZPINBkshlCO22OqADa777I";

    private static volatile AipFace client = new AipFace(APP_ID, APP_KEY, SECRET_KEY);
    // 创建单例避免多次获取sdk
    public static AipFace getClient(){
        client.setConnectionTimeoutInMillis(2000);
        client.setSocketTimeoutInMillis(60000);
        return client;
    }

    /**
     * 编码
     * @param form
     * @return
     */
    public static String  encodeBase64(byte[] form){
        return Base64Util.encode(form);
    }

    /**
     * 解码
     * @param data
     * @return
     */
    public static byte[] decodeBase64(String data){
        return Base64Util.decode(data);
    }
}

3.3 创建FaceResultUtil,统一处理请求

package com.ruoyi.common.utils.face.utils;
import com.ruoyi.common.utils.face.BizException;
import com.ruoyi.common.utils.face.constant.ErrorEnum;
import com.ruoyi.common.utils.face.constant.FaceConstant;
import com.ruoyi.common.utils.face.dto.FaceResult;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 结果工具封装
 */
public class FaceResultUtil {
    private static final Logger logger = LoggerFactory.getLogger("百度API接口请求结果解析");

    public static FaceResult isSuccess(JSONObject res){
        FaceResult result = parseJsonObject(res);
        if (!result.isSuccess()){
            // 对错误进行分类
            ErrorEnum errorEnum = ErrorEnum.getInstance(result.getErrorCode());
            if (errorEnum == null){
                throw new BizException("百度接口请求失败" + result.getErrorMsg());
            }else {
                throw new BizException(errorEnum.getCnDesc());
            }
        }
        return result;
    }


    /**
     * 解析JsonObject
     * @return
     */
    private static FaceResult parseJsonObject(JSONObject res){
        FaceResult faceResult = FaceResult.builder().build();
        try {
            String logId = res.has(FaceConstant.LOG_ID) ? res.get(FaceConstant.LOG_ID).toString() : "0";
            int errorCode = res.has(FaceConstant.ERROR_CODE) ? res.getInt(FaceConstant.ERROR_CODE) : -1;
            String errorMsg = res.has(FaceConstant.ERROR_MSG) ? res.getString(FaceConstant.ERROR_MSG) : "";
            int cached = res.has(FaceConstant.CACHED) ? res.getInt(FaceConstant.CACHED) : 0;
            long timestamp = res.has(FaceConstant.TIMESTAMP) ? res.getLong(FaceConstant.TIMESTAMP) : 0;
            Object dataString = res.has(FaceConstant.RESULT) ? res.get(FaceConstant.RESULT) : "";
            com.alibaba.fastjson.JSONObject data = null;
            if (dataString != null) {
                data = com.alibaba.fastjson.JSONObject.parseObject(dataString.toString());
            }
            faceResult.setLogId(logId);
            faceResult.setErrorCode(errorCode);
            faceResult.setErrorMsg(errorMsg);
            faceResult.setCached(cached);
            faceResult.setTimestamp(timestamp);
            faceResult.setData(data);
        }catch (Exception e){
            logger.error("JSONObject解析失败", e);
        }
        return faceResult;
    }
}


3.4 创建FaceResult

package com.ruoyi.common.utils.face.dto;

import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 *请求百度API接口结果
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FaceResult implements Serializable {
    private String logId;

    private String errorMsg;

    private int cached;

    private int errorCode;

    private long timestamp;

    private JSONObject data;

    public boolean isSuccess(){
        return 0 == this.errorCode ? true : false;
    }
}


3.5 创建ImageU

package com.ruoyi.common.utils.face.dto;
import com.ruoyi.common.utils.face.constant.ImageTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * 图像对象
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ImageU implements Serializable {

    private ImageTypeEnum imageTypeEnum;

    private String data;
}


3.6 创建FaceUserDTO

package com.ruoyi.common.utils.face.dto;

import com.ruoyi.common.utils.face.constant.FaceConstant;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;


@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FaceUserDTO<T> implements Serializable {

    private String userId;


    private String groupId = FaceConstant.DEFAULT_GROUP_ID;

    private String faceToken;

    private T user;
}


3.7 创建FaceManage[关键类](重点)

package com.ruoyi.common.utils.face;

import com.alibaba.fastjson.JSON;
import com.baidu.aip.face.FaceVerifyRequest;
import com.baidu.aip.face.MatchRequest;

import com.ruoyi.common.utils.face.constant.ActionTypeEnum;
import com.ruoyi.common.utils.face.constant.FaceConstant;
import com.ruoyi.common.utils.face.constant.LivenessControlEnum;
import com.ruoyi.common.utils.face.constant.QualityControlEnum;
import com.ruoyi.common.utils.face.dto.FaceResult;
import com.ruoyi.common.utils.face.dto.FaceUserDTO;
import com.ruoyi.common.utils.face.dto.ImageU;
import com.ruoyi.common.utils.face.utils.FaceResultUtil;
import com.ruoyi.common.utils.face.utils.FaceUtil;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;

/**
 *  人脸识别相关服务
 */
public class FaceManage {
    private static final Logger logger = LoggerFactory.getLogger(FaceManage.class);

    /**
     * 人脸注册
     */
    public static void faceRegister(FaceUserDTO userDTO, ImageU imageU){
        // 传入可选参数调用接口
        HashMap<String, String> options = new HashMap<String, String>();
        // 用户资料
        options.put("user_info", JSON.toJSONString(userDTO));
        // 图片质量
        options.put("quality_control", QualityControlEnum.LOW.name());
        // 活体检测控制
        options.put("liveness_control", LivenessControlEnum.NONE.name());
        // 操作方式
        options.put("action_type", ActionTypeEnum.REPLACE.name());

        String image = imageU.getData();
        String imageType = imageU.getImageTypeEnum().name();
        String groupId = userDTO.getGroupId();
        String userId = userDTO.getUserId();

        // 人脸注册
        JSONObject res = FaceUtil.getClient().addUser(image, imageType, groupId, userId, options);
        FaceResultUtil.isSuccess(res);
        logger.info("人脸注册成功");
    }


    /**
     * 人脸更新
     */
    public static void faceUpdate(FaceUserDTO userDTO, ImageU imageU) {
        HashMap<String, String> options = new HashMap<String, String>();
        // 用户资料
        options.put("user_info", JSON.toJSONString(userDTO));
        // 图片质量
        options.put("quality_control", QualityControlEnum.LOW.name());
        // 活体检测控制
        options.put("liveness_control", LivenessControlEnum.NONE.name());
        // 操作方式
        options.put("action_type", ActionTypeEnum.REPLACE.name());

        String image = imageU.getData();
        String imageType = imageU.getImageTypeEnum().name();
        String groupId = userDTO.getGroupId();
        String userId = userDTO.getUserId();

        // 人脸更新
        JSONObject res = FaceUtil.getClient().updateUser(image, imageType, groupId, userId, options);
        FaceResultUtil.isSuccess(res);
        logger.info("人脸更新成功 {}", res.toString(2));
    }


    /**
     * 人脸删除
     */
    public static void faceDelete(String userId, String groupId, String faceToken) {
        // 传入可选参数调用接口
        HashMap<String, String> options = new HashMap<String, String>();
        // 人脸删除
        JSONObject res = FaceUtil.getClient().faceDelete(userId, groupId, faceToken, options);
        FaceResultUtil.isSuccess(res);
        logger.info("人脸删除成功 {}", res.toString(2));
    }


    /**
     * 用户信息查询
     */
    public static FaceUserDTO<String> findUser(String userId, String groupId) {
        HashMap<String, String> options = new HashMap<>();
        // 用户信息查询
        JSONObject res  = FaceUtil.getClient().getUser(userId, groupId, options);
        FaceResult result = FaceResultUtil.isSuccess(res);
        return JSON.parseObject(result.getData().toJSONString(), FaceUserDTO.class);
    }


    /**
     * 获取用户人脸列表
     * @throws Exception
     */
    public static FaceResult faceGetList(String userId, String groupId){
        HashMap<String, String> options = new HashMap<String, String>();
        // 获取用户人脸列表
        JSONObject res = FaceUtil.getClient().faceGetlist(userId, groupId, options);
        return FaceResultUtil.isSuccess(res);
    }


    /**
     * 获取用户列表
     */
    public static FaceResult listUserByGroupId(String groupId) {
        HashMap<String, String> options = new HashMap<String, String>();
        options.put("start", "0");
        options.put("length", "50");
        // 获取用户列表
        JSONObject res = FaceUtil.getClient().getGroupUsers(groupId, options);
        return FaceResultUtil.isSuccess(res);
    }


    /**
     * 删除用户
     */
    public static void deleteUser(String userId, String groupId) {
        HashMap<String, String> options = new HashMap<String, String>();
        // 删除用户
        JSONObject res = FaceUtil.getClient().deleteUser(groupId, userId, options);
        FaceResultUtil.isSuccess(res);
        logger.info("用户删除成功 {}", res.toString(2));
    }


    /**
     * 创建用户组
     */
    public static void addGroup(String groupId) {
        HashMap<String, String> options = new HashMap<String, String>();
        // 创建用户组
        JSONObject res = FaceUtil.getClient().groupAdd(groupId, options);
        FaceResultUtil.isSuccess(res);
        logger.info("创建用户组 {}", res.toString(2));
    }


    /**
     * 删除用户组
     */
    public static void deleteGroup(String groupId){
        HashMap<String, String> options = new HashMap<String, String>();
        // 删除用户组
        JSONObject res = FaceUtil.getClient().groupDelete(groupId, options);
        FaceResultUtil.isSuccess(res);
        logger.info("删除用户组 {}", res.toString(2));
    }

    /**
     * 组列表查询
     */
    public static FaceResult listGroup() {
        HashMap<String, String> options = new HashMap<String, String>();
        options.put("start", "0");
        options.put("length", "50");
        // 组列表查询
        JSONObject res = FaceUtil.getClient().getGroupList(options);
        return FaceResultUtil.isSuccess(res);
    }


    /**
     * 身份验证(没权限使用)
     */
    public static FaceResult personVerify(String idCardNumber, String realName, ImageU imageU){
        HashMap<String, String> options = new HashMap<String, String>();
        options.put("quality_control", QualityControlEnum.LOW.name());
        options.put("liveness_control", LivenessControlEnum.NONE.name());
        // 身份验证
        JSONObject res = FaceUtil.getClient().personVerify(imageU.getData(), imageU.getImageTypeEnum().name(), idCardNumber, realName, options);
        return FaceResultUtil.isSuccess(res);
    }

    /**
     * 人脸对比
     */
    public static int faceMatchScore(ImageU imageU1, ImageU imageU2){
        MatchRequest req1 = new MatchRequest(imageU1.getData(), imageU1.getImageTypeEnum().name());
        MatchRequest req2 = new MatchRequest(imageU2.getData(), imageU2.getImageTypeEnum().name());
        ArrayList<MatchRequest> requests = new ArrayList<MatchRequest>();
        requests.add(req1);
        requests.add(req2);
        JSONObject res = FaceUtil.getClient().match(requests);
        FaceResult result = FaceResultUtil.isSuccess(res);
        // 对结果进行特殊处理
        Integer score = result.getData().getInteger(FaceConstant.SCORE);
        return score == null ? 0 : score;
    }


    /**
     * 人脸是否对比成功
     * @param imageU1
     * @param imageU2
     * @param score   匹配分数
     * @return
     */
    public static boolean isfaceMatch(ImageU imageU1, ImageU imageU2, Integer score){
        int defaultScore = FaceConstant.MATCH_SCORE;
        if (Objects.nonNull(score)){
            defaultScore = score;
        }
        return faceMatchScore(imageU1, imageU2) > defaultScore ? true : false;
    }

    /**
     * 人脸检测
     */
    public static FaceResult faceDetect(ImageU imageU) {
        HashMap<String, String> options = new HashMap<String, String>();
        options.put("face_field", "age");
        options.put("max_face_num", "2");
        options.put("face_type", "LIVE");
        // 人脸检测
        JSONObject res = FaceUtil.getClient().detect(imageU.getData(), imageU.getImageTypeEnum().name(), options);
        return FaceResultUtil.isSuccess(res);
    }


    /**
     * 人脸搜索
     */
    public static FaceResult faceSearch(String groupIds, ImageU imageU) {
        HashMap<String, String> options = new HashMap<String, String>();
        options.put("max_face_num", "1");
        options.put("max_user_num", "1");
        options.put("quality_control", QualityControlEnum.LOW.name());
        options.put("liveness_control", LivenessControlEnum.NONE.name());
        // 人脸搜索
        JSONObject res = FaceUtil.getClient().search(imageU.getData(), imageU.getImageTypeEnum().name(), groupIds, options);
        return FaceResultUtil.isSuccess(res);
    }

    /**
     * 活体检测
     */
    public static FaceResult faceverify(ImageU imageU) {
        FaceVerifyRequest req = new FaceVerifyRequest(imageU.getData(), imageU.getImageTypeEnum().name());
        ArrayList<FaceVerifyRequest> list = new ArrayList<FaceVerifyRequest>();
        list.add(req);
        JSONObject res = FaceUtil.getClient().faceverify(list);
        return FaceResultUtil.isSuccess(res);
    }
}


3.8、测试

package com.ruoyi.common.utils.face;

import com.alibaba.fastjson.JSON;
import com.example.common.face.FaceManage;
import com.example.common.face.constant.FaceConstant;
import com.example.common.face.constant.ImageTypeEnum;
import com.example.common.face.dto.FaceResult;
import com.example.common.face.dto.FaceUserDTO;
import com.example.common.face.dto.ImageU;
import com.example.common.face.utils.FaceUtil;
import com.example.common.utils.FilesUtil;
import org.apache.commons.io.IOUtils;
import org.junit.Test;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;

/**
 * User: lanxinghua
 * Date: 2019/8/29 20:17
 * Desc: 人脸识别测试
 */
public class FaceIdentificationTest {
    /**
     * 人脸注册,导入数据100张人脸图片
     */
    @Test
    public void test00() throws Exception{
        FaceUserDTO<String> userDTO = new FaceUserDTO<>();
        userDTO.setGroupId("group2");
        String filePath = "/Users/cxx/Downloads/entryPhoto/";
        File[] files = FilesUtil.listFile(filePath);
        int j = 0;
        for (File file : files) {
            int id = 7000 + j;
            j++;
            userDTO.setUserId(String.valueOf(id));
            InputStream is = new FileInputStream(new File(filePath + file.getName()));
            byte[] bytes = IOUtils.toByteArray(is);
            String data = FaceUtil.encodeBase64(bytes);
            ImageU imageU = ImageU.builder().data(data).imageTypeEnum(ImageTypeEnum.BASE64).build();
            userDTO.setUser("用户信息 group1 - " + id);
            try {
                FaceManage.faceRegister(userDTO, imageU);
            }catch (Exception e){
                System.out.println("注册失败 msg:" + e.getMessage());
                continue;
            }

        }
    }

    /**
     * 人脸注册
     */
    @Test
    public void test01() {
        FaceUserDTO<String> userDTO = new FaceUserDTO<>();
        userDTO.setGroupId("group1");
        userDTO.setUserId("6031");
        String image = "https://download.2dfire.com/mis/permanent/img2.jpg";
        ImageU imageU = ImageU.builder().data(image).imageTypeEnum(ImageTypeEnum.URL).build();
        userDTO.setUser("用户信息1");
        FaceManage.faceRegister(userDTO, imageU);
    }


    /**
     * 人脸更新
     */
    @Test
    public void test02() {
        FaceUserDTO<String> userDTO = new FaceUserDTO();
        userDTO.setGroupId("group1");
        userDTO.setUserId("6031");
        String image = "https://download.2dfire.com/mis/permanent/img2.jpg";
        ImageU imageU = ImageU.builder().data(image).imageTypeEnum(ImageTypeEnum.URL).build();
        userDTO.setUser("用户信息1");
        // 人脸更新
        FaceManage.faceUpdate(userDTO, imageU);
    }


    /**
     * 人脸删除接口
     */
    @Test
    public void test03() {
        String userId = "6030";
        String groupId = "group1";
        String faceToken = "5a1a8c17c40ea41264e8830017134972";
        FaceManage.faceDelete(userId, groupId, faceToken);
    }


    /**
     * 用户信息查询
     */
    @Test
    public void test04() {
        HashMap<String, String> options = new HashMap<>();
        String userId = "6030";
        String groupId = "group1";
        // 用户信息查询
        FaceUserDTO<String> userDTO = FaceManage.findUser(userId, groupId);
        System.out.println("用户信息:" + JSON.toJSONString(userDTO));
    }


    /**
     * 获取用户人脸列表
     */
    @Test
    public void test05() {
        String userId = "6030";
        String groupId = "group1";
        // 获取用户人脸列表
        FaceResult result = FaceManage.faceGetList(userId, groupId);
        String data = result.getData().getString(FaceConstant.FACE_LIST);
        System.out.println("人脸列表"+data);
    }


    /**
     * 获取用户列表
     */
    @Test
    public void test06() {
        String groupId = "group1";
        FaceResult result = FaceManage.listUserByGroupId(groupId);
        // 获取用户列表
        String userIds = result.getData().getString(FaceConstant.USER_ID_LIST);
        System.out.println("userIds" + userIds);
    }


    /**
     * 删除用户
     */
    @Test
    public void test07() {
        HashMap<String, String> options = new HashMap<String, String>();
        String groupId = "group1";
        String userId = "6031";
        // 删除用户
        FaceManage.deleteUser(userId, groupId);
    }


    /**
     * 创建用户组
     */
    @Test
    public void test08() {
        String groupId = "group2";
        FaceManage.addGroup(groupId);
    }


    /**
     * 删除用户组
     */
    @Test
    public void test09() {
        String groupId = "group2";
        FaceManage.deleteGroup(groupId);
    }


    /**
     * 组列表查询
     */
    @Test
    public void test10() {
        FaceResult result = FaceManage.listGroup();
        String groupIds = result.getData().getString(FaceConstant.GROUP_ID_LIST);
        System.out.println(groupIds);
    }


    /**
     * 身份验证(没权限使用)
     */
    @Test
    public void test11() {
        String image = "https://download.2dfire.com/mis/permanent/img1.jpg";
        ImageU imageU = ImageU.builder().data(image).imageTypeEnum(ImageTypeEnum.URL).build();
        String idCardNumber = "235151251";
        String name = "陈星星";
        FaceManage.personVerify(idCardNumber, name, imageU);
    }


    /**
     * 人脸对比
     */
    @Test
    public void test12() {
        String image1 = "https://download.2dfire.com/mis/permanent/img1.jpg";
        String image2 = "https://download.2dfire.com/mis/permanent/img2.jpg";
        ImageU imageU1 = ImageU.builder().data(image1).imageTypeEnum(ImageTypeEnum.URL).build();
        ImageU imageU2 = ImageU.builder().data(image2).imageTypeEnum(ImageTypeEnum.URL).build();

        boolean match = FaceManage.isfaceMatch(imageU2, imageU1, 80);
        int matchScore = FaceManage.faceMatchScore(imageU2, imageU1);
        System.out.println("是否匹配:" + match);
        System.out.println("匹配等分:" + matchScore);
    }

    /**
     * 人脸检测
     */
    @Test
    public void test13() {
        String image = "https://download.2dfire.com/mis/permanent/img1.jpg";
        ImageU imageU = ImageU.builder().data(image).imageTypeEnum(ImageTypeEnum.URL).build();
        FaceResult result = FaceManage.faceDetect(imageU);
        String data = result.getData().getString(FaceConstant.FACE_LIST);
        System.out.println(data);
    }


    /**
     * 人脸搜索
     */
    @Test
    public void test14() {
        String image = "https://download.2dfire.com/mis/permanent/img1.jpg";
        ImageU imageU = ImageU.builder().data(image).imageTypeEnum(ImageTypeEnum.URL).build();
        String groupIds = "group1,group2";
        FaceResult result = FaceManage.faceSearch(groupIds, imageU);
        String users = result.getData().getString(FaceConstant.USER_LIST);
        System.out.println(users);
    }

    /**
     * 活体检测
     */
    @Test
    public void test15() {
        String image = "https://download.2dfire.com/mis/permanent/img1.jpg";
        ImageU imageU = ImageU.builder().data(image).imageTypeEnum(ImageTypeEnum.URL).build();
        FaceResult result = FaceManage.faceverify(imageU);
        String users = result.getData().toJSONString();
        System.out.println(users);
    }
}

3.9人脸识别登陆Controller

  /**
     * 人脸登录
     */
    @PostMapping( "/facelogin")
    @ResponseBody
    public AjaxResult facelogin(@RequestParam("file") String file,@RequestParam("groupId") String groupId) throws Exception {
        /**
         * base64转为multipartFile
         */
        MultipartFile multipartFile = Base64DecodeMultipartFile.base64Convert(file);//很长
        if (multipartFile.isEmpty()) {
            throw new BizException("上传文件不能为空");
        }
        String groupIds = "group1";
        String data = FaceUtil.encodeBase64(multipartFile.getBytes());
        ImageU imageU = ImageU.builder().data(data).imageTypeEnum(ImageTypeEnum.BASE64).build();
        FaceResult result = FaceManage.faceSearch(groupIds, imageU);

        String users = result.getData().getString(FaceConstant.USER_LIST);
        if (StringUtils.isEmpty(users)){
            return AjaxResult.error("用户不存在");
        }
        JSONArray array = JSONObject.parseArray(users);
        JSONObject object = JSONObject.parseObject(array.get(0).toString());
        Integer score = object.getInteger(FaceConstant.SCORE);
        if (score == null){
            return AjaxResult.error("登录失败");
        }
        if (score >= FaceConstant.MATCH_SCORE){
            System.out.println(result.getData().toString());
            Long user_id = object.getLong("user_id");
            SysUser sysUser = sysUserService.selectUserById(user_id);
            /* 认证用户方法,可以自己写  */
            AjaxResult ajax = loginService.login(0,sysUser.getUserName());
            return ajax;
        }
        return AjaxResult.error("用户不存在");
    }

3.10 枚举,用到的一些辅助类

package com.ruoyi.common.utils.face;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;
import java.io.*;
import java.util.UUID;

/**
 * base64转为multipartFile工具类
 * base64Convert
 */

public class Base64DecodeMultipartFile implements MultipartFile {
    private final byte[] imgContent;
    private final String header;

    public Base64DecodeMultipartFile(byte[] imgContent, String header) {
        this.imgContent = imgContent;
        this.header = header.split(";")[0];
    }

    @Override
    public String getName() {
        return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1];
    }

    @Override
    public String getOriginalFilename() {
        return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1];
    }

    @Override
    public String getContentType() {
        return header.split(":")[1];
    }

    @Override
    public boolean isEmpty() {
        return imgContent == null || imgContent.length == 0;
    }

    @Override
    public long getSize() {
        return imgContent.length;
    }

    @Override
    public byte[] getBytes() throws IOException {
        return imgContent;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return new ByteArrayInputStream(imgContent);
    }

    @Override
    public void transferTo(File dest) throws IOException, IllegalStateException {
        new FileOutputStream(dest).write(imgContent);
    }

    /**
     * base64转multipartFile
     *
     * @param base64
     * @return
     */
    public static MultipartFile base64Convert(String base64) {
        String[] baseStrs = base64.split(",");

        BASE64Decoder decoder = new BASE64Decoder();
        byte[] b = new byte[0];
        try {
            b = decoder.decodeBuffer(baseStrs[1]);
        } catch (IOException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < b.length; ++i) {
            if (b[i] < 0) {
                b[i] += 256;
            }
        }
        return new Base64DecodeMultipartFile(b, baseStrs[0]);
    }
}
package com.ruoyi.common.utils.face;
/**
 * 自定义异常
 *
 */
public class BizException extends RuntimeException {
    private static final long serialVersionUID = 1L;

    private String msg;
    private int code = 500;

    public BizException(String msg) {
        super(msg);
        this.msg = msg;
    }

    public BizException(String msg, Throwable e) {
        super(msg, e);
        this.msg = msg;
    }

    public BizException(String msg, int code) {
        super(msg);
        this.msg = msg;
        this.code = code;
    }

    public BizException(String msg, int code, Throwable e) {
        super(msg, e);
        this.msg = msg;
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }


}

package com.ruoyi.common.utils.face.constant;

/**
 * 操作方式
 */
public enum ActionTypeEnum {
    APPEND("重复注册"),
    REPLACE("会用新图替换");

    ActionTypeEnum(String desc){
        this.desc = desc;
    }

    private String desc;

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}
package com.ruoyi.common.utils.face.constant;

/**
 *  百度接口错误码,还需要加CODE看官方文档
 */
public enum ErrorEnum {

    ERROR_ENUM_1(1, "Unknown error", "服务器内部错误,请再次请求"),
    ERROR_ENUM_13(13, "Get service token failed", "获取token失败"),
    ERROR_ENUM_222202(222202, "pic not has face", "图片中没有人脸"),
    ERROR_ENUM_222203(222203, "image check fail", "无法解析人脸"),
    ERROR_ENUM_222207(222207, "match user is not found", "未找到匹配的用户"),
    ERROR_ENUM_222209(222209, "face token not exist", "face token不存在"),
    ERROR_ENUM_222301(222301, "get face fail", "获取人脸图片失败"),
    ERROR_ENUM_223102(223102, "user is already exist", "该用户已存在"),
    ERROR_ENUM_223106(223106, "face is not exist", "该人脸不存在"),
    ERROR_ENUM_223113(223113, "face is covered", "人脸模糊"),
    ERROR_ENUM_223114(223114, "face is fuzzy", "人脸模糊"),
    ERROR_ENUM_223115(223115, "face light is not good", "人脸光照不好"),
    ERROR_ENUM_223116(223116, "incomplete face", "人脸不完整");

    ErrorEnum(int errorCode, String desc, String cnDesc){
        this.errorCode = errorCode;
        this.desc = desc;
        this.cnDesc = cnDesc;
    }

    private int errorCode;

    private String desc;

    private String cnDesc;


    public static ErrorEnum getInstance(int errorCode){
        for (ErrorEnum value : ErrorEnum.values()) {
            if (value.errorCode == errorCode){
                return value;
            }
        }
        return null;
    }

    public int getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public String getCnDesc() {
        return cnDesc;
    }

    public void setCnDesc(String cnDesc) {
        this.cnDesc = cnDesc;
    }
}
package com.ruoyi.common.utils.face.constant;

public class FaceConstant {
    /**
     * 默认组别
     */
    public static final String DEFAULT_GROUP_ID = "60030";

    /**
     * 匹配分数
     */
    public static final int MATCH_SCORE = 80;


    public static final String RESULT = "result";
    public static final String LOG_ID = "log_id";
    public static final String ERROR_MSG = "error_msg";
    public static final String CACHED = "cached";
    public static final String ERROR_CODE = "error_code";
    public static final String TIMESTAMP = "timestamp";

    public static final String SCORE = "score";
    public static final String FACE_LIST = "face_list";
    public static final String FACE_TOKEN = "face_token";
    public static final String USER_ID_LIST = "user_id_list";
    public static final String GROUP_ID_LIST = "group_id_list";
    public static final String USER_LIST = "user_list";
}

package com.ruoyi.common.utils.face.constant;
/**
 * 图片类型
 */
public enum  ImageTypeEnum {
    BASE64("BASE64", 2),
    URL("URL", 0),
    FACE_TOKEN("FACE_TOKEN", 0);

    ImageTypeEnum(String key, int size){
        this.key = key;
        this.size = size;
    }

    /**
     * key
     */
    private String key;

    /**
     * 大小 单位:M
     */
    private int size;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }
}
package com.ruoyi.common.utils.face.constant;

/**
 *  活体检测控制
 */
public enum  LivenessControlEnum {
    NONE("不进行控制"),
    LOW("较低的活体要求(高通过率 低攻击拒绝率)"),
    NORMAL("一般的活体要求(平衡的攻击拒绝率, 通过率)"),
    HIGH("较高的活体要求");

    LivenessControlEnum(String desc){
        this.desc = desc;
    }

    private String desc;

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}
package com.ruoyi.common.utils.face.constant;

/**
 *  图片质量控制
 */
public enum QualityControlEnum {
    NONE("不进行控制"),
    LOW("较低的质量要求"),
    NORMAL("一般的质量要求"),
    HIGH("较高的质量要求");

    QualityControlEnum(String desc){
        this.desc = desc;
    }

    private String desc;

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}

4、前端实现

<template>
  <div class="login">
    <el-form
      ref="loginForm"
      :model="loginForm"
      :rules="loginRules"
      class="login-form"
    >
      <h3 class="title">管理系统</h3>
      <el-tabs v-model="activeName" type="card" @tab-click="handleClick">
        <el-tab-pane label="账户登录" name="first">
          <el-form-item prop="username">
            <el-input
              v-model="loginForm.username"
              type="text"
              auto-complete="off"
              placeholder="账号"
            >
              <svg-icon
                slot="prefix"
                icon-class="user"
                class="el-input__icon input-icon"
              />
            </el-input>
          </el-form-item>
          <el-form-item prop="password">
            <el-input
              v-model="loginForm.password"
              type="password"
              auto-complete="off"
              placeholder="密码"
              @keyup.enter.native="handleLogin"
            >
              <svg-icon
                slot="prefix"
                icon-class="password"
                class="el-input__icon input-icon"
              />
            </el-input>
          </el-form-item>
          <el-form-item prop="code" v-if="captchaOnOff">
            <el-input
              v-model="loginForm.code"
              auto-complete="off"
              placeholder="验证码"
              style="width: 63%"
              @keyup.enter.native="handleLogin"
            >
              <svg-icon
                slot="prefix"
                icon-class="validCode"
                class="el-input__icon input-icon"
              />
            </el-input>
            <div class="login-code">
              <img :src="codeUrl" @click="getCode" class="login-code-img" />
            </div>
          </el-form-item>
          <el-checkbox
            v-model="loginForm.rememberMe"
            style="margin: 0px 0px 25px 0px"
            >记住密码</el-checkbox
          >
          <el-form-item style="width: 100%">
            <el-button
              :loading="loading"
              size="medium"
              type="primary"
              style="width: 100%"
              @click.native.prevent="handleLogin"
            >
              <span v-if="!loading">登 录</span>
              <span v-else>登 录 中...</span>
            </el-button>
            <div style="float: right" v-if="register">
              <router-link class="link-type" :to="'/register'"
                >立即注册</router-link
              >
            </div>
          </el-form-item>
        </el-tab-pane>
        <el-tab-pane label="人脸识别" name="second">
          <div class="testTrackingWrapper">
            <video
              id="video"
              width="340"
              height="300"
              preload
              autoplay
              loop
              muted
            ></video>
            <canvas id="canvas" width="550" height="400"></canvas>
            <div class="buttonWrapper">
              <button type="button" @click="submitPhoto">上传</button>
              <button type="button" name="button" @click="openCamera">
                拍照
              </button>
            </div>
          </div>
        </el-tab-pane>
      </el-tabs>
    </el-form>
    <!--  底部  -->
    <div class="el-login-footer">
      <span>Copyright © 2018-2021 nexauto All Rights Reserved.</span>
    </div>
    <img :src="image" />
  </div>
</template>

<script>
import { getCodeImg } from "@/api/login";
import Cookies from "js-cookie";
import { encrypt, decrypt } from "@/utils/jsencrypt";
require("../assets/js/tracking-min");
require("../assets/js/face-min.js");
require("../assets/js/dat.gui.min.js");
require("../assets/js/stats.min");
export default {
  name: "Login",
  data() {
    return {
      // open: false, //控制摄像头开关
      // video: null,
      image: "",
      activeName: "first",
      codeUrl: "",
      cookiePassword: "",
      loginForm: {
        username: "admin",
        password: "admin123",
        rememberMe: false,
        code: "",
        uuid: "",
      },
      loginRules: {
        username: [
          { required: true, trigger: "blur", message: "请输入您的账号" },
        ],
        password: [
          { required: true, trigger: "blur", message: "请输入您的密码" },
        ],
        code: [{ required: true, trigger: "change", message: "请输入验证码" }],
      },
      loading: false,
      // 验证码开关
      captchaOnOff: true,
      // 注册开关
      register: false,
      redirect: undefined,
    };
  },
  watch: {
    $route: {
      handler: function (route) {
        this.redirect = route.query && route.query.redirect;
      },
      immediate: true,
    },
  },
  created() {
    this.getCode();
    this.getCookie();
  },

  methods: {
    //选项卡点击事件
    handleClick(tab, event) {
      console.log(tab, event);
      if (tab.name == "first") {
        this.stopNavigator();
      } else {
        this.openCamera();
      }
    },
    getCode() {
      getCodeImg().then((res) => {
        this.captchaOnOff =
          res.captchaOnOff === undefined ? true : res.captchaOnOff;
        if (this.captchaOnOff) {
          this.codeUrl = "data:image/gif;base64," + res.img;
          this.loginForm.uuid = res.uuid;
        }
      });
    },
    getCookie() {
      const username = Cookies.get("username");
      const password = Cookies.get("password");
      const rememberMe = Cookies.get("rememberMe");
      this.loginForm = {
        username: username === undefined ? this.loginForm.username : username,
        password:
          password === undefined ? this.loginForm.password : decrypt(password),
        rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
      };
    },
    handleLogin() {
      this.$refs.loginForm.validate((valid) => {
        if (valid) {
          this.loading = true;
          if (this.loginForm.rememberMe) {
            Cookies.set("username", this.loginForm.username, { expires: 30 });
            Cookies.set("password", encrypt(this.loginForm.password), {
              expires: 30,
            });
            Cookies.set("rememberMe", this.loginForm.rememberMe, {
              expires: 30,
            });
          } else {
            Cookies.remove("username");
            Cookies.remove("password");
            Cookies.remove("rememberMe");
          }
          this.$store
            .dispatch("Login", this.loginForm)
            .then(() => {
              this.$router.push({ path: this.redirect || "/" }).catch(() => {});
            })
            .catch(() => {
              this.loading = false;
              if (this.captchaOnOff) {
                this.getCode();
              }
            });
        }
      });
    },
    //打开摄像头
    openCamera() {
      var _this = this;
      var video = document.getElementById("video");
      var canvas = document.getElementById("canvas");
      var context = canvas.getContext("2d");

      var tracker = new tracking.ObjectTracker("face");
      tracker.setInitialScale(4);
      tracker.setStepSize(2);
      tracker.setEdgesDensity(0.1);

      this.trackerTask = tracking.track("#video", tracker, { camera: true });

      tracker.on("track", function (event) {
        context.clearRect(0, 0, canvas.width, canvas.height);

        event.data.forEach(function (rect) {
          context.font = "11px Helvetica";
          context.fillText("已识别到人脸,请点击拍照", 100, 40);
          context.strokeStyle = "#a64ceb";
          context.strokeRect(rect.x, rect.y, rect.width, rect.height);
        });
      });
    },

    //提交
    submitPhoto() {
     
      let _this = this;
      let canvas = document.getElementById("canvas");
      let context = canvas.getContext("2d");
      let video = document.getElementById("video");
      context.drawImage(video, 0, 0, 500, 400);
     let formData=new FormData();
      let base64File=canvas.toDataURL(); 
      formData.append("file",base64File);
      formData.append("groupId","group1");
          this.$store
            .dispatch("Facelogin", formData)
            .then(() => {
              this.stopNavigator();
              this.$router.push({ path: this.redirect || "/" }).catch(() => {});
            })
            .catch(() => {
              this.loading = false;
              this.getCode();
            });
         
    },
    //关闭摄像头
    stopNavigator() {
      if (video && video !== null) {
        video.srcObject.getTracks()[0].stop();
        // this.open = true; //切换成打开摄像头
      }
    },
  },
  beforeDestroy() {
    this.stopNavigator();
  },
};
</script>

<style rel="stylesheet/scss" lang="scss">
.login {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  background-image: url("../assets/images/login-background.jpg");
  background-size: cover;
}
.title {
  margin: 0px auto 30px auto;
  text-align: center;
  color: #707070;
}

.login-form {
  border-radius: 6px;
  background: #ffffff;
  width: 400px;
  padding: 25px 25px 5px 25px;
  .el-input {
    height: 38px;
    input {
      height: 38px;
    }
  }
  .input-icon {
    height: 39px;
    width: 14px;
    margin-left: 2px;
  }
}
.login-tip {
  font-size: 13px;
  text-align: center;
  color: #bfbfbf;
}
.login-code {
  width: 33%;
  height: 38px;
  float: right;
  img {
    cursor: pointer;
    vertical-align: middle;
  }
}
.el-login-footer {
  height: 40px;
  line-height: 40px;
  position: fixed;
  bottom: 0;
  width: 100%;
  text-align: center;
  color: #fff;
  font-family: Arial;
  font-size: 12px;
  letter-spacing: 1px;
}
.login-code-img {
  height: 38px;
}

.testTrackingWrapper {
  height: 300px;
  width: 200px;
  position: relative;
}
video,
canvas {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
}
.buttonWrapper {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 7%;
}
</style>

写的不好的地方,可以纠正,如出问题,鄙人可以交流学习:

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码

)">
< <上一篇
下一篇>>