Next.js中构建完整的身份验证系统【翻译】

案例使用第三方库:NextAuth.js, Shadcn/ui, react-hook-form, and Zod等

  • prisma: is an open-source database toolkit. We will use it to store user credentials.
  • next-auth: Authentication for Next.js.
  • react-hook-form: a library that helps you validate forms in React.
  • zod: a data validator.
  • bcrypt: to hash passwords.
  • shadcn/ui: a collection of reusable components.

简要概述:

确保用户的安全和隐私比以往任何时候都更加重要。 Web认证在这方面发挥着至关重要的作用,是保护用户信息和数据的第一道防线。
今天,我们拥有像 NextAuth.js 这样的工具,使我们的工作变得更加轻松,使我们能够在 Next.js 应用程序中轻松实现不同类型的身份验证。
在本系列教程中,我们将在 Next.js 14 中构建一个完整的身份验证系统,从基础知识开始:使用电子邮件和密码进行身份验证。

什么是 NextAuth.js (Auth.js)?

在 JavaScript 生态系统中,更具体地说,在使用 Next.js 开发的应用程序中,处理身份验证的最著名的库之一是 NextAuth.js。
该工具提供了一个简单且易于实施的解决方案,用于向我们的应用程序添加身份验证。最好的一点是它的灵活性;除了基于凭证的身份验证(例如经典的电子邮件和密码)之外,它还允许集成不同的身份验证提供商(例如 Google、Facebook 和 Twitter)。

实现凭证认证 Authentication

凭证验证是非常有用的在需要完全控制验证过程和用户凭证存储的应用程序中,或者当您不想依赖外部身份验证提供商时。

起步

  1. 使用以下命令创建一个新的Next.js项目,并按照指示的步骤进行操作。我们将使用TypeScript和src/文件夹。
npx create-next-app@latest
  1. 安装项目需要的依赖,使用pnpm进行依赖管理
pnpm install next-auth prisma react-hook-form zod, bcrypt

我们使用Shadcn/ui组件

pnpm dlx shadcn-ui@latest init
  • prisma: 是一个开源数据库工具包。我们将使用它来存储用户凭据。
  • next-auth: Next.js的身份验证。
  • react-hook-form: 一个帮助你在React中验证表单的库。
  • zod: 数据验证器。
  • bcrypt: 对密码进行哈希运算。
  • shadcn/ui: 可重用UI组件的集合。
  1. 为项目创建以下结构
...
├── prisma/
...
├── src/
│   ├── actions/
│   │   └── auth-actions.tsx
│   ├── app/
│   │   ├── api/auth/[...nextauth]
│   │   │   └── route.ts
│   │   ├── auth/
│   │   │   ├── signin
│   │   │   │   └── page.tsx
│   │   │   └── signup
│   │   │       └── page.tsx
│   │   │   ...   
│   ├── components/
│   │   ├── auth/
│   │   │   ├── auth-buttons.tsx
│   │   │   ├── signin-form.tsx
│   │   │   ├── signup-form.tsx
│   │   │   └── user-nav.ts
│   │   ├── ui/
│   │   │   ...
│   │   ├── auth-provider.tsx
│   │   ├── icons.tsx
│   │   └── theme-provider.tsx
│   ├── lib/
│   │   ├── prisma.ts
│   │   ├── types.d.ts
│   │   └── utils.ts
│   ...
...

设置Prisma,初始化数据结构

我们将使用Prisma在数据库中存储和检索用户。Prisma允许集成不同的数据库类型,因此您可以使用所需的任何数据库,我们将使用SQLite

初始化Prisma

npx prisma init --datasource-provider sqlite 

这将创建包含其数据model的数据文件夹。

创建 models.

为了创建模型,我们将使用@auth/prisma-adapter提供的模型,并对其进行一些自定义,如下所示

