Chuyển đến nội dung chính

第 13 課:身分驗證與授權

nuxt-auth-utils,sidebase/nuxt-auth。 JWT、OAuth2(Google、GitHub)。會話管理、基於 cookie 的身份驗證。受保護的路由,RBAC。集成了 Lucia Auth。

💻 程式設計 — 第 13 課 第 13 課:身分驗證與授權

Vue.js 和 Nuxt:從基礎到高級

第 4 部分:Nuxt 高級和全端

亞洲開發網

1. nuxt-auth-utils

npx nuxi module add nuxt-auth-utils
// server/routes/auth/github.get.ts
export default defineOAuthGitHubEventHandler({
  config: {
    emailRequired: true,
  },
  async onSuccess(event, { user, tokens }) {
    await setUserSession(event, {
      user: {
        id: user.id.toString(),
        name: user.login,
        email: user.email,
        avatar: user.avatar_url,
      },
    })
    return sendRedirect(event, '/dashboard')
  },
  onError(event, error) {
    console.error('GitHub OAuth error:', error)
    return sendRedirect(event, '/login?error=oauth')
  },
})

// server/routes/auth/google.get.ts
export default defineOAuthGoogleEventHandler({
  async onSuccess(event, { user }) {
    await setUserSession(event, {
      user: {
        id: user.sub,
        name: user.name,
        email: user.email,
        avatar: user.picture,
      },
    })
    return sendRedirect(event, '/dashboard')
  },
})

2. 會話管理

<script setup lang="ts">
const { loggedIn, user, session, clear } = useUserSession()
</script>

<template>
  <div v-if="loggedIn">
    <img :src="user?.avatar" :alt="user?.name" class="w-8 h-8 rounded-full" />
    <span>{{ user?.name }}</span>
    <button @click="clear()">Đăng xuất</button>
  </div>
  <div v-else>
    <a href="/auth/github">Login with GitHub</a>
    <a href="/auth/google">Login with Google</a>
  </div>
</template>

3. 憑證登入

// server/api/auth/login.post.ts
import bcrypt from 'bcryptjs'

export default defineEventHandler(async (event) => {
  const { email, password } = await readBody(event)

  const user = await db.query.users.findFirst({
    where: eq(users.email, email),
  })

  if (!user || !await bcrypt.compare(password, user.hashedPassword)) {
    throw createError({ statusCode: 401, message: 'Email hoặc mật khẩu sai' })
  }

  await setUserSession(event, {
    user: { id: user.id, name: user.name, email: user.email, role: user.role },
  })

  return { success: true }
})

4. 保護路線

// server/utils/auth.ts
export async function requireAuth(event: H3Event) {
  const session = await getUserSession(event)
  if (!session.user) {
    throw createError({ statusCode: 401, message: 'Unauthorized' })
  }
  return session.user
}

export async function requireRole(event: H3Event, role: string) {
  const user = await requireAuth(event)
  if (user.role !== role) {
    throw createError({ statusCode: 403, message: 'Forbidden' })
  }
  return user
}

// server/api/admin/users.get.ts
export default defineEventHandler(async (event) => {
  await requireRole(event, 'admin')
  return db.query.users.findMany()
})

5. 認證中介軟體

// middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
  const { loggedIn } = useUserSession()

  if (!loggedIn.value) {
    return navigateTo(`/login?redirect=${to.fullPath}`)
  }
})

// middleware/guest.ts — redirect if already logged in
export default defineNuxtRouteMiddleware(() => {
  const { loggedIn } = useUserSession()

  if (loggedIn.value) {
    return navigateTo('/dashboard')
  }
})
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    session: {
      maxAge: 60 * 60 * 24 * 7, // 7 days
    },
    oauth: {
      github: {
        clientId: '',
        clientSecret: '',
      },
      google: {
        clientId: '',
        clientSecret: '',
      },
    },
  },
})

下一篇: Nuxt 中的資料庫和 ORM — 細雨 ORM、遷移、CRUD。