React Context API登录实例:如何使用FaceIO进行人脸认证

246 阅读5分钟

在这个构建中,我将展示如何使用React Context API和useReduce钩子来管理反应应用程序中的状态。为了证明这一点,我们将建立一个用户认证的反应组件,利用简单的FaceIO面部认证系统来认证我们的用户。一旦他们被认证,我们将把用户数据传递给状态,并能够在其他组件中使用它。

为什么我们要使用FaceIO

FaceIO 是一个用于网站和基于网络的应用程序的面部认证系统。

FaceIO是:

  1. 非常容易实现。它只需要不到3行代码,基本上需要添加一个像google gtag的脚本。

  2. 无缝,不需要用户获得FIDO密钥、OTP代码和安全问题,从而提高了我们网站的用户体验。

  3. 非常安全。首先,它提供了一个防御级别的面部识别精度,确保我们使用其面部识别引擎准确地验证每一个用户。其次,它保证了用户的隐私,确保他们的数据是安全的,而且他们实际上并不存储用户的脸,而是一个面部矢量

我们将从上次离开的地方继续前进。首先,我们要适当地安排我们的内容。

在src文件夹中,让我们创建一个pages文件夹,并在其中创建两个文件,名为Home.tsx和SignUp.tsx。

接下来我们要添加react router dom

这样,你的构建现在看起来应该是这样的。 image.png

添加页面

我们将把App.tsx的所有内容转移到主页,除了SignUpForm。

const Home = () => {
  return (
    <div className='min-h-screen flex flex-col '>
      <h1 className='text-3xl font-bold text-yellow-600 flex justify-center items-center'>
        FaceIO authentication using react and typescript
      </h1>

    </div>
  );
};

export default Home

把注册组件导入到注册页面。

import SignupForm from '../components/SignupForm';

const SignUp = () => {
  return (
    <div>
      <SignupForm />
    </div>
  );
};

export default SignUp;

现在让我们把react route dom带入App.tsx中,这样我们就能够路由到我们应用程序的不同页面。

import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
import SignUp from './pages/SignUp';

function App() {
  return (
    <div className='min-h-screen flex flex-col justify-center items-center'>
      <BrowserRouter>
        <Routes>
          <Route path='/' element={<Home />} />
          <Route path='signup' element={<SignUp />} />
        </Routes>
      </BrowserRouter>
    </div>
  );
}

export default App;

由于现在我们已经完成了网站的安排,让我们进入我们构建的要点。

我们将创建一个登录函数,通过FaceIO登录(记得我们在前面的部分创建了一个注册函数)。

然后,我们将从FaceIO响应中获取用户数据,并使用useContext和useReducer将其传递到状态中。

在React和Typescript中设置useContext

让我们在src文件夹中创建一个名为context的新文件夹,在那里有一个名为StateProvider.tsx的文件,在那里我们将建立userContext提供者。

import React, {createContext, ReactNode} from 'react';


export const userContext = createContext()


const StateProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
  return(
    <userContext.Provider value={}>
    {children}
  </userContext.Provider>
  )
};

export default StateProvider;

因为我们使用的是typescript,我们需要为我们的构建提供类型。

在src/@types/user.d.ts,我们将创建userContext类型。

所以我们的createContext将是userContextType类型,因为当没有用户时,用户将是空的。

现在让我们在src中创建一个helpers文件夹,并在其中创建一个名为Reducers.ts的文件。

/** src/helpers/Reducer.ts */

import { userContextType, Action } from '../@types/user';


//the initial state of the user
export const initialState = {
  user: null,
};


//the action we are going to take when we login that is set the user
export const actionTypes = {
  SET_USER: 'SET_USER',
};


//the reducer function note the parameter type annotations
export const reducer = (state: userContextType, action: Action) => {
  console.log(action);
  switch (action.type) {
    case actionTypes.SET_USER:
      return {
        ...state,
        user: action.user,
      };
    default:
      return state;
  }
};

有一种类型我们没有定义,那就是动作类型,让我们相应地更新它

转到src/@types/user.d.ts,然后添加。

type Action = {
  type: 'SET_USER';
  user: Iuser;
};

现在一旦我们完成了这个,我们就可以像这样更新StateProvider。

import React, { createContext, ReactNode, useReducer } from 'react';
import { userContextType } from '../@types/user';
import { initialState, reducer } from '../helpers/Reducers';

export const userContext = createContext<{
  state: userContextType;
  dispatch: React.Dispatch<any>;
}>({
  state: initialState,
  dispatch: () => null,
});

