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

posted on 2021-12-25 16:23  吞天泡泡龙  阅读(6083)  评论(1编辑  收藏  举报