一、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项目的项目名![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/a4888b853191aef95a05e6515217eded.png)
到这里我们就基本完成了前置的准备工作,下面继续。
三、具体实现
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![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/b1e6ad62cedc07a521628621795f35a8.png)
基本就是用来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 supplied
const user = { id: "1", name: "J Smith", email: "jsmith@example.com" }
if (user) {
// Any object returned will be saved in `user` property of the JWT
return 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.password
const passwordcre=credentials?.password
console.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){ //没有返回error
router.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’,
},