generator client {provider = "prisma-client-js"output = "../../node_modules/.prisma/client"
}datasource db {provider = "sqlite"url      = env("DATABASE_URL")
}...
model User {id            String    @id @default(cuid())username      Stringpassword      Stringemail         String    @uniqueemailVerified DateTime?phone         String?image         String?
}

创建第一个migration

npx prisma migrate dev --name first-migration

使用此命令,在Prisma文件夹中创建了更多文件,数据库已与模型同步。

Prisma客户端

最后,我们创建一个Prisma客户端代码。

import { PrismaClient } from "@prisma/client";const globalForPrisma = global as unknown as {prisma: PrismaClient;
};export const prisma = globalForPrisma.prisma || new PrismaClient();if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;export default prisma;

设置NextAuth.js

创建 .env 环境变量

# Secret key for NextAuth.js, used for encryption and session security.  It should be a long,
# random string unique to your application.
NEXTAUTH_SECRET=XXX3B2CC28F123456C6934531CXXXXX# Base URL for your Next.js app, used by NextAuth.js for redirects and callbacks.
NEXTAUTH_URL=http://localhost:3000/

创建身份auth验证路由

此路径允许在单个端点上处理所有身份验证请求(如登录、注销和供应商回调)。
src/app/api/auth/[...nextauth]

创建providers

...
// Imports the Prisma User type for typing.
import { User } from '@prisma/client'// Configuration of authentication options for NextAuth.
export const authOptions: AuthOptions = {...// Defines authentication providers, in this case, only credentials.providers: [CredentialsProvider({name: 'Credentials',// Defines the required fields for authentication.credentials: {email: { label: 'Email', type: 'text' },password: { label: 'Password', type: 'password' },},// Function to authenticate the user with the provided credentials.async authorize(credentials) {// Searches for the user in the database by email.const user = await prisma.user.findUnique({where: {email: credentials?.email,},})// Checks if the user exists and if the password is correct.if (!user) throw new Error('User name or password is not correct')if (!credentials?.password) throw new Error('Please Provide Your Password')const isPasswordCorrect = await bcrypt.compare(credentials.password, user.password)if (!isPasswordCorrect) throw new Error('User name or password is not correct')// Returns the user without including the password.const { password, ...userWithoutPass } = userreturn userWithoutPass},}),],
}// Exports the configured NextAuth handler to handle GET and POST requests.
const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }

创建 Auth Provider

src/components/auth-provider.tsx:

'use client'import { SessionProvider } from 'next-auth/react'export default function AuthProvider({ children }: { children: React.ReactNode }) {return <SessionProvider>{children}</SessionProvider>
}

此组件充当使用NextAuth进行身份验证的Next.js应用程序的会话提供者。
将组件或页面包装在此提供程序中,可以授予它们访问会话上下文的权限,允许子组件使用NextAuth钩子和功能,例如useSession来访问或修改用户当前会话的状态。
src/app/layout.tsx:

export default function RootLayout({ children }: { children: React.ReactNode }) {return (<htmllang='en'suppressHydrationWarning><body className={`${inter.className} relative`}><AuthProvider><main>{children}</main></AuthProvider></body></html>)
}

使用Shadcn/UI设置用户界面

按照文档安装shadcn/ui
Would you like to use TypeScript (recommended)? yes
Which style would you like to use? › Default
Which color would you like to use as base color? › Slate
Where is your global CSS file? › src/app/globals.css
Do you want to use CSS variables for colors? › yes
Are you using a custom tailwind prefix eg. tw-? Leave blank
Where is your tailwind.config.js located? › tailwind.config.js
Configure the import alias for components: › @/components
Configure the import alias for utils: › @/lib/utils
Are you using React Server Components? › yes
实施黑暗模式

src/app/layout.tsx

export default function RootLayout({ children }: { children: React.ReactNode }) {return (<htmllang='en'suppressHydrationWarning><body className={`${inter.className} relative`}><AuthProvider><ThemeProviderattribute='class'defaultTheme='dark'enableSystemdisableTransitionOnChange><main>{children}</main><Toaster /></ThemeProvider></AuthProvider></body></html>)
}
安装以下shadcn/ui组件:
pnpm dlx shadcn-ui@latest add avatar button dropdown-menu form input label tabs toast

