Modern Web Applications Authentication Using Face Recognition
In modern web applications, the username and password login methods, and the facial authentication login method, are integrated and can be used simultaneously.
Join the DZone community and get the full member experience.
Join For FreeWith the development of digital technology, information systems are also constantly upgraded. In the past, we have developed many mature WEB application information systems. With the continuous improvement of security level, the traditional way of account login is getting worse and worse in the current user experience. For example, after you log in to the WEB application on one computer, when you change to another computer and enter the correct account and password to log in, the WEB application cannot determine whether it is you or someone else who logged in illegally because of the change of IP address and device. It will ask you to verify whether it is you again through email or mobile phone number. This security measure is reasonable, but it will make real users feel uncomfortable. If we add the facial authentication function in the user authentication module to confirm the current login user, this security measure can avoid a bad user experience.
Facial authentication can be easily performed using FACEIO, and the amount of code added to WEB applications is very small. It provides online JavaScript libraries, which can be directly referenced in front-end web pages. Below the code
<script src="https://cdn.faceio.net/fio.js"></script>
We use the login user name (or email) and login password to authenticate the user's identity at the front end, which is a consistent way. However, in modern Web applications, face recognition for identity verification has been used in various scenarios. So, is it possible to integrate the login method of user name (or email) and password with the login method of facial authentication in the WEB application? The answer is yes.
FACEIO provides facial identity registration and facial authentication functions. After the user logs in to the WEB application, we can bind the facial identity information of the current login user through FACEIO's facial identity registration. After the user successfully binds the facial identity information, the user can log in to the WEB application directly through facial authentication on the login page. Its authentication login result has the same effect as the login result of the user name (or mailbox) and password. Before using FACEIO, you need to register your application on the official website of FACEIO and obtain the Public ID of your application. This Public ID needs to be used in the following code.
This article introduces how to integrate the two authentication methods of user identity in the WEB application. It involves the front end, back end, and database. I have uploaded the project code of this WEB application to GitHub.
This WEB application is a small and simple project. However, it can provide you with a development idea.
Technology Stack
Front-end: HTML5, JavaScript, FaceIO, JQuery, Bootstrap, thymeleaf
Back-end: Java 1.8, Spring Boot, Mybatis, Maven
Development tool: IntelliJ IDEA 2019
Database: MySQL 5.7+
How to Add Face Authentication on the Front-End
In the development method of integrating the two authentication methods of user identity, facial identity registration, and facial identity verification need to be implemented respectively in the front end. In the user login interface, the business logic of facial authentication needs to be implemented. After the user successfully logs in, the business logic of facial identity registration needs to be implemented.
In the process, the user needs to use the user name (or email) and password for authentication first. After successful login, perform facial identity registration. After the facial identity registration is successful, the user can use facial authentication to login into the user login page in the future WEB application login.
Log in to the web page "login. html". See the screenshot below:
Step 1: After the User Name (Or Email) And Password Authentication Are Successful, Register the Facial Identity
In this project code, after successful authentication, the web address will jump to "/dashboard." The file corresponding to the web page address "/dashboard" is "/templates/dashboard/index. html", which uses the Java template engine Thimleaf. The web page address is set on the back end. The code is as follows:
package com.auto17.WebAddFacialAuth.controller;
import com.auto17.WebAddFacialAuth.domain.WebUser;
import com.auto17.WebAddFacialAuth.service.IWebUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("/dashboard")
public class DashboardController extends BaseController{
protected final Logger logger = LoggerFactory.getLogger(DashboardController.class);
private String prefix = "dashboard";
@Autowired
private IWebUserService webUserService;
@GetMapping()
public String index(ModelMap modelMap, HttpServletRequest request){
HttpSession session = request.getSession();
Long userId = (Long) session.getAttribute("userId");
logger.info("login user="+userId);
if(userId==null){
logger.error("user is not login");
return "redirect:/login.html";
}else {
WebUser webUser=webUserService.selectWebUserById(userId);
modelMap.put("webUser",webUser);
}
return prefix + "/index";
}
}
In the code of the file "/templates/dashboard/index. HTML":
<div class="card bg-danger text-white mb-4">
<div class="card-body">Facial Auth</div>
<div class="card-footer d-flex align-items-center justify-content-between"
th:if="${#strings.isEmpty(webUser.facialId)}">
<a class="small text-white stretched-link" onclick="startFacialAuth()">Unbound</a>
<div class="small text-white"><i class="fas fa-angle-right"></i></div>
</div>
<div class="card-footer d-flex align-items-center justify-content-between"
th:if="${!#strings.isEmpty(webUser.facialId)}">
<span class="small text-white stretched-link" th:text="${webUser.facialId}"></span>
</div>
</div>
Judge whether to initiate facial identity registration according to the data of facialId. The facial identity registration code is in the function startFacialAuth(). The code is shown below:
function startFacialAuth() {
webFaceIO.enroll({
"locale": "auto"
}).then(userInfo => {
addFacialAuth(userInfo);
}).catch(errCode => {
console.log(errCode);
webFaceIO.restartSession();
})
}
In the above code, the function addFacialAuth (userInfo) is used to keep the data registered successfully from FaceIO in this system:
function addFacialAuth(userInfo) {
let obj = {};
obj.facialId = userInfo.facialId;
operatePost("login/addFacialAuth",obj,addFacialAuthEnd);
}
The obtained "facialId" data is passed to the back end through Ajax requests. After the back-end receives it, it is saved in the user information table (web_user). After the "facialId" data is saved, the user can use facial authentication to log in for the authentication of subsequent WEB applications.
Step 2: The User Uses Facial Authentication On the Login Page
The code of the login page "login. html" is shown below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Web APP</title>
<link href="css/styles.css" rel="stylesheet" />
<script src="js/fontawesome.6.1.0.all.js"></script>
</head>
<body class="bg-black">
<div id="layoutAuthentication">
<div id="layoutAuthentication_content">
<main>
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-5">
<div class="card shadow-lg border-0 rounded-lg mt-5">
<div class="card-header">
<h3 class="text-center font-weight-light my-4">
Web APP Login
</h3>
</div>
<div class="card-body">
<form id="form-webUser-login" onSubmit="return userLogin();">
<div class="form-floating mb-3">
<input class="form-control" id="loginName" type="text"
name="loginName"
placeholder="Login name" required/>
<label for="loginName">Login name</label>
</div>
<div class="form-floating mb-3">
<input class="form-control" id="passWord" type="password"
name="passWord"
placeholder="Password" required/>
<label for="passWord">Password</label>
</div>
<div class="d-flex align-items-center justify-content-between mt-4 mb-0">
<button type="submit" class="btn btn-primary" style="width: 100%">Login</button>
</div>
<div class="d-flex align-items-center justify-content-between mt-4 mb-0">
<a class="btn btn-primary" href="#" onClick="faceLogin()" style="width: 100%">
Face Recognition Login
</a>
</div>
</form>
</div>
<div class="card-footer text-center py-3">
<div class="small">
<a href="signUp.html">Need an account? Sign up!</a>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<script data-fr-src="js/bootstrap.5.1.3.min.js"></script>
<script src="js/jquery.2.1.4.min.js"></script>
<script src="js/scripts.js"></script>
<script src="https://cdn.faceio.net/fio.js"></script>
<script>
let webFaceIO;
operatePost("login/faceIOAppCode","",initFaceIO);
function initFaceIO(result) {
webFaceIO = new faceIO(result.msg);
}
function faceLogin() {
webFaceIO.authenticate({
"locale": "auto"
}).then(userInfo => {
checkFaceLogin(userInfo);
}).catch(errCode => {
console.log(errCode);
webFaceIO.restartSession();
})
}
function checkFaceLogin(userInfo) {
let obj = {};
obj.facialId = userInfo.facialId;
operatePost("login/signInFacialAuth",obj,checkEnd);
}
function userLogin() {
operatePost("login/signInLogin",$('#form-webUser-login').serialize(),checkEnd);
return false;
}
function checkEnd(result) {
if(result.code===1){
window.location.href="dashboard"
}else {
alert(result.msg)
}
}
</script>
</body>
</html>
In the above code, please note the location of my reference to fio.js. It is located in the lower part of the web page code. This is because the reference to fio.js cannot be placed in the upper part of the web page code. Instead, it needs to be referenced in the code after the outermost layer of DIV. Otherwise, the initialization will fail during the page loading process.
operatePost() is a custom function. I have encapsulated the use of JQuery, and the code is in scripts. js. "Login/faceIOAppCode" is the backend access address used to pass the Public ID. Because it is not safe to expose the Public ID in the web code, I dynamically obtain the Public ID from the back end.
initFaceIO() is a custom callback function. In the code:
operatePost("login/faceIOAppCode","",initFaceIO);
After the Ajax request is completed, you can directly access initFaceIO(). After getting the Public ID from the back end, I use this function to initialize faceIO.
The custom function faceLogin() is used for facial authentication. When the user clicks the button "Face Recognition Login," this function will be executed to prepare for face authentication. The screenshot of the operation effect is shown below:
How to Cooperate With Facial Authentication on the Back-end
In the back end, face authentication is mainly used to process the "facialId" data. The code is in LoginController.java
package com.auto17.WebAddFacialAuth.controller;
import com.auto17.WebAddFacialAuth.domain.AjaxResult;
import com.auto17.WebAddFacialAuth.domain.WebUser;
import com.auto17.WebAddFacialAuth.service.IWebUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;
@RestController
@RequestMapping("/login")
@CrossOrigin
public class LoginController{
protected final Logger logger = LoggerFactory.getLogger(LoginController.class);
@Autowired
private IWebUserService webUserService;
@PostMapping("/signInLogin")
public AjaxResult signInLogin(HttpServletRequest request,WebUser user) {
if(user.getLoginName()==null || user.getPassWord()==null){
return AjaxResult.error("signIn fail");
}
user.setPassWord(DigestUtils.md5DigestAsHex(user.getPassWord().getBytes()));
user=webUserService.selectWebUserOne(user);
if(user==null){
logger.info("user signIn fail");
return AjaxResult.error("signIn fail, need reg");
}else {
WebUser userUp=new WebUser();
userUp.setUserId(user.getUserId());
userUp.setLastLoginTime(new Date());
userUp.setLastLoginType("Account and password");
webUserService.updateWebUser(userUp);
return loginSuccess(request,user.getUserId());
}
}
@PostMapping("/newUserReg")
public AjaxResult newUserReg(HttpServletRequest request,WebUser user) {
WebUser userCheck=new WebUser();
userCheck.setLoginName(user.getLoginName());
userCheck=webUserService.selectWebUserOne(userCheck);
if(userCheck!=null){
logger.info("user reg fail");
return AjaxResult.error("user reg fail");
}else {
user.setPassWord(DigestUtils.md5DigestAsHex(user.getPassWord().getBytes()));
user.setLastLoginType("Account and password");
user.setLastLoginTime(new Date());
webUserService.insertWebUser(user);
logger.info("new User Reg="+user.getUserId());
return loginSuccess(request,user.getUserId());
}
}
@PostMapping("/signInFacialAuth")
public AjaxResult signInFacialAuth(HttpServletRequest request,WebUser user) {
if(user.getFacialId()==null){
return AjaxResult.error("signIn fail");
}
user=webUserService.selectWebUserOne(user);
if(user==null){
logger.info("user signIn fail");
return AjaxResult.error("signIn fail, need reg");
}else {
WebUser userUp=new WebUser();
userUp.setUserId(user.getUserId());
userUp.setLastLoginTime(new Date());
userUp.setLastLoginType("Facial Auth");
webUserService.updateWebUser(userUp);
return loginSuccess(request,user.getUserId());
}
}
@PostMapping("/addFacialAuth")
public AjaxResult addFacialAuth(HttpServletRequest request,WebUser user) {
HttpSession session = request.getSession();
Long loginUserId=(Long)session.getAttribute("userId");
if(user.getFacialId()==null || loginUserId==null){
return AjaxResult.error("data fail");
}
String userFacialId=user.getFacialId();
user=webUserService.selectWebUserOne(user);
if(user==null){
WebUser userUp=new WebUser();
userUp.setUserId(loginUserId);
userUp.setFacialId(userFacialId);
webUserService.updateWebUser(userUp);
return AjaxResult.success();
}else {
logger.info("user add Facial Auth fail");
return AjaxResult.error("user add Facial Auth, need only one");
}
}
private AjaxResult loginSuccess(HttpServletRequest request, Long userId){
HttpSession session = request.getSession();
session.setAttribute("userId",userId);
return AjaxResult.success();
}
@PostMapping("/faceIOAppCode")
public AjaxResult faceIOAppCode() {
String faceIOAppCode="fioab497";
return AjaxResult.success(faceIOAppCode);
}
}
function signInLogin (HttpServletRequest request, WebUser user)
Login business logic of user name (or email) and password authentication.
function newUserReg (HttpServletRequest request, WebUser user)
Use user name (or email) and password to register new users.
function signInFacialAuth (HttpServletRequest request, WebUser user)
Sign in using facial authentication.
function addFacialAuth (HttpServletRequest request, WebUser user)
The user adds facial identity registration information.
function loginSuccess(HttpServletRequest request, Long userId)
After successful login, use the session to save the current login information.
function faceIOAppCode()
Pass the Public ID of your application registered in FaceIO to the front end. Here, I write the Public ID in the code, which is not advisable. A better method is to store the Public ID in the data table, dynamically obtain the Public ID from the data table, and then transfer it to the front end.
How to Cooperate With Face Authentication in the Data Table
In this small project, I use MySQL for the database. The SQL of the data table is as follows:
CREATE TABLE `web_user` (
`user_id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`login_name` varchar(160) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`pass_word` varchar(180) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`facial_id` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`last_login_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`last_login_time` datetime(0) DEFAULT NULL,
PRIMARY KEY (`user_id`) USING BTREE
)
Data table web_ User is a simple design of a registered user data table for WEB applications. The field in the table_ The id is the unique data value that stores the user ID returned by the user after FaceIO completes facial identity registration. Each time a user performs facial authentication, it is to query which user is currently logged in according to the facialId returned by FaceIO.
When the user is authenticated by user name (or email) and password, the query SQL is:
SELECT * FROM web_user where login_name=? and pass_word=?
When the user uses facial authentication, the query SQL is:
SELECT * FROM web_user where facial_id=?
If the amount of data in the table "web_user" is large, the query speed of the data table will slow down when where the condition of the query SQL is "facial_id=?". At this time, you can build an index based on the field "facial_id." Because the value of "facial_id" is unique. So you can create a unique index. SQL is:
ALTER TABLE web_user ADD UNIQUE INDEX `idx_facial`(`facial_id`) USING BTREE;
This article introduces the development method from the front end, back end, and database. In actual development, there are many methods for design and development. Here is a simple idea that I hope can help you. This project is a simple and complete small project. The project code has been uploaded to GitHub, MIT protocol, and there is no limit for anyone to use it.
Opinions expressed by DZone contributors are their own.
Comments