使用面部识别构建安全的员工仪表盘:Next.js 全面教程
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
准备好革新您的工作场所管理了吗?在本教程中,我们将深入探讨如何创建一个利用人脸识别技术的先进员工仪表盘。我们将使用一些最热门的 Web 开发工具:Next.js、FACEIO和 Shadcn UI。完成本指南后,您将拥有一个简洁、安全的仪表盘,让您的员工体验未来科技!
开始之前你需要准备什么
在深入探讨之前,让我们先确保你已做好一切准备:
- 您的计算机上已安装 Node.js
- npm 或 yarn(哪个顺手就用哪个)
都明白了吗?太好了!那我们开始吧。
项目设置:第一步
步骤 1:启动您的 Next.js 项目
首先,让我们创建 Next.js 项目。打开终端并输入以下命令:
npx create-next-app@latest faceio-app
cd faceio-app
你会被问到几个问题。以下是如何回答:
- TypeScript?当然可以!
- ESLint?当然!
- Tailwind CSS?当然!
- src/ 目录?不用了,没问题。
- 应用路由?好的,谢谢!
- 自定义默认导入别名?这个我们就不做了。
步骤二:准备工具
现在,让我们来安装所有需要的组件。运行以下命令来安装依赖项:
npm install @faceio/fiojs @shadcn/ui class-variance-authority clsx tailwind-merge
步骤三:打造你的秘方
在项目根目录下创建一个名为 .env.local 的文件。我们将在这里保存我们的FACEIO应用 ID:
NEXT_PUBLIC_FACEIO_APP_ID=your-super-secret-faceio-app-id
请务必将“your-super-secret-faceio-app-id”替换为您实际的FACEIO应用程序ID。请妥善保管!
步骤 4:文件结构
您的项目结构应如下所示:
faceio-app/
├── app/
│ ├── layout.tsx
│ ├── page.tsx
│ └── components/
│ ├── FaceAuth.tsx
│ └── EmployeeDashboard.tsx
├── public/
├── .env.local
├── next.config.js
├── package.json
├── tsconfig.json
└── tailwind.config.js
步骤 5:优化 Tailwind CSS
是时候给 Tailwind 来个大改造了。用以下配置更新你的 tailwind.config.js 文件:
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: [
'./app/**/*.{ts,tsx}',
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
}
构建仪表盘的核心
步骤 1:构建 FaceAuth 组件
让我们来创建本次演示的主角——FaceAuth 组件。新建一个文件 app/components/FaceAuth.tsx,并将以下代码粘贴到该文件中:
import { useEffect } from 'react';
import faceIO from '@faceio/fiojs';
import { Button, Card, CardHeader, CardTitle, CardContent } from '@shadcn/ui';
import { useToast } from '@shadcn/ui';
interface FaceAuthProps {
onSuccessfulAuth: (data: any) => void;
}
const FaceAuth: React.FC<FaceAuthProps> = ({ onSuccessfulAuth }) => {
const { toast } = useToast();
useEffect(() => {
const faceio = new faceIO(process.env.NEXT_PUBLIC_FACEIO_APP_ID);
const enrollNewUser = async () => {
try {
const userInfo = await faceio.enroll({
locale: 'auto',
payload: {
email: 'employee@example.com',
pin: '12345',
},
});
toast({
title: "Success!",
description: "You're now enrolled in the facial recognition system!",
});
console.log('User Enrolled!', userInfo);
} catch (errCode) {
toast({
title: "Oops!",
description: "Enrollment failed. Please try again.",
variant: "destructive",
});
console.error('Enrollment Failed', errCode);
}
};
const authenticateUser = async () => {
try {
const userData = await faceio.authenticate();
toast({
title: "Welcome back!",
description: "Authentication successful.",
});
console.log('User Authenticated!', userData);
onSuccessfulAuth({
name: 'John Doe',
position: 'Software Developer',
department: 'Engineering',
photoUrl: 'https://example.com/john-doe.jpg',
});
} catch (errCode) {
toast({
title: "Authentication failed",
description: "Please try again or enroll.",
variant: "destructive",
});
console.error('Authentication Failed', errCode);
}
};
const enrollBtn = document.getElementById('enroll-btn');
const authBtn = document.getElementById('auth-btn');
if (enrollBtn) enrollBtn.onclick = enrollNewUser;
if (authBtn) authBtn.onclick = authenticateUser;
return () => {
if (enrollBtn) enrollBtn.onclick = null;
if (authBtn) authBtn.onclick = null;
};
}, [toast, onSuccessfulAuth]);
return (
<Card className="w-full max-w-md mx-auto">
<CardHeader>
<CardTitle>Facial Authentication</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Button id="enroll-btn" variant="outline" className="w-full">
Enroll New Employee
</Button>
<Button id="auth-btn" variant="default" className="w-full">
Authenticate
</Button>
</CardContent>
</Card>
);
};
export default FaceAuth;
步骤 2:构建员工仪表盘组件
现在,我们来创建员工将看到的仪表盘。创建 app/components/EmployeeDashboard.tsx 文件:
import { useState } from 'react';
import { Card, CardHeader, CardTitle, CardContent } from '@shadcn/ui';
import { Button, Avatar, Badge, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@shadcn/ui';
import FaceAuth from './FaceAuth';
interface EmployeeData {
name: string;
position: string;
department: string;
photoUrl: string;
}
const EmployeeDashboard: React.FC = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [employeeData, setEmployeeData] = useState<EmployeeData | null>(null);
const handleSuccessfulAuth = (data: EmployeeData) => {
setIsAuthenticated(true);
setEmployeeData(data);
};
const mockAttendanceData = [
{ date: '2024-07-14', timeIn: '09:00 AM', timeOut: '05:30 PM' },
{ date: '2024-07-13', timeIn: '08:55 AM', timeOut: '05:25 PM' },
{ date: '2024-07-12', timeIn: '09:05 AM', timeOut: '05:35 PM' },
];
return (
<div className="space-y-6">
{!isAuthenticated ? (
<FaceAuth onSuccessfulAuth={handleSuccessfulAuth} />
) : (
<>
<Card>
<CardHeader>
<CardTitle>Employee Profile</CardTitle>
</CardHeader>
<CardContent className="flex items-center space-x-4">
<Avatar className="h-20 w-20" src={employeeData?.photoUrl} alt={employeeData?.name} />
<div>
<h2 className="text-2xl font-bold">{employeeData?.name}</h2>
<p className="text-gray-500">{employeeData?.position}</p>
<Badge variant="outline">{employeeData?.department}</Badge>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Quick Actions</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Button className="w-full">Check-in</Button>
<Button className="w-full" variant="secondary">Request Leave</Button>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Attendance Records</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Date</TableHead>
<TableHead>Time In</TableHead>
<TableHead>Time Out</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{mockAttendanceData.map((record, index) => (
<TableRow key={index}>
<TableCell>{record.date}</TableCell>
<TableCell>{record.timeIn}</TableCell>
<TableCell>{record.timeOut}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
</Card>
</>
)}
</div>
);
};
export default EmployeeDashboard;
第三步:整合所有内容
最后,让我们更新主页,展示一下我们的成果。更新 app/page.tsx:
import EmployeeDashboard from './components/EmployeeDashboard';
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-center p-4">
<EmployeeDashboard />
</main>
);
}
现在,让我们来设置包裹整个应用程序的布局。添加以下代码:app/layout.tsx
import './globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'Employee Dashboard with Facial Authentication',
description: 'A cutting-edge employee dashboard featuring facial recognition for secure authentication and efficient workplace management.',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>
<header className="bg-primary text-white p-4">
<h1 className="text-2xl font-bold">Faceio Solutions</h1>
</header>
<main className="container mx-auto p-4">
{children}
</main>
<footer className="bg-gray-100 text-center p-4 mt-8">
<p>© 2024 Faceio . All rights reserved.</p>
</footer>
</body>
</html>
)
}
这种布局就像房屋的框架——它为整个应用程序提供了结构。它包括带有公司名称的页眉、显示仪表盘的主内容区域和页脚。此外,它还通过元数据设置了一些 SEO 优化功能!
FACEIO 集成的关键隐私和安全实践
隐私设计
- 使用访问控制、用户同意和退出选项来保护隐私。
有意义的同意
- 确保用户了解数据收集情况。
- 赋予用户数据选择和控制的自由。
- 允许随时撤销同意和删除数据。
最佳实践
- 务必获得明确且适当的同意,尤其是对于未成年人。
- 让用户能够轻松找到并理解同意请求。
- 避免自动注册和未经授权的注册。
- 在收集生物识别数据之前通知用户。
- 遵守法律规定的数据隐私要求。
数据安全
- 账户删除时,用户数据也会被删除。
- 保持严格的数据保留和销毁规范。
- 定期实施和审查安全措施。
更多详情,请参阅FACEIO最佳实践。
FACEIO 集成的关键安全考虑因素
安全设计
- 应用程序安全对于维护用户信任至关重要。
- 遵循 FACEIO 的安全最佳实践来降低风险。
核心安全功能
-
拒绝弱密码
- 避免使用弱密码,例如 0000 或 1234。
- 默认值:否。
-
防止重复注册
- 阻止用户多次注册。
- 默认值:否。
-
防范深度伪造技术
- 检测并阻止欺骗攻击。
- 默认值:否。
-
禁止未成年人入学
- 禁止 18 岁以下用户注册。
- 默认值:否。
-
需要输入PIN码进行身份验证
- 每次验证都需要输入PIN码。
- 默认值:是。
-
强制执行唯一密码
- 确保每个用户的PIN码都是唯一的。
- 默认值:否。
-
忽略被遮挡的脸
- 在光线昏暗或部分遮挡的情况下丢弃面孔。
- 默认值:是。
-
拒绝缺少标头
- 阻止缺少正确 HTTP 标头的实例化。
- 默认值:是。
-
限制实例化
- 仅限特定领域和国家/地区。
- 默认值:否。
-
启用 Webhooks
- 通知后端 FACEIO 事件。
- 默认值:否。
更多详情,请参阅FACEIO安全最佳实践。
实际应用:它可以在哪些方面使用?
现在我们已经构建了这个超棒的仪表盘,你可能会想:“在实际应用中,我该如何使用它呢?” 让我告诉你,它的应用场景可谓无穷无尽!以下仅列举几个例子:
-
办公室管理:告别老式打卡机!这套系统可以彻底改变您追踪考勤、控制办公室不同区域的访问权限以及管理员工信息的方式。
-
安全系统:想象一下,您的办公室像诺克斯堡一样安全,却无需承担任何麻烦。这套人脸识别系统可以成为强大安全协议的基石。
-
客户服务自助服务终端:想象一下——顾客走到自助服务终端前,终端立即识别出他们,并提供个性化服务。这不再是科幻小说里的情节了!
接下来是什么?天空才是极限!
恭喜你,技术达人!你刚刚搭建了一个配备人脸识别功能的尖端员工仪表盘。但为什么就此止步呢?这套系统的优势在于它的灵活性。以下是一些将其提升到更高层次的建议:
- 实现重要更新的实时通知
- 为人力资源部门增加详细的报告功能。
- 与其他系统(例如工资系统或项目管理工具)集成
记住,在科技世界里,唯一的限制就是你的想象力(或许还有你的咖啡因摄入量)。
那么,您觉得怎么样?准备好让您的工作场所迈向未来了吗?不妨试试这个项目,然后告诉我进展如何。我很想了解您的体验,您添加的任何酷炫功能,或者您在过程中遇到的任何挑战。
祝您编程愉快,也希望您的面部识别系统永远不会把您误认为是办公室里的植物!
文章来源:https://dev.to/vyan/building-a-secure-employee-dashboard-with-facial-authentication-a-compressive-nextjs-tutorial-2c4g