创建身份验证组件

src/components/auth-buttons.tsx:

'use client'import Link from 'next/link'
import { signIn, useSession } from 'next-auth/react'import { Button } from '../ui/button'
import { UserNav } from './user-nav'export default function AuthButtons() {// Use the useSession hook to access session dataconst { data: session } = useSession()return (<div className='flex justify-end gap-4'>{session && session.user ? (<UserNav user={session.user} />) : (<><Buttonsize={'sm'}variant={'secondary'}onClick={() => signIn()}>Sign In</Button><Buttonsize={'sm'}asChildclassName='text-foreground'><Link href='/auth/signup'>Sign Up</Link></Button></>)}</div>)
}

此组件根据用户的会话状态动态显示身份验证选项。如果用户已登录,则会显示用户特定的导航。此外,它还提供了登录或注册按钮,利用Next.js的路由和NextAuth的身份验证功能,提供流畅的用户体验。
src/components/auth/signup-form.tsx

'use client'/*all imports
*/// Function to register a new user
import { registerUser } from '@/actions/auth-actions'// Define the validation schema for the signup form using Zod
const formSchema = z.object({username: z.string({required_error: 'Username is required',}).min(2, 'User name must have at least 2 characters').max(12, 'Username must be up to 12 characters').regex(new RegExp('^[a-zA-Z0-9]+$'), 'No special characters allowed!'),email: z.string({ required_error: 'Email is required' }).email('Please enter a valid email address'),password: z.string({ required_error: 'Password is required' }).min(6, 'Password must have at least 6 characters').max(20, 'Password must be up to 20 characters'),confirmPassword: z.string({ required_error: 'Confirm your password is required' }).min(6, 'Password must have at least 6 characters').max(20, 'Password must be up to 20 characters'),}).refine(values => values.password === values.confirmPassword, {message: "Password and Confirm Password doesn't match!",path: ['confirmPassword'],})// Type inference for form inputs based on the Zod schema
type InputType = z.infer<typeof formSchema>export function SignUpForm() {const [isLoading, setIsLoading] = useState(false)const { toast } = useToast() // Hook to show toast notifications// Initialize form handling with React Hook Form and Zod for validationconst form = useForm<InputType>({resolver: zodResolver(formSchema),})// Handles form submissionasync function onSubmit(values: InputType) {try {setIsLoading(true)const { confirmPassword, ...user } = values // Exclude confirmPassword from data to be sentconst response = await registerUser(user) // Register the userif ('error' in response) {toast({title: 'Something went wrong!',description: response.error,variant: 'success',})} else {toast({title: 'Account Created!',description: 'Your account has been created successfully! You can now login.',})}} catch (error) {console.error(error)toast({title: 'Something went wrong!',description: "We couldn't create your account. Please try again later!",variant: 'destructive',})} finally {setIsLoading(false)}}return (<Form {...form}><form onSubmit={form.handleSubmit(onSubmit)}><div className='grid gap-2'>// Each FormField validates and displays an input<FormFieldcontrol={form.control}name='username'render={({ field }) => (<FormItem><FormControl><div className='flex items-center gap-2'><Icons.userclassName={`${form.formState.errors.username ? 'text-destructive' : 'text-muted-foreground'} `}/><Inputplaceholder='Your Username'className={`${form.formState.errors.username && 'border-destructive bg-destructive/30'}`}{...field}/></div></FormControl><FormMessage /></FormItem>)}/>// Repeated structure for email, password, and confirmPassword with respective validations and icons<ButtonclassName='text-foreground mt-4'disabled={isLoading} // Disable button during form submission>{isLoading && <Icons.spinner className='mr-2 h-4 w-4 animate-spin' />} // Show loading icon if isLoading is trueSign Up</Button></div></form></Form>)
}

