安装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