我們可以根據(jù)自己的項(xiàng)目需求,來定制我們自己的授權(quán)登錄系統(tǒng)。這里我推薦使用Nuxt-auth,一個(gè)包裝了 React的 Next-auth的開源項(xiàng)目授權(quán)框架。Nuxt3版本的使用的是Sidebase
安裝
npm i -D @sidebase/nuxt-auth
項(xiàng)目配置
Nuxt auth 新版本提供了 local 和 authjs 兩種模式
local 模式,提供全自定義授權(quán)規(guī)則,需要自己開發(fā)相應(yīng)的授權(quán)接口,官方提供的local 模式 的 參考源碼,本文將主要介紹Nuxt-auth自帶的 authjs 模式
nuxt.config.ts
export default defineNuxtConfig({
modules: ['@sidebase/nuxt-auth'],
auth: {
provider: {
type: 'authjs'
},
session: {
// Whether to refresh the session every time the browser window is refocused.
enableRefreshOnWindowFocus: true,
// Whether to refresh the session every `X` milliseconds. Set this to `false` to turn it off. The session will only be refreshed if a session already exists.
enableRefreshPeriodically: 5000,
},
globalAppMiddleware: {
isEnabled: true, // 自動(dòng)開啟全局鑒權(quán),如果沒有登錄自動(dòng)跳到登錄頁(yè)面
},
}
})
新增 server/api/auth/[...].ts
// file: ~/server/api/auth/[...].ts
import CredentialsProvider from "next-auth/providers/credentials";
import { NuxtAuthHandler } from "#auth";
export default NuxtAuthHandler({
// A secret string you define, to ensure correct encryption
secret: useRuntimeConfig().private.NUXT_SECRET,
providers: [
// @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point
CredentialsProvider.default({
// The name to display on the sign in form (e.g. 'Sign in with...')
name: "Credentials",
// The credentials is used to generate a suitable form on the sign in page.
// You can specify whatever fields you are expecting to be submitted.
// e.g. domain, username, password, 2FA token, etc.
// You can pass any HTML attribute to the <input> tag through the object.
credentials: {
username: {
label: "Username",
type: "text",
placeholder: "(hint: jsmith)",
},
password: {
label: "Password",
type: "password",
placeholder: "(hint: hunter2)",
},
},
authorize(credentials: any) {
// You need to provide your own logic here that takes the credentials
// submitted and returns either a object representing a user or value
// that is false/null if the credentials are invalid.
// NOTE: THE BELOW LOGIC IS NOT SAFE OR PROPER FOR AUTHENTICATION!
const user = {
id: "1",
name: "J Smith",
username: "admin",
password: "admin",
};
if (
credentials?.username === user.username &&
credentials?.password === user.password
) {
// Any object returned will be saved in `user` property of the JWT
return user;
} else {
// eslint-disable-next-line no-console
console.error(
"Warning: Malicious login attempt registered, bad credentials provided"
);
// If you return null then an error will be displayed advising the user to check their details.
throw createError({
statusMessage: "賬戶密碼錯(cuò)誤",
statusCode: 302,
});
// 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
}
},
}),
],
pages: {
signIn: "/login",
},
});
注意
pages : {}
如果系統(tǒng)需要自定義授權(quán)登錄頁(yè),需要重寫系統(tǒng)默認(rèn)的page.signIn的值。
新增首頁(yè) pages/index.vue
<template>
<div>
<section>Login Dashboard</section>
<section>{{ session }}</section>
<section>
<el-button @click="signOut"> Sign out </el-button>
</section>
</div>
</template>
<script setup lang="ts">
const {
status,
data,
lastRefreshedAt,
getCsrfToken,
getProviders,
getSession,
signIn,
signOut
} = useAuth()
const session = await getSession()
</script>
訪問 http://localhost:3000/,系統(tǒng)將自動(dòng)自動(dòng)檢測(cè)session是否存在,如果沒有或已過期,將會(huì)自動(dòng)重定向地址到 http://localhost:3000/login,這時(shí)候我們新增登錄代碼到 login.vue 頁(yè)面。
definePageMeta({
auth: {
unauthenticatedOnly: true,
navigateAuthenticatedTo: '/'
}
})
const {
status,
data,
lastRefreshedAt,
getCsrfToken,
getProviders,
getSession,
signIn,
signOut
} = useAuth()
const state = reactive({
showPassword: false,
form: {
username: 'admin',
password: 'admin'
}
})
const form = ref(state.form)
const logIn = async (e) => {
e.preventDefault()
const { error, url } = await signIn('credentials', {
callbackUrl: '/',
redirect: false,
username: state.form.username,
password: state.form.password
})
if (error) {
ElMessage.error(error)
} else {
// No error, continue with the sign in, e.g., by following the returned redirect:
return navigateTo('/', { replace: true })
}
}
綁定登錄事件
<div class="w-full text-center">
<el-button
class="w-[90%]"
round
type="primary"
size="large"
@click="logIn"
>Sign In</el-button>
</div>
完成之后,刷新頁(yè)面,輸入賬號(hào):admin, 密碼:admin。如果不會(huì)將會(huì)攔截并提示賬號(hào)密碼錯(cuò)誤,點(diǎn)擊登錄將跳轉(zhuǎn)到 http://localhost:3000/,頁(yè)面效果如下,我們可以使用getSession 方法來獲取用戶登錄的信息。

點(diǎn)擊 Sign out 系統(tǒng)將自動(dòng)清除session并且返回到登錄頁(yè)面。