该组件封装了一个用户注册表,使用react钩子表单进行表单状态管理,使用Zod进行模式验证。
我在页面上添加了更多样式,看起来像这样:
image.png
src/actions/auth-action.ts

'use server'/*all imports
*/export async function registerUser(user: Omit<User, 'id' | 'phone' | 'emailVerified' | 'image'>) {try {// Attempt to create a new user record in the databaseconst result = await prisma.user.create({data: {...user,// Hash the password before storing itpassword: await bcrypt.hash(user.password, 10),},})return result} catch (error) {console.log(error)// Handle known request errors from Prismaif (error instanceof Prisma.PrismaClientKnownRequestError) {// Check for unique constraint failure (e.g., email already exists)if (error.code === 'P2002') {return { error: 'Email already exists.' }}}// Return a generic error message for any other errorsreturn { error: 'An unexpected error occurred.' }}
}

registerUser函数旨在通过在数据库中创建包含所提供用户信息的记录来安全地注册新用户,不包括id、phone、emailVerified和image等字段。
它使用bcrypt对用户的密码进行哈希运算,以实现安全存储。
为了测试我们的注册并验证用户是否正确注册,我们需要添加一些回调;这些功能允许您自定义身份验证和会话管理的行为。
src/app/api/auth/[...nextauth]

export const authOptions: AuthOptions = {// Define custom pages for authentication flowpages: {signIn: '/auth/signin', // Custom sign-in page},// Configure session management to use JSON Web Tokens (JWT)session: {strategy: 'jwt',},// JWT configuration, including secret for token signingjwt: {secret: process.env.NEXTAUTH_SECRET, // Secret used to sign the JWT, stored in environment variables},...// Callbacks for customizing JWT and session behaviorscallbacks: {// Callback to modify the JWT content. Adds user information if available.async jwt({ token, user }) {if (user) token.user = user as User // Cast user object to User type and assign to tokenreturn token},// Callback to modify session content. Adds user information to the session.async session({ token, session }) {session.user = token.user // Assign user information from token to sessionreturn session},},
}

回调jwt:

在身份验证生命周期中,每当创建或更新JSON Web令牌(jwt)时,都会执行此回调。它允许您在令牌被签名并发送到客户端或存储在服务器上之前修改令牌的内容。

这对于向令牌添加可能与您的应用程序逻辑相关的其他信息非常有用。
session 回调

每次读取会话数据时都会调用此回调,例如在服务器端呈现期间或在受保护的API请求中。它允许在将会话数据发送到客户端之前对其进行修改。

这对于基于JWT中存储的信息或其他标准添加或修改会话数据特别有用。
最后,我们需要扩展NextAuth Session和JWT类型定义,以包含其他用户信息。
src/lib/types.d.ts

import { User } from '@prisma/client'declare module 'next-auth' {interface Session {user: User}
}declare module 'next-auth/jwt' {interface JWT {user: User}
}

现在,如果我们填写表格并提交,我们将能够看到成功的提示语。为了验证用户是否保存在数据库中,我们可以使用以下命令以图形方式查看Prisma创建的表:

nxp prisma studio

将提供以下路线http://localhost:5555
image.png
src/components/auth/user-nav.tsx:

/*all imports
*/interface Props {user: User // Expect a user object of type User from Prisma client
}export function UserNav({ user }: Props) {return (<DropdownMenu><DropdownMenuTrigger asChild><Buttonvariant='ghost' className='relative h-8 w-8 rounded-full'><Avatar className='h-9 w-9'><AvatarImagesrc='/img/avatars/01.png'alt='' /><AvatarFallback>UU</AvatarFallback></Avatar></Button></DropdownMenuTrigger><DropdownMenuContentclassName='w-56'align='end'forceMount><DropdownMenuLabel className='font-normal'><div className='flex flex-col space-y-1'><p className='text-sm font-medium leading-none'>{user.username}</p><p className='text-xs leading-none text-muted-foreground'>{user.email}</p></div></DropdownMenuLabel><DropdownMenuSeparator /><DropdownMenuItem><Linkhref={'/api/auth/signout'} // Link to the signout API routeclassName='w-full'>Sign Out</Link></DropdownMenuItem></DropdownMenuContent></DropdownMenu>)
}

src/components/auth/signin-form.tsx

/* all imports 
*/// Schema definition for form validation using Zod
const formSchema = z.object({email: z.string({ required_error: 'Please enter your email' }).email('Please enter a valid email address'),password: z.string({required_error: 'Please enter your password',}),
})// Type inference for form inputs based on the Zod schema
type InputType = z.infer<typeof formSchema>// Props definition, optionally including a callback URL
interface Props {callbackUrl?: string
}export function SignInForm({ callbackUrl }: Props) {const [isLoading, setIsLoading] = useState(false)const { toast } = useToast()const router = useRouter() // Hook to control routingconst form = useForm<InputType>({resolver: zodResolver(formSchema), // Set up Zod as the form validation resolver})// Function to handle form submissionasync function onSubmit(values: InputType) {try {setIsLoading(true)// Attempt to sign in using the 'credentials' providerconst response = await signIn('credentials', {redirect: false, // Prevent automatic redirectionemail: values.email,password: values.password,})// Handle unsuccessful sign in attemptsif (!response?.ok) {toast({title: 'Something went wrong!',description: response?.error,variant: 'destructive',})return}toast({title: 'Welcome back! ',description: 'Redirecting you to your dashboard!',})router.push(callbackUrl ? callbackUrl : '/') // Redirect to the callback URL or home page} catch (error) {toast({title: 'Something went wrong!',description: "We couldn't create your account. Please try again later!",variant: 'destructive',})} finally {setIsLoading(false)}}return (<Form {...form}><form onSubmit={form.handleSubmit(onSubmit)}><div className='grid gap-2'><div className='grid gap-1'><FormFieldcontrol={form.control}name='email'render={({ field }) => (<FormItem><FormControl><div className='flex items-center gap-2'><Icons.email className={`${form.formState.errors.email ? 'text-destructive' : 'text-muted-foreground'} `}/><Inputtype='email'placeholder='Your Email'className={`${form.formState.errors.email && 'border-destructive bg-destructive/30'}`}{...field}/></div></FormControl><FormMessage /></FormItem>)}/>{/* Password field */}{/* Similar structure to email field, customized for password input */}</div><ButtonclassName='text-foreground mt-4'disabled={isLoading} // Disable button while loading>{isLoading && <Icons.spinner className='mr-2 h-4 w-4 animate-spin' />} // Show loading spinner when processingSign In</Button></div></form></Form>)
}

image.png

我们已经完成了使用NextAuth.js实现基本身份验证。
项目仓库代码
要拥有一个完整的身份验证系统,还有很多事情要做,我们将在接下来的教程中介绍它们。
https://hackernoon.com/how-to-send-email-verification-in-nextjs-14-with-nextauthjs-resend-and-react-email
https://hackernoon.com/enhancing-password-security-and-recovery-with-nextjs-14-and-nextauthjs

项目总结

总之,我们探讨了如何使用NextAuth在Next.js中实现和定制身份验证系统,如何扩展会话和JWT以丰富用户管理,以及如何使用react hook form和Zod通过有效验证来处理表单。

文章链接:https://hackernoon.com/how-to-implement-authentication-in-nextjs-14-with-nextauthjs-shadcnui-react-hook-form-and-zod

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1486908.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Github Desktop 关于将本地文件夹设置为新仓库的 使用笔记

实际要达到的结果: 将UE5工程同步到Github,工程太大,我们只需要将必要的工程文件夹同步即可,缓存等一些不必要的文件夹则不需要同步 最终效果预览: 1. 将本地文件夹设置为新仓库 将本地文件夹作为仓库一般你是没有这个仓库的,所以你需要新建一个仓库 如果忽略某些不必要的文…

# OpenCV 图像预处理—形态学:膨胀、腐蚀、开运算、闭运算 原理详解

文章目录 形态学概念膨胀使用膨胀操作来修复裂痕示例代码关键解析&#xff1a; 腐蚀使用腐蚀操作消除噪点示例代码&#xff1a; 开运算—先腐蚀后膨胀闭运算—先膨胀后腐蚀 形态学概念 首先看这两张图片 一张图周围有大大小小的噪音和彩点&#xff0c;另一张图片中字母有间隙&…

go语言Gin框架的学习路线(十一)

目录 GORM的CRUD教程 更新操作 更新所有字段 更新指定字段 使用 Select 和 Omit 更新 无 Hooks 更新 批量更新 删除操作 删除记录 批量删除 软删除 物理删除 示例代码 GORM的CRUD教程 CRUD 是 "Create, Read, Update, Delete"&#xff08;创建、查询、…

Google Cloud Platform数据工程简介

Google Cloud Platform数据工程简介 前言 云计算的出现为数据驱动型组织提供了采用成本效益高且可扩展的数据工程解决方案的机会。在云服务提供商中&#xff0c;Google Cloud Platform (GCP) 是近年来表现优异的领导者之一。GCP的增长被归因于其在企业和初创公司中的日益普及…

【TypeScript 一点点教程】

文章目录 一、开发环境搭建二、基本类型2.1 类型声明2.2 基本类型 三、编译3.1 tsc命令3.2 tsconfig.json3.2.1 基本配置项includeexcludeextendsfiles 3.2.2 compilerOptions编译器的配置项 四、面向对象4.1 类4.2 继承4.3 抽象类4.4 接口 一、开发环境搭建 下载Node.js《Nod…

【全国大学生电子设计竞赛】2023年B题

&#x1f970;&#x1f970;全国大学生电子设计大赛学习资料专栏已开启&#xff0c;限时免费&#xff0c;速速收藏~

IP数据报结构详解:从基础到进阶

目录 IP数据报的格式 IP数据报首部的固定部分 IP数据报首部的可变部分 实例分析&#xff1a;数据报的分片 生存时间&#xff08;TTL&#xff09;与协议 首部检验和 总结 在网络通信中&#xff0c;IP数据报是至关重要的基本单元。本文将带您深入了解IP数据报的格式及其各个…

Python酷库之旅-第三方库Pandas(041)

目录 一、用法精讲 136、pandas.Series.ne方法 136-1、语法 136-2、参数 136-3、功能 136-4、返回值 136-5、说明 136-6、用法 136-6-1、数据准备 136-6-2、代码示例 136-6-3、结果输出 137、pandas.Series.eq方法 137-1、语法 137-2、参数 137-3、功能 137-4、…

数据结构-C语言-排序(4)

代码位置&#xff1a; test-c-2024: 对C语言习题代码的练习 (gitee.com) 一、前言&#xff1a; 1.1-排序定义&#xff1a; 排序就是将一组杂乱无章的数据按照一定的规律&#xff08;升序或降序&#xff09;组织起来。(注&#xff1a;我们这里的排序采用的都为升序) 1.2-排…

如何借助生成式人工智能引领未来的科技狂潮

如何借助生成式人工智能引领未来的科技狂潮 1. 生成式AI的现状1.1 技术基础1.1.1 深度学习1.1.2 生成对抗网络&#xff08;GANs&#xff09;1.1.3 变分自编码器&#xff08;VAEs&#xff09; 1.2 主要应用1.2.1 语言模型1.2.2 图像生成1.2.3 音频与视频生成 2. 未来的发展趋势2…

Windows下帆软BI(finebi)单机部署移植(Tomcat)攻略

一、基础环境 操作系统&#xff1a;Windows 10 64bit 帆软BI 版本&#xff1a;V9.0/V10.0 HTTP工具&#xff1a;Tomcat 外置数据库&#xff1a;Oracle 11g 实验内容&#xff1a;将已经部署好的帆软BI从一台电脑移植到另一台电脑 二、前期准备 1、做好外置数据库移植&…

<数据集>苹果腐烂识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;978张 标注数量(xml文件个数)&#xff1a;978 标注数量(txt文件个数)&#xff1a;978 标注类别数&#xff1a;2 标注类别名称&#xff1a;[fresh_apple, rotten_apple] 序号类别名称图片数框数1fresh_apple520922…

2022 年中高职组“网络安全”赛项-海南省省竞赛任务书-1-B模块-B-4Web渗透测试

前言 本章节我将尝试操作B-4模块的渗透测试&#xff0c;搭建环境很难&#xff0c;还望大家点点赞多多支持&#xff01; 任务概览 最后4、5、6有一定的难度。 环境要求 kali Linux192.168.41.2Web服务器&#xff08;假设为PYsystem 2020 模拟平台&#xff09;192.168.41.7交换…

日常开发记录分享——C#控件ToolTip实现分栏显示内容

文章目录 需求来源实现思路实施请看VCR等等别走&#xff0c;有优化 需求来源 需要在鼠标浮动到指定位置后提示出详细的信息&#xff0c;一开始使用的tooltip实现&#xff0c;但是里面的内容效果并不理想&#xff0c;需要有条理性&#xff0c;于是就想到能不能将展示的东西分列…

代理协议解析:如何根据需求选择HTTP、HTTPS或SOCKS5?

代理IP协议是一种网络代理技术&#xff0c;可以实现隐藏客户端IP地址、加速网站访问、过滤网络内容、访问内网资源等功能。常用的IP代理协议主要有Socks5代理、HTTP代理、HTTPS代理这三种。代理IP协议主要用于分组交换计算机通信网络的互联系统中使用&#xff0c;只负责数据的路…

Linux(CentOS)的“应用商城” —— yum

Linux&#xff08;CentOS&#xff09;的“应用商城” —— yum 关于 yum 和软件包Linux 系统&#xff08;CentOS&#xff09;的生态yum 相关操作yum 本地配置yum 安装 lrzsz.x86_64 关于 yum 和软件包 首先 yum 是软件下载安装管理的客户端&#xff0c;类似各种手机里的“应用…

面试场景题系列--(1)如果系统的 QPS 突然提升 10 倍该怎么设计?--xunznux

1. 如果系统的 QPS 突然提升 10 倍该怎么设计&#xff1f; 1.1 硬件的扩展微服务的拆分 如果所有的业务包括交易系统、会员信息、库存、商品等等都夹杂在一起&#xff0c;当流量一旦起来之后&#xff0c;单体架构的问题就暴露出来了&#xff0c;机器挂了所有的业务就全部无法…

【机器学习】Jupyter Notebook如何使用之基本步骤和进阶操作

引言 Jupyter Notebook 是一个交互式计算环境&#xff0c;它允许创建包含代码、文本和可视化内容的文档 文章目录 引言一、基本步骤1.1 启动 Jupyter Notebook1.2 使用 Jupyter Notebook 仪表板1.3 在笔记本中工作1.4 常用快捷键1.5 导出和分享笔记本 二、进阶用法2.1 组织笔…

“微软蓝屏”事件,给IT行业带来的宝贵经验和教训

“微软蓝屏”事件是指2024年7月19日发生的一次全球性技术故障&#xff0c;主要涉及微软视窗&#xff08;Windows&#xff09;操作系统及其相关应用和服务。 以下是对该事件的详细解析&#xff1a; 一、事件概述 发生时间&#xff1a;2024年7月19日事件影响&#xff1a;全球多个…

暑期c++ 命名空间

今天是暑期第一天开始写c笔记&#xff0c;新起点&#xff0c;新开始加油 我们先来看两串代码 这串代码编译没有问题 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int rand 14; int main(void) {int rand 14;printf("%d\n", rand);return 0; }但是…