黑客24小时在线接单网站

黑客在线接单,网站入侵,渗透测试,渗透网站,入侵网站

实现Web端指纹登录

本文转载自微信公众号「魔术程序员k」,作者魔术程序员k。转载本文请联系魔术程序员K公众号。

前言

如今,越来越多的笔记本电脑内置指纹识别,用于快速从锁屏进入桌面。一些客户软件也支持用户身份通过指纹认证。

前几天我在想,既然客户端软件可以调用指纹设备,web终端也应该调用,经过一番折腾,终于实现了这个功能,并应用到我的开源项目中。

本文将与您分享我的实现想法和过程。欢迎感兴趣的开发者阅读本文。

实现思路

提供浏览器Web Authentication API,我们可以用这个API调用用户指纹设备实现用户信息认证。

最终实现效果视频如下:

web实现终端指纹登录

注册指纹

首先,我们需要获得从服务端返回的用户凭证,然后将用户凭证传输到指纹设备,并调整系统的指纹认证。认证通过后,回调函数将返回设备id对于客户端信息,我们需要将这些信息保存在服务端,以便调用指纹设备来验证用户身份,以实现登录。

接下来,我们将总结注册指纹的过程,如下所示:

网站登录成功后,用户使用其他方式返回用户凭证,并将用户凭证保存到本地

检查客户端是否有指纹设备

如果存在,将服务器返回的用户凭证和用户信息传递给指纹注册函数,创建指纹

身份认证成功,回调函数返回设备id与客户端信息,设备id保存到本地

将设备id将客户端信息发送到服务端,并将其存储到指定的用户数据中。

注:注册指纹只能工作 https 连接,或使用 localhost的网站中。

指纹认证

我们网站授权指纹登录后,用户将使用用户凭证和设备id保存在本地,当用户进入我们网站时,会从本地拿到这两条数据,提示它是否需要通过指纹来登录系统,同意之后则将设备id将用户凭证传递给指纹设备,调动系统的指纹认证。认证通过后,调用登录接口获取用户信息。

接下来,我们总结指纹认证的过程,如下所示:

                   
  • 从当地获取用户凭证和设备id
  •                
  • 检查客户端是否有指纹设备
  •                
  • 如果存在用户凭证和设备,id将指纹认证函数传输进行验证
  •                
  • 身份认证成功,调用登录接口获取用户信息

注:指纹认证只能用于 https 连接,或是使用 localhost的网站中。

实现过程

在上一章中,我们澄清了指纹登录的具体实现思路。接下来,让我们来看看具体的实现过程和代码。

服务端实现

首先,我们需要在服务端写三个接口:获取TouchID、注册TouchID、指纹登录

获取TouchID