const StateProvider: React.FC<{ children: ReactNode }> = ({ children }) => {

//bring the useReducer hook and feed it with the reducer function and the initial state 

  const [state, dispatch] = useReducer(reducer, initialState);
return (

//update the value with the state and dispatch thus you are able to access this values in any component or page

    <userContext.Provider value={{ state, dispatch }}>
      {children}
    </userContext.Provider>
  );
};

到App.tsx中导入StateProvider,并用它来包装整个构建。

<StateProvider>
      <div className='min-h-screen flex flex-col justify-center items-center'>
        <BrowserRouter>
          <Routes>
            <Route path='/' element={<Home />} />
            <Route path='signup' element={<SignUp />} />
          </Routes>
        </BrowserRouter>
      </div>
</StateProvider>

现在让我们创建一个登录组件,以便能够将用户数据输入到状态中。

在componets文件夹中创建一个Login.tsx文件,并在其中创建一个登录。

import { Link } from 'react-router-dom';
const Login = () => {
  return (
    <div>
      <h1 className='text-3xl font-bold text-blue-600 mb-4'>
        Welcome, please login
      </h1>
      <button className='w-full p-3  bg-blue-700 rounded-md text-white text-sm mb-4'>
        Login
      </button>
<div>
        <p>You don't have an account? Please sign-up.</p>
        <Link to='/signup'>
          <button className='w-full p-3 bg-white outline-blue-800 rounded-md text-blue-800 text-sm mb-4'>
            Sign Up
          </button>
        </Link>
      </div>
    </div>
  );
};
export default Login;

这个组件将是我们的门卫,它将拒绝任何人进入我们的网站,除非他们已经登录了。

所以在我们的主页组件中,我们将需要做一些改变。

我们将查询是否有一个用户的状态,即如果用户不是空的,我们将显示主页,否则我们将显示

登录组件

..... return (
    <div className='min-h-screen flex flex-col '>
      {!state?.user ? (
        <Login />
      ) : (
        <div>
          <h1 className='text-3xl font-bold text-yellow-600 flex justify-center items-center'>
            FaceIO authentication using react and typescript
          </h1>
        </div>
      )}
    </div>
  );

这就是你的页面现在应该是的样子。

image.png

登录组件

现在让我们回到登录组件并更新逻辑,即调用FaceIO并分配用户数据。

当我们调用FaceIO进行认证时(FaceIO认证在这里有记录),认证成功后,我们将收到一个包含用户注册时提供的数据的有效载荷。这个有效载荷就是我们要分配给状态的东西。

import React, { useContext, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { userContext } from '../context/StateProvider';
import { actionTypes } from '../helpers/Reducers';
const Login = () => {
  //we load faceIO using a useEffect hook
let faceio: any;
  useEffect(() => {
    faceio = new faceIO('fioa48b7');
  }, []);
//we use a useContext hook dispatch to be able to dispatch our user to the state
  const { dispatch } = useContext(userContext);
//we set up the handle login function
  const handleLogin = async () => {
    try {
      let response = await faceio.authenticate({
        locale: 'auto',
      });
      alert(`
        You have been identified successfully
        Unique Facial ID: ${response.facialId}
          PayLoad: ${JSON.stringify(response.payload)}
          `);
dispatch({ type: actionTypes.SET_USER, user: response.payload });
alert('You have successfully logged in');
    } catch (error) {
      console.log(error);
      alert('Failed to login, please refresh and try again');
    }
  };
return (
    <div className='flex flex-col justify-center items-center'>
      <h1 className='text-3xl font-bold text-blue-600 mb-4'>
        Welcome, please login
      </h1>
      <button onClick={handleLogin} className='w-full p-3  bg-blue-700 rounded-md text-white text-sm mb-4'>
        Login
      </button>
<div>
        <p>You don't have an account? Please sign-up.</p>
        <Link to='/signup'>
          <button
            
            className='w-full p-3 bg-white outline-blue-800 rounded-md text-blue-800 text-sm mb-4'
          >
            Sign Up
          </button>
        </Link>
      </div>
    </div>
  );
};
export default Login;

一旦我们完成了这些工作,我们的应用程序就做好了准备,但让我们在主页上添加一个欢迎用户的声明。

在h1标签下添加。

<h2 className='text-blue-900 pt-28 font-bold'>
   Welcome {state.user.name} Email:{state.user.email}
</h2>

我们现在完成了,如果你成功了,你尝试登录,你会看到。

构建完成

这个构建的全部内容可以在Github上找到,当你在那里的时候,请给它加一颗星。