Vue3 基于AWS Amplify实现Cognito用户认证及OAuth令牌获取
Vue3 + AWS Amplify + Cognito 认证落地方案
核心问题回应
- 完全可以在Vue3项目中单独使用AWS Amplify的认证模块,不需要引入完整的UI组件包,仅靠
@aws-amplify/auth就能完成从登录、状态检查到令牌自动刷新的全流程,目前已有大量生产项目落地该方案。 - 对接Cognito OAuth2流程不需要强制走页面重定向跳转,通过原生的Auth模块调用就能直接用用户名密码完成认证,拿到可用于调用AWS API Gateway的访问令牌,全程可在单页应用内完成交互,不需要跳出当前站点。
现有代码无法正常运行的核心原因
- 自定义元素未注册:Vue3默认会把模板里的非内置标签当成本地组件解析,你直接编写的
amplify-authenticator、amplify-sign-in这类第三方自定义标签,没有经过注册流程会直接被Vue忽略,根本不会渲染对应的UI,这就是你看不到登录、登出按钮的直接原因。 - 存储配置错误:你在配置里给
cookieStorage设置的domain是Cognito的托管域名(*.amazoncognito.com),受浏览器跨域安全规则限制,当前localhost站点根本不允许向其他第三方域名写入cookie,这会导致认证状态完全无法持久化,每次页面加载都读不到之前的登录态,始终显示未登录。 - 插件挂载方式错误:Vue3的
app.use()方法仅支持第一个参数为插件实例,后续传入的参数会被当成插件的初始化参数,而非依次注册插件。你原来的写法把Amplify、Auth作为参数传给了vuex的store插件,根本没有触发Amplify的内部初始化逻辑,相当于只导入了依赖包,没有完成真正的运行时绑定。
分步实现可正常运行的认证流程
1. 修正main.js初始化逻辑
把原有错误的配置、挂载逻辑替换为如下代码,重点修正存储配置、自定义元素规则、初始化顺序:
import { createApp } from 'vue' import App from './App.vue' import { createStore } from 'vuex' import { Amplify } from 'aws-amplify' import { Auth } from '@aws-amplify/auth' Amplify.configure({ Auth: { identityPoolId: 'eu-central-1_REST-OF-ID', region: 'eu-central-1', userPoolWebClientId: '4t49u0pu0skkREST-OF-ID', mandatorySignIn: false, // 移除指向Cognito域名的cookieStorage配置,本地开发用默认localStorage存储状态 storage: window.localStorage, authenticationFlowType: 'USER_PASSWORD_AUTH', oauth: { domain: 'PREFIX.auth.eu-central-1.amazoncognito.com', scope: ['phone', 'email', 'profile', 'openid', 'aws.cognito.signin.user.admin'], redirectSignIn: 'http://localhost:3000/', redirectSignOut: 'http://localhost:3000/', responseType: 'code' } } }) const store = createStore({ state() { return { entries: [], authState: 'loading' // 新增认证状态字段,用于控制UI渲染 } }, mutations: { addTime(state, item) { state.entries.push(item); }, setAuthState(state, payload) { state.authState = payload } } }) const app = createApp(App) // 告诉Vue所有以amplify-开头的标签是外部自定义元素,不要当成本地组件解析 app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('amplify-') app.use(store) app.mount("#app")
2. 手动实现登录交互,获取OAuth2令牌
如果不想依赖Amplify自带的UI组件,完全可以自己编写登录表单,直接调用Auth模块方法完成认证,这种方式可控性更强,也不会出现组件渲染异常的问题。
在需要的业务组件中引入Auth模块,编写核心逻辑:
import { Auth } from '@aws-amplify/auth' export default { data() { return { username: '', password: '', isLoading: false, errorMsg: '' } }, async mounted() { // 页面加载时检查已有登录态 try { const currentSession = await Auth.currentSession() // 拿到的JWT令牌直接可以用于调用API Gateway const accessToken = currentSession.getAccessToken().getJwtToken() const idToken = currentSession.getIdToken().getJwtToken() this.$store.commit('setAuthState', 'signedIn') // 将令牌存在store中,后续请求时直接读取并加到请求头 } catch (e) { this.$store.commit('setAuthState', 'signIn') } }, methods: { async handleLogin() { this.isLoading = true this.errorMsg = '' try { // 传入用户名密码完成认证 await Auth.signIn(this.username, this.password) // 登录成功后直接拉取会话,拿到全套OAuth2令牌 const session = await Auth.currentSession() const accessToken = session.getAccessToken().getJwtToken() // 调用API Gateway时,在请求头加上 `Authorization: ${accessToken}` 即可通过鉴权 this.$store.commit('setAuthState', 'signedIn') } catch (e) { this.errorMsg = e.message } finally { this.isLoading = false } }, async handleLogout() { await Auth.signOut() this.$store.commit('setAuthState', 'signIn') } } }
3. UI渲染逻辑修正
在模板中根据store存储的认证状态手动渲染登录、登出按钮,不需要依赖Amplify的authenticator组件:
<template> <div v-if="$store.state.authState === 'loading'">状态加载中...</div> <div v-else-if="$store.state.authState === 'signIn'"> <input v-model="username" placeholder="用户名" /> <input v-model="password" type="password" placeholder="密码" /> <button @click="handleLogin" :disabled="isLoading">登录</button> <p v-if="errorMsg" style="color: red">{{ errorMsg }}</p> </div> <div v-else> <p>当前已登录</p> <button @click="handleLogout">登出</button> </div> </template>
关键注意事项
- Cognito用户池的对应应用客户端必须在AWS控制台开启
USER_PASSWORD_AUTH认证流,否则调用登录接口会直接返回权限错误。 - 本地开发场景不要配置指向Cognito托管域名的cookie存储,浏览器跨域安全策略会直接阻断状态写入,用默认的localStorage存储状态是最稳定的方案。
- Auth模块内部已经自动处理了令牌刷新、过期续期的全流程,拿到的Access Token不需要额外走OAuth的code换token步骤,直接读取JWT值加到API请求头即可使用。
内容的提问来源于stack exchange,提问作者1574ad6
相关产品推荐
相关产品推荐

