如何实现刷脸登录
本文主要采用百度AI平台的人脸识别技术实现,原理比较简单,注册面部到人脸库,之后拍照获取照片,在人脸库中查找相似的图片,如果相似度超过设定的阈值,则认为存在这个用户,这里主要介绍下如何获取access_token,并建立自己的人脸库。并且对自己的人脸库进行管理以及登录功能。
一、首先注册百度云账号
二、注册完之后登录找到 “>” ->人工智能->人脸识别
三、我们来创建自己的第一个人脸库应用,点击上图的创建应用
填写上基本信息,人脸识别默认会全选,创建完成之后,点击查看应用详情
需要注意的一点:AppID,API Key,Secret Key是我们需要用到的几个。可以先复制出来
三、练习
在实现刷脸登录时,首先我们来练习如何管理刚刚创建的人脸库
// An highlighted block
< dependency>
<groupId>com. baidu. aip</ groupId>
<artifactId>java-sdk</ artifactId>
<version>4.8. 0</ version>
</ dependency>
1、人脸注册
首先在Test里面创建一个类FaceTest
以上红框的地方就是之前自己创建人脸库的时候,事先让大家把AppID,API Key,Secret Key复制出来的数据,完成上面的代码后可以在自己的人脸库查看到自己上传上去的图片:
2、人脸检测
我们在登录的时候,首先要判断图片是否具有面部信息
完成上述代码后运行测试,查看控制台如下则成功:
如果图片中存在人脸则出现以下输出:
3、人脸搜索
根据用户上传的图片和指定人脸库中的所有图片进行比较,获取相似度最高的一个或某几个进行评分,也就是一开始所说的阈值,如果阈值达到了我们说设置的则成功,代码如下:
完成以上代码运行,会返回数据:
一般我们设置的阈值大于80分才认为是同一个人,很显然上面的评分不符合我们的要求,则过滤掉
4、人脸更新
在我们使用软件的过程中,我们的面部可能会有所变化,为了防止不准确,我们会更新人脸库的照片,( 注册和更新的代码基本一样 注册时是addUser 更新时是updateUser)
完成以上代码运行即可更新完成
三、刷脸登录
我们在进行刷脸登录的时候,首先会创建一个二维码,使用手机扫描二维码进行人脸赵信,(二维码的内容可以放前端调用摄像头的地址,这样扫码之后就会打开手机摄像头),以下是刷脸登录的流程图:
在此之前先熟悉一下二维码的生成:
加入依赖:
// An highlighted block
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.0</version>
</dependency>
完成上述代码即可
接下来就是刷脸登录的核心代码:
在进行人脸登录的时候首先将一张图片上传到百度云,在进行人脸上传的时候我们可以用户id作为百度云照片的id,因为我们在搜索成功之后,会返回此id,这样可以根据id获取用户的信息登录
// An highlighted block
@RequestMapping(value = "/user/upload/{id}",method = RequestMethod.POST)
public Result upload(@PathVariable String id,@RequestParam(name = "file") MultipartFile file)throws IOException {
//调用service保存图片,同时获取图片访问地址
String imgUrl = userService.uploadImage(id,file);
//返回数据
return new Result(ResultCode.SUCCESS,imgUrl);
}
}
// An highlighted block
//用户头像上传
//注册到百度云人脸库
//调用到百度云接口,判断是否已注册
@Override
public String uploadImage(String id, MultipartFile file)throws IOException {
//根据id查询用户
User user = userDao.findById(id).get();
// 使用DataURL存储图片,对图片进行base64编码
String encode = "data:image/png;base64," + Base64.encode(file.getBytes());
//将图片上传到七牛云存储
// String encode = new QiNiuUploadUtil().upload(user.getId(), file.getBytes());
//更新用户头像返回路径
user.setStaffPhoto(encode);
String base64 = Base64.encode(file.getBytes());
//判断是否已注册
Boolean b = baiDuYunUtil.faceExiet(id);
if (b){
baiDuYunUtil.faceUpdate(id,base64);
}
baiDuYunUtil.faceRegister(id,base64);
userDao.save(user);
return encode;
}
QRCodeUtil 【二维码工具类】
// An highlighted block
public class QRCodeUtil {
//生成base64二维码
public String createQRcode(String content)throws IOException{
ByteArrayOutputStream os = new ByteArrayOutputStream();
try{
//生成二维码
QRCodeWriter qrCodeUtil = new QRCodeWriter();
//基本配置
//二维码信息
//图片类型
//宽度
//长度
BitMatrix bitMatrix = qrCodeUtil.encode(content, BarcodeFormat.QR_CODE,200,200);
//创建ByteArrayOutputStream
//将二维码数据以byte数组保存到ByteArrayOutputStream
BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix);
ImageIO.write(image,"png",os);
return new String("data:image/png;base64," + Base64.encode(os.toByteArray()));
}catch (Exception e){
e.printStackTrace();
}finally {
os.close();
}
return null;
}
}
// An highlighted block
public class QRCode implements Serializable {
public static final String serialVersionUID = "43753879088123002L";
//随机生成码
private String code;
//base64二维码文件
private String file;
}
// An highlighted block
public class FacLoginResult implements Serializable {
//二维码使用状态:-1:未使用,0:登录失败,1:登陆成功
private String state;
//登录信息
private String token ;
//用户id
private int userId;
public FacLoginResult(String state,String token ,int userId){
this.state = state;
this.token = token;
this.userId = userId;
}
public FacLoginResult(String state){
this.state = state;
}
}
BaiDuYunUtil 【百度云工具类】
// An highlighted block
public class BaiDuYunUtil {
@Value("${ai.appId}")
private String APP_ID;
@Value("${ai.API_KEY}")
private String API_KEY;
@Value("${ai.secretKey}")
private String secretKey;
@Value("${ai.imageType}")
private String IMAGE_TYPE;
@Value("${ai.groundId}")
private String GROUND_ID;
private static AipFace aipFace;
private HashMap<String, String> map = new HashMap<>();
public BaiDuYunUtil(){
map.put("quality_control", "NORMAL");//图片质量:NONE,LOW,NORMAL,HIGH
map.put("liveness_control", "LOW");//活体检测
}
@PostConstruct
public void init(){
//创建java代码和百度云交互的client对象
aipFace = new AipFace(APP_ID, API_KEY, secretKey);
}
//人脸注册
//String.valueOf(userId) 因为数据库的id是int类型 建议使用String
public Boolean faceRegister(int userId, String image){
JSONObject jsonObject = aipFace.addUser(image, IMAGE_TYPE, GROUND_ID, String.valueOf(userId), map);
Integer integer = jsonObject.getInt("error_code");
return integer == 0 ? true : false;
}
//人脸检测
/**
* 人脸检测:判断上传图片中是否具有面部头像
*/
public Boolean faceCheck(String image) throws JSONException {
JSONObject res = aipFace.detect(image, IMAGE_TYPE, map);
if (res.has("error_code") && res.getInt("error_code") == 0) {
JSONObject resultObject = res.getJSONObject("result");
Integer faceNum = resultObject.getInt("face_num");
return faceNum == 1?true:false;
}else{
return false;
}
}
/**
* 人脸更新 :更新人脸库中的用户照片
*/
public Boolean faceUpdate(int userId, String image) throws JSONException {
// 人脸更新
JSONObject res = aipFace.updateUser(image, IMAGE_TYPE, GROUND_ID, String.valueOf(userId), map);
Integer errorCode = res.getInt("error_code");
return errorCode == 0 ? true : false;
}
//判断用户是否注册百度云
public boolean faceExiet(int userId){
JSONObject user = aipFace.getUser(String.valueOf(userId), GROUND_ID, null);
Integer integer = user.getInt("error_code");
return integer == 0 ? true:false;
}
//人脸查找:评分大于80,则同意
public int faceSearch(String image){
JSONObject jsonObject = aipFace.search(image,IMAGE_TYPE,"ihrm", map);
if (jsonObject.has("error_code") && jsonObject.getInt("error_code") == 0){
JSONObject result = jsonObject.getJSONObject("result");
JSONArray jsonArray = result.getJSONArray("user_list");
if (jsonArray.length() > 0){
JSONObject user = jsonArray.getJSONObject(0);
double score = user.getDouble("score");
if (score > 80 ){
return user.getInt("user_id");
}
}
}
return 0;
}
}
Conroller【控制层】
// An highlighted block
@RestController
@RequestMapping("/sys/faceLogin")
@Api(tags = "用户刷脸登录")
public class FaceLoginController {
@Autowired
private FaceLoginService faceLoginService;
@Autowired
private BaiDuYunUtil baiDuYunUtil;
/**
* 获取刷脸登录二维码
*/
@GetMapping(value = "/qrcode")
@ApiOperation(value = "获取二维码")
public DataResult qrcode() throws Exception {
QRCode qrCode = faceLoginService.getQRCode();
return DataResult.getResult(BaseResponseCode.SUCCESS,qrCode);
}
/**
* 检查二维码:登录页面轮询调用此方法,根据唯一标识code判断用户登录情况
*/
@GetMapping(value = "/qrcode/{code}")
@ApiOperation(value = "检查二维码")
public DataResult qrcodeCeck(@PathVariable(name = "code") String code) throws Exception {
FacLoginResult checkQRCode = faceLoginService.checkQRCode(code);
return new DataResult(BaseResponseCode.SUCCESS , checkQRCode);
}
/**
* 人脸登录:根据落地页随机拍摄的面部头像进行登录
* 根据拍摄的图片调用百度云AI进行检索查找
*/
@PostMapping(value = "/{code}")
@ApiOperation(value = "人脸登录")
public DataResult loginByFace(@PathVariable(name = "code") String code, @RequestParam(name = "file") MultipartFile attachment) throws Exception {
String userId = faceLoginService.loginByFace(code, attachment);
if (userId != null){
return new DataResult(BaseResponseCode.SUCCESS);
}else {
return new DataResult(BaseResponseCode.FAIL);
}
}
/**
* 图像检测,判断图片中是否存在面部头像
*/
@RequestMapping(value = "/checkFace", method = RequestMethod.POST)
@ApiOperation(value = "图像检测")
public DataResult checkFace(@RequestParam(name = "file") MultipartFile attachment) throws Exception {
String image = Base64Util.encode(attachment.getBytes());
Boolean isExist = baiDuYunUtil.faceCheck(image);
if (isExist){
return new DataResult(BaseResponseCode.SUCCESS);
}else{
return new DataResult(BaseResponseCode.FAIL);
}
}
}
FaceLoginService 【业务层】
public interface FaceLoginService {
//创建二维码
public QRCode getQRCode() throws Exception;
//根据唯一标识,查询用户是否登录成功
public FacLoginResult checkQRCode(String code);
//扫描二维码之后,使用拍摄照片进行登录
public String loginByFace(String code, MultipartFile attachment) throws Exception;
}
FaceLoginServiceImpl 【业务实现层】
// An highlighted block
@Service
public class FaceLoginServiceImpl implements FaceLoginService {
@Value("${qr.url}")
private String url;
@Autowired
private IdWorker idWorker;
@Autowired
private QRCodeUtil qrCodeUtil;
@Autowired
private RedisUtil redisUtil;
@Autowired
private BaiDuYunUtil baiDuYunUtil;
@Autowired
private UserMapper userMapper;
@Autowired
private SysPermissionService sysPermissionService;
//创建二维码
public QRCode getQRCode() throws Exception {
//创建二维码唯一标识
String code = idWorker.nextId()+"";
//二维码内容
String content = url + "?code=" + code;
System.out.println(content);
String file = qrCodeUtil.createQRcode(content);
System.out.println(file);
//将二维码存入redis
FacLoginResult result = new FacLoginResult("-1");
redisUtil.set(getCacheKey(code),result,10,TimeUnit.MINUTES);
return new QRCode(code , file);
}
//根据唯一标识,查询用户是否登录成功
public FacLoginResult checkQRCode(String code) {
String key = getCacheKey(code);
FacLoginResult faceLoginResult = (FacLoginResult) redisUtil.get(key);
return faceLoginResult;
}
//扫描二维码之后,使用拍摄照片进行登录
public String loginByFace(String code, MultipartFile attachment) throws Exception {
//1.调用百度云AI查询当前的用户
int userId = baiDuYunUtil.faceSearch(Base64Util.encode(attachment.getBytes()));
//2.自动登录
FacLoginResult result = new FacLoginResult("0");
if (userId != 0){
User user = userMapper.findById(userId);
if (user != null){
String token = JwtUtil.sign(user.getName(),user.getPassword(),userId);
//将token存入redis缓存,并设置过期时间
redisUtil.set(Constant.PREFIX_USER_TOKEN + user.getName(),token,Constant.TOKEN_EXPIRE_TIME, TimeUnit.SECONDS);
//根据id获取权限集合
// List<String> PermissionsList = userMapper.getUserPermissionsList(user.getId());
Set<String> PermissionsList = sysPermissionService.getPermissionsByUserId(userId);
redisUtil.set(Constant.JWT_PERMISSIONS_KEY +user.getName(),PermissionsList,Constant.TOKEN_EXPIRE_TIME, TimeUnit.SECONDS);
//根据id获取角色集合
List<String> RolesList = userMapper.getUserRolesList(user.getId());
//将RolesList中的权限数据放入redis,并设置权限过期时间
redisUtil.set(Constant.JWT_ROLES_KEY +user.getName(),RolesList,Constant.TOKEN_EXPIRE_TIME, TimeUnit.SECONDS);
AsyncManager.me().execute(AsyncFactory.insertLog(user.getName(),Constant.LOGIN_SUCCESS,"登录成功"));
result = new FacLoginResult("1" , token , userId);
}else {
throw new BusinessException(4045,"用户未注册");
}
}
//3.修改二维码的状态
redisUtil.set(getCacheKey(code),result , 10 , TimeUnit.MINUTES);
return String.valueOf(userId);
}
//构造缓存key
private String getCacheKey(String code) {
return "qrcode_" + code;
}
}
完成以上代码之后我们来进行测试,由于前端没有进行调用摄像头,我们可以上传一张图片进行测试,首先打开postman,由于我的项目整合了swagger,所以我直接在swagger进行测试
1、先获取二维码的code
1、将获取的code,填入之后进行登录
返回操作成功,也生成了token,由此刷脸登录成功!