Spring OAuth2社交登录与NUXT 3客户端集成及令牌管理问题
解决方案:Nuxt3 + Spring OAuth2 社交登录集成
一、认证状态管理:Pinia 是优先选择
别纠结自定义组合式函数了,直接用Pinia——它是Nuxt3官方推荐的状态管理方案,自带成熟的持久化插件,能帮你轻松搞定令牌、用户角色的跨页面存储,不用自己写本地存储逻辑,调试起来也更方便。
示例Pinia Store代码:
// stores/auth.ts import { defineStore } from 'pinia' export const useAuthStore = defineStore('auth', { state: () => ({ accessToken: null, userInfo: null, roles: [] }), actions: { setAuthData(token, user, roles) { this.accessToken = token this.userInfo = user this.roles = roles }, clearAuthData() { this.accessToken = null this.userInfo = null this.roles = [] } }, persist: true // 自动持久化到localStorage,刷新不丢状态 })
二、令牌处理:别直接从URL硬提
你现在从route.query.token拿令牌的方式太直白,还容易暴露敏感信息。优化方案分两种:
- 推荐用授权码模式:让Spring OAuth2返回授权码,前端拿授权码去后端换令牌(授权码是一次性的,比直接返回令牌安全)
- 如果后端只能用隐式模式返回令牌:拿到后立刻清理URL参数,同时验证令牌有效性
示例Nuxt插件处理逻辑:
// plugins/auth-handler.ts export default defineNuxtPlugin(async (nuxtApp) => { const authStore = useAuthStore() const route = useRoute() const router = useRouter() nuxtApp.hook('page:finish', async () => { const token = route.query.access_token if (token && !authStore.accessToken) { // 先调用后端接口验证令牌,顺便拉取用户角色 const { data: userData } = await useFetch('/api/auth/user-info', { headers: { Authorization: `Bearer ${token}` } }) if (userData.value) { authStore.setAuthData(token, userData.value, userData.value.roles) // 清理URL里的令牌参数,避免泄露 await router.push({ path: route.path, query: {} }) } } }) })
三、全局请求拦截:自动带令牌
封装一个全局请求函数,自动给所有接口加上Authorization头,同时处理令牌过期:
// composables/useApi.ts export const useApi = () => { const authStore = useAuthStore() return $fetch.create({ baseURL: '/api', onRequest({ options }) { // 请求前自动注入令牌 if (authStore.accessToken) { options.headers.Authorization = `Bearer ${authStore.accessToken}` } }, onResponseError({ response }) { // 令牌过期/无效时清空状态并跳转登录页 if (response.status === 401) { authStore.clearAuthData() navigateTo('/login') } } }) }
四、角色权限控制
用Pinia的状态封装权限判断函数,在页面/组件里直接用:
// composables/useAuthCheck.ts export const useAuthCheck = () => { const authStore = useAuthStore() return { hasRole: (role: string) => authStore.roles.includes(role), isLoggedIn: () => !!authStore.accessToken } }
页面中使用示例:
<script setup> const { hasRole } = useAuthCheck() </script> <template> <button v-if="hasRole('ADMIN')">管理员操作按钮</button> </template>
内容的提问来源于stack exchange,提问作者Oz Shemesh
相关产品推荐
相关产品推荐

