安裝next-auth
npm install next-auth
配置pages/_app.js
添加SessionProvider讓所有頁面均能獲取到session數據
import { SessionProvider } from 'next-auth/react'
export default function App ({ Component, pageProps }) {
//自定義布局方法,可刪除
const layout = Component.layout || ((page) => page)
return (
<SessionProvider
options={{
staleTime: 0,
refetchInterval: 0
}}
session={pageProps.session} >
{layout(<Component {...pageProps} />)}
</SessionProvider>
)
}
中間件攔截未授權路由
例如需要攔截management路由下的所有頁面請求,則在/pages/management
下新建立_middleware.js
import {NextResponse} from 'next/server'
import {getToken} from "next-auth/jwt"
export async function checkAuth(req) {
//獲取token
const session = await getToken({
req,
secret: process.env.SECRET,
secureCookie:
process.env.NEXTAUTH_URL?.startsWith("https://") ??
!!process.env.VERCEL_URL,
})
//未授權,跳轉到登錄頁面
if (!session) {
return NextResponse.redirect("/user/login")
} else {
NextResponse.next()
}
}
授權api
新建/api/auth/[...nextauth].js
文件
import NextAuth from "next-auth"
import CredentialsProvider from 'next-auth/providers/credentials'
import {getUser} from "../../../request/modules/userReq";
//配置next-auth,參考https://next-auth.js.org/configuration/options
export default NextAuth({
// provider配置憑證登錄
providers: [
CredentialsProvider({
name: 'login',
async authorize(credentials, req) {//具體授權邏輯
const user = await getUser(credentials.userName)
if(user?.password===credentials.password){
return {name:user.userName}
}
return {status:'reject'}
}
})
],
secret: process.env.SECRET,
session: {
strategy: "jwt",
},
jwt: {},
pages: {//自定義界面 ,可配置signIn,signOut,error,verifyRequest,newUser
signIn: '/user/login',
},
callbacks: {//回調函數
async signIn({ user, account, profile, email, credentials }) {
//登錄回調,如果authorize不成功,重定向到login界面,並附帶錯誤信息參數
if(user?.status==='reject'){
return '/user/login/?msg=invalid'
}
return true
},
// async redirect({ url, baseUrl }) {//不設置回調,直接默認使用url
// url一般為被中間件攔截之前的目標url,例如:localhost:3000/management/index,baseurl為localhost:3000,如果url不包含baseUrl,大概率是signIn回調函數重定向頁面
// if (url.startsWith(baseUrl)) return url
// else if (url.startsWith("/")) return new URL(url, baseUrl).toString()
// },
// async session({session, token, user}) {
// return session
// },
// async jwt({token, user, account, profile, isNewUser}) {
// return token
// }
},
events: {},
theme: {colorScheme: "light"},
debug: false,
})
登錄界面user/login.js
其中包含自定義組件信息,可自定義實現
import Head from "next/head";
import styles from '/styles/pages/management/login.module.scss'
import Form from "../../components/form/Form";
import Input from "../../components/form/Input";
import Button from "../../components/form/Button";
import Row from "../../components/layouts/FlexLayout/Row";
import Col from "../../components/layouts/FlexLayout/Col";
import {getCsrfToken} from "next-auth/react"
import Link from 'next/link'
import {useRouter} from "next/router";
const ERROR_MSG={
invalid:"用戶名或密碼錯誤"
}
export default function SignIn(props) {
const router = useRouter()
const msg = router?.query?.msg//解析的錯誤信息 參數
return (
<div>
<Head>
<title>登錄</title>
<meta name="description" content="管理登錄"/>
{/*<link rel="icon" href="/favicon.ico"/>*/}
</Head>
<main className={styles['xl-login-card']}>
<p className='title'>登錄</p>
{msg&&<p className='xl-error-msg'>{ERROR_MSG[msg]}</p>}
<Form labelWidth={110} action="/api/auth/callback/credentials">
//此處csrfToken一定要一並上傳
<input name="csrfToken" type="hidden" defaultValue={props.csrfToken}/>
<Form.FormItem label='用戶名'>
<Input name='userName' label='用戶名' height={0}/>
</Form.FormItem>
<Form.FormItem label='密碼'>
<Input name='password' label='密碼' type='password' height={0}/>
</Form.FormItem>
<Form.FormItem labelWidth={0} style={{'margin-top': '40px'}}>
<Row justify='space-around'>
<Col><Button label={'主頁'} to='/'/></Col>
<Col><Button type='submit' label={'登錄'}/></Col>
</Row>
</Form.FormItem>
</Form>
</main>
<style global jsx>{`
//設置背景
body {
background:url(/imgs/light-bg.jpg) no-repeat;
//background-size: cover;//或者background-size: 100% 100%;
background-size:100% 100%;
background-attachment: fixed;
}
`}</style>
</div>
)
}
//獲取初始化csrfToken
export async function getServerSideProps(context) {
return {
props: {
csrfToken: await getCsrfToken(context),
},
}
}
簡單退出登錄
新建/management/index.js
該路由未授權下 會被中間件攔截 重定向到login頁面
import {useSession, signOut} from "next-auth/react"
import Button from "../../components/form/Button";
function Management() {
//獲取session
const {data: session} = useSession()
return <div>{JSON.stringify(session)}
//退出登錄直接調用signOut方法即可
<Button onClick={signOut}>out</Button>
</div>
}
export default Management