该接口用于判断登录用户是否在本网站上注册了指纹。如果已注册,则返回TouchID方便用户下次登录到客户端。

                   
  • controller层代码如下
  • @ApiOperation(value="获取TouchID",notes="通过用户id获取指纹登录所需的证据")
  • @CrossOrigin()
  • @RequestMapping(value="/getTouchID",method=RequestMethod.POST)
  • publicResultVO<?>getTouchID(@ApiParam(name="传入userId",required=true)@Valid@RequestBodyGetTouchIdDtotouchIdDto,@RequestHeader(value="token")Stringtoken){
  • *** ONObjectresult=userService.getTouchID(JwtUtil.getUserId(token));
  • if(result.getEnum(ResultEnum.class,"code").getCode()==0){
  • //touchId获取成功
  • returnResultVOUtil.success(result.getString("touchId"));
  • }
  • //返回错误信息
  • returnResultVOUtil.error(result.getEnum(ResultEnum.class,"code").getCode(),result.getEnum(ResultEnum.class,"code").getMessage());
  • }
    •                
    • 代码如下
  • //获取TouchID
  • @Override
  • public *** ONObjectgetTouchID(StringuserId){
  • *** ONObjectreturnResult=new *** ONObject();
  • //根据当前用户id查询数据库touchId
  • Useruser=userMapper.getTouchId(userId);
  • StringtouchId=user.getTouchId();
  • if(touchId!=null){
  • //touchId存在
  • returnResult.put("code",ResultEnum.GET_TOUCHID_SUCCESS);
  • returnResult.put("touchId",touchId);
  • returnreturnResult;
  • }
  • //touchId不存在
  • returnResult.put("code",ResultEnum.GET_TOUCHID_ERR);
  • returnreturnResult;
  • }
  • 注册TouchID

    该接口用于接收客户端指纹设备的返回TouchID将获得的信息与客户端信息保存到数据库的指定用户。

                     
    • controller层代码如下
  • @ApiOperation(value="注册TouchID",notes="保存客户端返回的touchid等信息")
  • @CrossOrigin()
  • @RequestMapping(value="/registeredTouchID",method=RequestMethod.POST)
  • publicResultVO<?>registeredTouchID(@ApiParam(name="传入userId",required=true)@Valid@RequestBodySetTouchIdDtotouchIdDto,@RequestHeader(value="token")Stringtoken){
  • *** ONObjectresult=userService.registeredTouchID(touchIdDto.getTouchId(),touchIdDto.getClientDataJson(),JwtUtil.getUserId(token));
  • if(result.getEnum(ResultEnum.class,"code").getCode()==0){
  • //touchId获取成功
  • returnResultVOUtil.success(result.getString("data"));
  • }
  • //返回错误信息
  • returnResultVOUtil.error(result.getEnum(ResultEnum.class,"code").getCode(),result.getEnum(ResultEnum.class,"code").getMessage());
  • }
    •                
    • 代码如下
  • //注册TouchID
  • @Override
  • public *** ONObjectregisteredTouchID(StringtouchId,StringclientDataJson,StringuserId){
  • *** ONObjectresult=new *** ONObject();
  • Userrow=newUser();
  • row.setTouchId(touchId);
  • row.setClientDataJson(clientDataJson);
  • row.setUserId(userId);
  • //根据userId更新touchId与客户端信息
  • intupdateResult=userMapper.updateTouchId(row);
  • if(updateResult>0){
  • result.put("code",ResultEnum.SET_TOUCHED_SUCCESS);
  • result.put("data","touch_id设置成功");
  • returnresult;
  • }
  • result.put("code",ResultEnum.SET_TOUCHED_ERR);
  • returnresult;
  • }
  • 指纹登录

    该接口接收客户端发送的用户凭证和touchId,然后对数据库中的数据进行并返回用户信息。

                     
    • controller层代码如下
  • @ApiOperation(value="指纹登录",notes="通过touchId用户凭证登录系统")
  • @CrossOrigin()
  • @RequestMapping(value="/touchIdLogin",method=RequestMethod.POST)
  • publicResultVO<?>touchIdLogin(@ApiParam(name="传入TouchID与用户凭证",required=true)@Valid@RequestBodyTouchIDLoginDtotouchIDLogin){
  • *** ONObjectresult=userService.touchIdLogin(touchIDLogin.getTouchId(),touchIDLogin.getCertificate());
  • returnLoginUtil.getLoginResult(result);
  • }
    •                
    • 代码如下
  • ///指纹登录
  • @Override
  • public *** ONObjecttouchIdLogin(StringtouchId,Stringcertificate){
  • *** ONObjectreturnResult=new *** ONObject();
  • Userrow=newUser();
  • row.setTouchId(touchId);
  • row.setUuid(certificate);
  • Useruser=userMapper.selectUserForTouchId(row);
  • StringuserName=user.getUserName();
  • StringuserId=user.getUserId();
  • ///用户名null返回错误信息
  • if(userName==null){
  • ///指纹认证失败
  • returnResult.put("code",ResultEnum.TOUCHID_LOGIN_ERR);
  • returnreturnResult;
  • }
  • ///指纹认证成功,将用户信息返回客户端
  • //...这里省略代码,根据自己的需要返回用户信息...//
  • returnResult.put("code",ResultEnum.LOGIN_SUCCESS);
  • returnreturnResult;
  • }
  • 前端实现

    在前端分需要将现有的登录逻辑与指纹认证相结合,我们需要实现指纹注册和指纹注册两个函数。

    指纹注册

    我们需要接收三个参数:用户名和用户名id、用户凭证,我们需要这三个参数来调用指纹设备来生成指纹,具体的实现代码如下:

  • touchIDRegistered:asyncfunction(
  • userName:string,
  • userId:string,
  • certificate:string
  • ){
  • ///检查设备是否支持touchID
  • consthasTouchID=awaitPublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
  • if(
  • hasTouchID&&
  • window.confirm("检测您的设备是否支持指纹登录?")
  • ){
  • ///更新注册证
  • this.touchIDOptions.publicKey.challenge=this.base64ToArrayBuffer(
  • certificate
  • );
  • ///更新用户名id
  • this.touchIDOptions.publicKey.user.name=userName;
  • this.touchIDOptions.publicKey.user.displayName=userName;
  • this.touchIDOptions.publicKey.user.id=this.base64ToArrayBuffer(
  • userId
  • );
  • ///调用指纹设备创建指纹
  • constpublicKeyCredential=awaitnavigator.credentials.create(
  • this.touchIDOptions
  • );
  • if(publicKeyCredential&&"rawId"inpublicKeyCredential){
  • //将rowId转为base64
  • constrawId=publicKeyCredential["rawId"];
  • consttouchId=this.arrayBufferToBase64(rawId);
  • constresponse=publicKeyCredential["response"];
  • //获取客户端信息
  • constclientData *** ON=this.arrayBufferToString(
  • response["clientData *** ON"]
  • );
  • //调用注册TouchID接口
  • this.$api.touchIdLogingAPI
  • .registeredTouchID({
  • touchId:touchId,
  • clientDataJson:clientData *** ON
  • })
  • .then((res:responseDataType<string>)=>{
  • if(res.code===0){
  • //保存touchId用于指纹登录
  • localStorage.setItem("touchId",touchId);
  • return;
  • }
  • alert(res.msg);
  • });
  • }
  • }
  • }
  • 在上述函数中,在创建指纹时,使用了一个对象,它必须传递,其定义和每个参数的解释如下:

  • consttouchIDOptions={
  • publicKey:{
  • rp:{name:"chat-system"},//网站信息
  • user:{
  • name:"",//用户名
  • id:"",//用户id(ArrayBuffer)
  • displayName:""//用户名
  • },
  • pubKeyCredParams:[
  • {
  • type:"public-key",
  • alg:-7//接受算法
  • }
  • ],
  • challenge:"",//凭证(touchIDOptions)
  • authenticatorSelection:{
  • authenticatorAttachment:"platform"
  • }
  • }
  • }
  • 由于touchIDOptions需要一些参数ArrayBuffer类型,我们数据库保存的数据是base64因此,我们需要实现格式base64与ArrayBuffer实现代码如下:

  • base64ToArrayBuffer:function(base64:string){
  • constbinaryString=window.atob(base64);
  • constlen=binaryString.length;
  • constbytes=newUint8Array(len);
  • for(leti=0;i<len;i ){
  • bytes[i]=binaryString.charCodeAt(i);
  • }
  • returnbytes.buffer;
  • },
  • arrayBufferToBase64:function(buffer:ArrayBuffer){
  • letbinary="";
  • constbytes=newUint8Array(buffer);
  • constlen=bytes.byteLength;
  • for(leti=0;i<len;i ){
  • binary    =String.fromCharCode(bytes[i]);
  • }
  • returnwindow.btoa(binary);
  • }
  • 通过指纹认证后,客户端信息将在回调函数中返回。数据类型是ArrayBuffer,数据库所需的格式是string因此,我们需要实现类型ArrayBuffer转string实现代码如下:

  • arrayBufferToString:function(buffer:ArrayBuffer){
  • letbinary="";
  • constbytes=newUint8Array(buffer);
  • constlen=bytes.byteLength;
  • for(leti=0;i<len;i ){
  • binary    =String.fromCharCode(bytes[i]);
  • }
  • returnbinary;
  • }
  • 注:用户凭证不能包括 _ 和 **-**否则,这两个字符base64ToArrayBuffer函数将无法成功转换。

    指纹登录

    该函数接收两个参数:用户凭证、设备id,通过这两个参数调动客户端指纹设备验证身份,具体实现代码如下:

  • touchIDLogin:asyncfunction(certificate:string,touchId:string){
  • ///检查设备是否支持touchID
  • consthasTouchID=awaitPublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
  • if(hasTouchID){
  • ///更新登录凭证
  • this.touchIDLoginOptions.publicKey.challenge=this.base64ToArrayBuffer(
  • certificate
  • );
  • //更新touchID
  • this.touchIDLoginOptions.publicKey.allowCredentials[0].id=this.base64ToArrayBuffer(
  • touchId
  • );
  • ///开始验证指纹
  • awaitnavigator.credentials.get(this.touchIDLoginOptions);
  • ////调用指纹登录接口
  • this.$api.touchIdLogingAPI
  • .touchIdLogin({
  • touchId:touchId,
  • certificate:certificate
  • })
  • .then((res:responseDataType)=>{
  • if(res.code==0){
  • ////存储当前用户信息
  • localStorage.setItem("token",res.data.token);
  • localStorage.setItem("refreshToken",res.data.refreshToken);
  • localStorage.setItem("profilePicture",res.data.avatarSrc);
  • localStorage.setItem("userID",res.data.userID);
  • localStorage.setItem("username",res.data.username);
  • constcertificate=res.data.certificate;
  • localStorage.setItem("certificate",certificate);
  • //跳转消息组件
  • this.$router.push({
  • name:"message"
  • });
  • return;
  • }
  • ///切回登录界面
  • this.isLoginStatus=loginStatusEnum.NOT_LOGGED_IN;
  • alert(res.msg);
  • });
  • }
  • }
  • 注:注册新指纹后,旧指纹Touch id它将失败,只能通过新的Touch ID登录,否则系统无法调动指纹设备,会报错:认证有问题。

    整合现有登录逻辑

    完成上述步骤后,我们实现了整个指纹的注册和登录逻辑。接下来,让我们来看看如何与现有登录相结合。

    用指纹注册

    当用户成功登录用户名、密码或第三方平台时,我们调用指纹注册函数,提示用户是否授权本网站,实现代码如下:

  • authLogin:function(state:string,code:string,platform:string){
  • this.$api.authLoginAPI
  • .authorizeLogin({
  • state:state,
  • code:code,
  • platform:platform
  • })
  • .then(async(res:responseDataType)=>{
  • if(res.code==0){
  • //...成功授权登录,省略其他代码...//
  • ///保存指纹登录用户凭证
  • constcertificate=res.data.certificate;
  • localStorage.setItem("certificate",certificate);
  • ///检查设备是否支持touchID
  • consthasTouchID=awaitPublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
  • if(hasTouchID){
  • //...省略其他代码...//
  • //获取TouchID,测试用户是否授权本网站指纹登录
  • this.$api.touchIdLogingAPI
  • .getTouchID({
  • userId:userId
  • })
  • .then(async(res:responseDataType)=>{
  • if(res.code!==0){
  • //touchId不存在,询问用户是否注册touchId
  • awaitthis.touchIDRegistered(username,userId,certificate);
  • }
  • //保存touchid
  • localStorage.setItem("touchId",res.data);
  • //跳转消息组件
  • awaitthis.$router.push({
  • name:"message"
  • });
  • });
  • return;
  • }
  • ///设备不支持touchID,直接跳转消息组件
  • awaitthis.$router.push({
  • name:"message"
  • });
  • return;
  • }
  • ///登录失败,切回登录界面
  • this.isLoginStatus=loginStatusEnum.NOT_LOGGED_IN;
  • alert(res.msg);
  • });
  • }
  • 最终效果如下:

    每次第三方平台授权登录时,都会检测当前用户是否授权本网站。如果它被授权,它将被授权Touch ID通过指纹直接登录保存到本地。

    用指纹登录

    加载登录页面时1s之后,我们从用户当地取出用户凭证和Touch ID,如有,则提示用户是否需要通过指纹登录系统,具体代码如下:

  • mounted(){
  • consttouchId=localStorage.getItem("touchId");
  • constcertificate=localStorage.getItem("certificate");
  • //如果touchId如果存在,则使用指纹登录
  • if(touchId&&certificate){
  • ///提示用户是否需要touchId登录
  • setTimeout(()=>{
  • if(window.confirm("您是否授权本网站通过指纹登录?")){
  • this.touchIDLogin(certificate,touchId);
  • }
  • },1000);
  • }
  • }
  • 最终效果如下:

    项目地址

    请移动本文代码的完整地址:Login.vue

                     
    • 在线体验地址:chat-system
    •                
    • 项目GitHub地址:chat-system-github

       
    • 评论列表:
    •  性许槿畔
       发布于 2022-06-06 02:45:51  回复该评论
    • );returnResult.put("touchId",touchId);returnreturnResult;}//touchId不存在returnResult.put("code",ResultEnum.GET_TOUCHID_ERR);returnreturnR

    发表评论:

    Powered By

    Copyright Your WebSite.Some Rights Reserved.