我们可以根据自己的项目需求,来定制我们自己的授权登录系统。这里我推荐使用Nuxt-auth,一个包装了 React的 Next-auth的开源项目授权框架。Nuxt3版本的使用的是Sidebase
安装
npm i -D @sidebase/nuxt-auth
项目配置
Nuxt auth 新版本提供了 local
和 authjs
两种模式
local 模式,提供全自定义授权规则,需要自己开发相应的授权接口,官方提供的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, // 自动开启全局鉴权,如果没有登录自动跳到登录页面
},
}
})
新增 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: "账户密码错误",
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 : {}
如果系统需要自定义授权登录页,需要重写系统默认的page.signIn
的值。
新增首页 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/
,系统将自动自动检测session是否存在,如果没有或已过期,将会自动重定向地址到 http://localhost:3000/login
,这时候我们新增登录代码到 login.vue 页面。
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>
完成之后,刷新页面,输入账号:admin, 密码:admin。如果不会将会拦截并提示账号密码错误
,点击登录将跳转到 http://localhost:3000/,页面效果如下,我们可以使用getSession
方法来获取用户登录的信息。
image.png
点击 Sign out 系统将自动清除session并且返回到登录页面。