一、next-auth 身份验证凭据-使用电子邮件和密码注册登录
文章目录
- 一、next-auth 身份验证凭据-使用电子邮件和密码注册登录
- 一、前言
- 二、前置准备
- 1、环境配置
- 2、相关库安装
- (1)vercel 配置
- (2)Yarn 包管理配置
- 3、next项目初始化与创建
- 三、具体实现
- 1、github 仓库创建与链接
- 2、Vercel项目链接Vercel账户并部署
- (1)项目上传vercel
- (2)项目创建并链接postgre 数据库
- (3)本地化项目链接数据库准备
- 3、登录页面与注册页面的前端
- (1)登录页面
- (1)注册页面
- (1)register 页面
- 四、数据库交互
- 1、数据库创建
- 2、vercel项目链接数据库并插入
- (1)vercel postgre准备
- (2) 项目注册并写sql插入数据库
- (3) 项目查询sql数据库并登录
- 五、状态增加
- 1、查询到登录之后登录自动跳转状态增加
- 2、登出与登录状态转换
- 3、重定向
- 4、root保护
- 5、root保护登录页面转为自定义登录页面
一、前言
近些时间需要在next 里面加入登录注册的功能遂整理了一下学习视频:
Next auth 身份验证凭据 - 使用电子邮件和密码注册和登录(NextJS app 路由)
二、前置准备
1、环境配置
-
Vscode
-
node环境配置
-
vercel 账户注册
-
github账户注册
2、相关库安装
(1)vercel 配置
- npm i -g vercel //安装vercel CLI
(2)Yarn 包管理配置
- npm i -g yarn //安装yarn
3、next项目初始化与创建
- yarn create next-app
-
cd nextauth-review (这里面放你写的项目名)
-
yarn dev //运行程序,在http://localhost:3000可以查看next项目的项目名
到这里我们就基本完成了前置的准备工作,下面继续。
三、具体实现
1、github 仓库创建与链接
将新建的项目上传github
2、Vercel项目链接Vercel账户并部署
(1)项目上传vercel
-
vercel login //vercel 登录
-
Vercel //链接与上传
第三行输入n。最开始的时候不连接vercel项目。
注:后续如果项目更新了,要推送到vercel部署,可以通过输入vercel ,然后在第三行输入y,并输入第一步创建的项目名。
(2)项目创建并链接postgre 数据库
(3)本地化项目链接数据库准备
- vercel env pull .env.development.local
注释掉VERCEL,不注释掉就会强制使用https,但我们在本地调试所以说http会报错。正式运行再取消注释。
- openssl rand -base64 32 ,生成32位密码,并修改.env.development.local如下:
-
yarn add @types/bcrypt 安装加密工具bcrypt
-
yarn add @vercel/postgres 安装vercel postgres
-
yarn add next-auth 安装next-auth
到这里该项目的数据库就配置好了,下面我们开始页面开发。
3、登录页面与注册页面的前端
(1)登录页面
新建login文件夹,并在其中新建page.tsx下面是具体的内容
export default function LoginPage(){return (<form className="flex flex-col gap-2 mx-auto max-w-md mt-10"><input name='email' className ="border border-black text-black" type="email" /><input name='password' className ="border border-black text-black" type="password" /><button type="submit">Login</button></form>)
}
(1)注册页面
新建register文件夹,并在其中新建page.tsx下面是具体的内容
export default function LoginPage(){return (<form className="flex flex-col gap-2 mx-auto max-w-md mt-10"><input name='email' className ="border border-black text-black" type="email" /><input name='password' className ="border border-black text-black" type="password" /><button type="submit">Register</button></form>)
}
到这里前端就差不多了,大家可以在http://localhost:3000/login 和http://localhost:3000/register看到你写的页面
nextAuth的官方文档:https://next-auth.js.org/providers/credentials
基本就是用来nextAuth 官方文档中的credentials 字段,结尾加上export {handler as GET, handler as POST}; 就差不多了。
类似如下:
api/auth/[…nextauth]/route.ts
import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
const handler =NextAuth({providers: [CredentialsProvider({credentials: {email: { },password: { }},async authorize(credentials, req) {// Add logic here to look up the user from the credentials suppliedconst user = { id: "1", name: "J Smith", email: "jsmith@example.com" }if (user) {// Any object returned will be saved in `user` property of the JWTreturn user} else {// If you return null then an error will be displayed advising the user to check their details.return null// You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter}}})]
})export {handler as GET, handler as POST};
api/auth/register/route.ts
import { NextResponse } from "next/server";export async function POST(request : Request){try{const {email,password} = await request.json();console.log({email,password});}catch(e){console.log({e});}return NextResponse.json({message:"success"});
}
(1)register 页面
register\page.tsx
import { log } from "console";
import { FormEvent } from "react"export default function LoginPage(){const handleSubmit= async (e:FormEvent<HTMLFormElement>)=>{e.preventDefault();const formData = new FormData(e.currentTarget);const response = await fetch(`/api/auth/register`,{method:"POST",body:JSON.stringify({email:formData.get('email'),password:formData.get('password'),}),})console.log({response});}return (<form onSubmit ={ handleSubmit}className="flex flex-col gap-2 mx-auto max-w-md mt-10"><input name='email' className ="border border-black text-black" type="email" /><input name='password' className ="border border-black text-black" type="password" /><button type="submit">register</button></form>)
}
运行报错:
注:错误原因:需要把组件写到客户端部分,不能直接写进去Page.tsx
修改如下:
register/form.tsx
'use client'
import { FormEvent } from "react"export default function Form(){const handleSubmit = async (e:FormEvent<HTMLFormElement>) =>{e.preventDefault();const formData = new FormData(e.currentTarget);console.log(formData.get('email'),formData.get('password'));const response = await fetch(`/api/auth/register`,{method:'POST',body:JSON.stringify({email:formData.get('email'),password:formData.get('password')}),});console.log({response});};return(<form onSubmit={handleSubmit} className="flex flex-col gap-2 mx-auto max-w-md mt-10"><input name='email' className ="border border-black text-black" type="email" /><input name='password' className ="border border-black text-black" type="password" /><button type="submit">Resgiter</button></form>)
}
register/page.tsx
import Form from './form'export default async function RegisterPage(){return <Form />;}
到现在运行yarn dev 并到 http://localhost:3001/register 页面输入账户和密码,点击登录调用接口:fetch(/api/auth/register
)然后打印{ email: ‘123@123’, password: ‘123’ }
到这里就完成简单的前后端编写,下面进入数据库交互部分。
四、数据库交互
1、数据库创建
依据需求创建表单users1 如上,一共有两个属性email 和password
2、vercel项目链接数据库并插入
确保Browse页面确实能查到这个新建的数据库
(1)vercel postgre准备
yarn add @vercel/postgres
(2) 项目注册并写sql插入数据库
将API/auth/register/route.ts 的内容修改如下即可实现针对数据库的插入
import { NextResponse } from "next/server";
import {hash} from 'bcrypt'
import {sql} from '@vercel/postgres'
export async function POST(request : Request){try{const {email,password} = await request.json();console.log({email,password});const hashedPassword = await hash(password,10); //将密码hash加密到10位const response = await sql`INSERT INTO users1 (email,password) VALUES (${email},${password})`;//${参数} 参数化查询}catch(e){console.log({e});}return NextResponse.json({message:"success"});
}
前端页面register\form.tsx 如下,page.tsx 不变
'use client'
import { signIn } from "next-auth/react";
import { FormEvent } from "react"export default function LoginForm() {const handleSubmit = async (e:FormEvent<HTMLFormElement>) =>{e.preventDefault();const formData = new FormData(e.currentTarget);signIn('credentials',{ //nextauth 登录模块email:formData.get('email'),password:formData.get('password'),redirect:false});};return(<form onSubmit={handleSubmit} className="flex flex-col gap-2 mx-auto max-w-md mt-10"><input name='email' className ="border border-black text-black" type="email" /><input name='password' className ="border border-black text-black" type="password" /><button type="submit">Login</button></form>)
}
测试的时候可以发现可以显示登录信息了
问题:发现邮箱有时候同邮箱有多个,所以需要当相同的时候就不添加而是修改
在数据库中限制让email唯一
(3) 项目查询sql数据库并登录
将API/auth/[…nextauth]/route.ts 的内容修改如下即可实现针对数据库的插入
import { sql } from "@vercel/postgres";
import { compare } from "bcrypt";
import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
const handler =NextAuth({session:{strategy:'jwt'},providers: [CredentialsProvider({credentials: {email: { },password: { }},async authorize(credentials, req) {const response = await sql`SELECT * FROM users1 WHERE email=${credentials?.email}`;const user = response.rows[0];const passwordCorrect = await compare(credentials?.password ||'',user.password);const passworduser=user.passwordconst passwordcre=credentials?.passwordconsole.log({passwordCorrect},{passwordcre},{passworduser});if (passwordCorrect){return {id:user.id,email:user.email}}//console.log({credentials}); //打印credentials信息return null}})]
})export {handler as GET, handler as POST};
前端页面login\form.tsx 如下,page.tsx 不变
'use client'
import { signIn } from "next-auth/react";
import { FormEvent } from "react"export default function LoginForm() {const handleSubmit = async (e:FormEvent<HTMLFormElement>) =>{e.preventDefault();const formData = new FormData(e.currentTarget);const response= await signIn('credentials',{ //nextauth 登录模块email:formData.get('email'),password:formData.get('password'),redirect:false});console.log({response});};return(<form onSubmit={handleSubmit} className="flex flex-col gap-2 mx-auto max-w-md mt-10"><input name='email' className ="border border-black text-black" type="email" /><input name='password' className ="border border-black text-black" type="password" /><button type="submit">Login</button></form>)
}
这时候就可以写入数据库了
五、状态增加
1、查询到登录之后登录自动跳转状态增加
解析:
如果查询到登录模块,没有返回error。则自动导航到‘/’目录同时刷新。
核心修改:
export default function LoginForm() {const router=useRouter();const handleSubmit = async (e:FormEvent<HTMLFormElement>) =>{e.preventDefault();const formData = new FormData(e.currentTarget);const response= await signIn('credentials',{ //nextauth 登录模块email:formData.get('email'),password:formData.get('password'),redirect:false});console.log({response});if(!response?.error){ //没有返回errorrouter.push('/'); //跳转到‘/’router.refresh(); //刷新缓存}};
login/form.tsx全部内容如下
'use client'
import { signIn } from "next-auth/react";
import { useRouter } from "next/navigation";
import { FormEvent } from "react"export default function LoginForm() {const router=useRouter();const handleSubmit = async (e:FormEvent<HTMLFormElement>) =>{e.preventDefault();const formData = new FormData(e.currentTarget);const response= await signIn('credentials',{ //nextauth 登录模块email:formData.get('email'),password:formData.get('password'),redirect:false});console.log({response});if(!response?.error){router.push('/');router.refresh();}};return(<form onSubmit={handleSubmit} className="flex flex-col gap-2 mx-auto max-w-md mt-10"><input name='email' className ="border border-black text-black" type="email" /><input name='password' className ="border border-black text-black" type="password" /><button type="submit">Login</button></form>)
}
2、登出与登录状态转换
登录之后,登出
登出之后,登录,并自动跳转到首页
功能解析
在全部页面都需要有这个跳转。
1、在app首页的layout.tsx页面进行编写。
2、自动跳转用next
该功能核心修改是增加:
<nav>{!!session &&<Logout />}{!session &&<Link href='/login'>Login</Link>}</nav>
文件全部代码如下:
layout.tsx
mport type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { getServerSession } from "next-auth";
import Link from "next/link";
import Logout from "./logout";const inter = Inter({ subsets: ["latin"] });export const metadata: Metadata = {title: "Create Next App",description: "Generated by create next app",
};export default async function RootLayout({children,
}: Readonly<{children: React.ReactNode;
}>) {const session = await getServerSession();return (<html lang="en"><body className={inter.className}><nav>{!!session &&<Logout />}{!session &&<Link href='/login'>Login</Link>}</nav>{children}</body></html>);
}
由于这个里面也涉及到后端,所以需要重新编写一个的客户端API进行处理,在同目录编写
logout.tsx
'use client'import { signOut } from "next-auth/react"
export default function Logout(){return (<span onClick={() => {signOut();}}>Logout</span>)
}
到这里,登出与登录按钮增加完毕
3、重定向
当你登录后不想再返回登录页面可以参考以下的操作。
其他的网址也可以这么操作。
import { getServerSession } from "next-auth";
import Form from "./form";
import { redirect } from "next/navigation";export default async function LoginPage(){const session =await getServerSession();if(session){redirect("/")}return <Form />
}
4、root保护
一些页面需要你登录之后才能访问,在这里面我们借助中间件实现这个操作
在项目主目前增加中间件
middleware.ts
export {default } from 'next-auth/middleware'export const conifg ={matcher:['/dashboard']} //加入你要登录保护的地址
这样子,这个功能就实现完毕了。
5、root保护登录页面转为自定义登录页面
在nextauth 的route页面增加signin路径
pages:{
signIn:‘/login’,
},