Firebase Auth 和 Vue-router
Firebase Auth and Vue-router
我正在尝试使用 firebase 对 Vue.js 应用进行身份验证。
我有一个问题,如果尝试在登录时直接访问受登录保护的 URL,路由器将在 firebase.js 有时间 [=30= 之前加载并检查身份验证状态] 授权响应。这会导致用户跳转到登录页面(当他们已经登录时)。
如何延迟 vue-router 导航,直到从 firebase 检索到授权状态?我可以看到 firebase 将 auth 数据存储在 localStorage 中,检查它是否作为初步身份验证检查是否安全?理想情况下,最终结果是在用户通过身份验证时显示加载微调器或其他内容,然后他们应该能够访问他们导航到的页面。
router/index.js
let router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/example',
name: 'Example',
component: Example,
beforeEnter: loginRequired
}
})
function loginRequired (to, from, next) {
if (authService.authenticated()) {
next()
} else {
next('/login')
}
}
auth.js
import * as firebase from 'firebase'
var config = {
// firebase config
}
firebase.initializeApp(config)
var authService = {
firebase: firebase,
user: null,
authenticated () {
if (this.user == null) {
return false
} else {
return !this.user.isAnonymous
}
},
setUser (user) {
this.user = user
},
login (email, password) {
return this.firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => {
this.setUser(user)
})
},
logout () {
this.firebase.auth().signOut().then(() => {
console.log('logout done')
})
}
}
firebase.auth().onAuthStateChanged(user => {
authService.setUser(user)
})
export default authService
app.vue
<template>
<div id="app">
<p v-if="auth.user !== null">Logged in with {{ auth.user.email }}</p>
<p v-else>not logged in</p>
<router-view v-if="auth.user !== null"></router-view>
</div>
</template>
<script>
import authService from './auth'
export default {
name: 'app',
data () {
return {
auth: authService
}
}
}
</script>
Firebase 始终会在启动时触发身份验证状态更改事件,但不会立即触发。
您需要做出 authService.authenticated
return 承诺,以便等待 Firebase 完成其 user/auth 初始化。
const initializeAuth = new Promise(resolve => {
// this adds a hook for the initial auth-change event
firebase.auth().onAuthStateChanged(user => {
authService.setUser(user)
resolve(user)
})
})
const authService = {
user: null,
authenticated () {
return initializeAuth.then(user => {
return user && !user.isAnonymous
})
},
setUser (user) {
this.user = user
},
login (email, password) {
return firebase.auth().signInWithEmailAndPassword(email, password)
},
logout () {
firebase.auth().signOut().then(() => {
console.log('logout done')
})
}
}
您不需要从 signInWith...
承诺中调用 setUser
,因为这已经由 initializeAuth
承诺处理。
我刚遇到同样的问题,最终将 Vue 对象的创建延迟到第一个 onAuthStatedChanged。
# main.js
// wait for first firebase auth change before setting up vue
import { AUTH_SUCCESS, AUTH_LOGOUT } from "@/store/actions/auth";
import { utils } from "@/store/modules/auth";
let app;
firebase.auth().onAuthStateChanged(async user => {
if (!app) {
if (user) {
await store.dispatch(AUTH_SUCCESS, utils.mapUser(user));
} else {
await store.dispatch(AUTH_LOGOUT);
}
app = new Vue({
router,
store,
i18n,
render: h => h(App)
}).$mount("#app");
}
});
然后在我的路线中,我会正常检查,如果它们最终进入登录路线,我会将它们推送到我的概览页面,这是我的仪表板页面。
#router.js
router.beforeEach((to, from, next) => {
let authenticated = store.getters.isAuthenticated;
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!authenticated) {
next({
name: "Login",
query: { redirect: to.fullPath }
});
} else {
next();
}
} else {
// doesn't require auth, but if authenticated already and hitting login then go to overview
if (authenticated && to.name === "Login") {
next({
name: "Overview"
});
}
next(); // make sure to always call next()!
}
});
基于 答案,对于那些使用常规 Vue(而非 Vuex)的人
main.js
//initialize firebase
firebase.initializeApp(config);
let app: any;
firebase.auth().onAuthStateChanged(async user => {
if (!app) {
//wait to get user
var user = await firebase.auth().currentUser;
//start app
app = new Vue({
router,
created() {
//redirect if user not logged in
if (!user) {
this.$router.push("/login");
}
},
render: h => h(App)
}).$mount("#app");
}
});
router.js
//route definitions
//...
router.beforeEach((to, from, next) => {
const currentUser = firebase.auth().currentUser;
const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
if (requiresAuth && !currentUser) {
const loginpath = window.location.pathname;
next({ name: 'login', query: { from: loginpath } });
} else if (!requiresAuth && currentUser) {
next("defaultView");
} else {
next();
}
});
您有两个选择:
1) 使用组件中的 beforeRouteEnter:
export default {
name: "example",
....
beforeRouteEnter(to, from, next){
if (authService.authenticated()) {
next()
} else {
next('/login')
}
},
}
2) 从路由器使用 beforeResolve。
router.beforeResolve((to, from, next) => {
if(to.fullPath === '/example' && !authService.authenticated()){
next('/login')
}else{
next()
}
})
要延迟授权状态,您需要做的就是
- a) 应用挂载前,使用firebase auth onAuthStateChanged 方法。
- b) 在您的路由器文件中,将元添加到要添加登录保护的父路由
- c) 使用 router.beforeEach 进行身份验证以及您希望如何重定向用户 to.I 将为以下每种情况提供示例:
阶段 a)
firebase.auth().onAuthStateChanged(function(user) {
console.log(user)
new Vue({
router,
render: h => h(App),
}).$mount('#app')
}
});
阶段 b)
....
....
....
{
path: "/dashboard",
name: "dashboard",
component: Dashboard,
meta: { requiresAuth: true },//Add this
children: [
{
path: "products",
name: "products",
component: Products,
},
],
....
....
....
阶段 c)
router.beforeEach((to, from, next) => {
const requiresAuth = to.matched.some(record => record.meta.requiresAuth)
const currentUser = firebase.auth().currentUser
if(requiresAuth && !currentUser) {
next("/")
} else if(requiresAuth && currentUser) {
next()
}else{
next()
}
})
我相信你走这条路很好。
我正在尝试使用 firebase 对 Vue.js 应用进行身份验证。
我有一个问题,如果尝试在登录时直接访问受登录保护的 URL,路由器将在 firebase.js 有时间 [=30= 之前加载并检查身份验证状态] 授权响应。这会导致用户跳转到登录页面(当他们已经登录时)。
如何延迟 vue-router 导航,直到从 firebase 检索到授权状态?我可以看到 firebase 将 auth 数据存储在 localStorage 中,检查它是否作为初步身份验证检查是否安全?理想情况下,最终结果是在用户通过身份验证时显示加载微调器或其他内容,然后他们应该能够访问他们导航到的页面。
router/index.js
let router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/example',
name: 'Example',
component: Example,
beforeEnter: loginRequired
}
})
function loginRequired (to, from, next) {
if (authService.authenticated()) {
next()
} else {
next('/login')
}
}
auth.js
import * as firebase from 'firebase'
var config = {
// firebase config
}
firebase.initializeApp(config)
var authService = {
firebase: firebase,
user: null,
authenticated () {
if (this.user == null) {
return false
} else {
return !this.user.isAnonymous
}
},
setUser (user) {
this.user = user
},
login (email, password) {
return this.firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => {
this.setUser(user)
})
},
logout () {
this.firebase.auth().signOut().then(() => {
console.log('logout done')
})
}
}
firebase.auth().onAuthStateChanged(user => {
authService.setUser(user)
})
export default authService
app.vue
<template>
<div id="app">
<p v-if="auth.user !== null">Logged in with {{ auth.user.email }}</p>
<p v-else>not logged in</p>
<router-view v-if="auth.user !== null"></router-view>
</div>
</template>
<script>
import authService from './auth'
export default {
name: 'app',
data () {
return {
auth: authService
}
}
}
</script>
Firebase 始终会在启动时触发身份验证状态更改事件,但不会立即触发。
您需要做出 authService.authenticated
return 承诺,以便等待 Firebase 完成其 user/auth 初始化。
const initializeAuth = new Promise(resolve => {
// this adds a hook for the initial auth-change event
firebase.auth().onAuthStateChanged(user => {
authService.setUser(user)
resolve(user)
})
})
const authService = {
user: null,
authenticated () {
return initializeAuth.then(user => {
return user && !user.isAnonymous
})
},
setUser (user) {
this.user = user
},
login (email, password) {
return firebase.auth().signInWithEmailAndPassword(email, password)
},
logout () {
firebase.auth().signOut().then(() => {
console.log('logout done')
})
}
}
您不需要从 signInWith...
承诺中调用 setUser
,因为这已经由 initializeAuth
承诺处理。
我刚遇到同样的问题,最终将 Vue 对象的创建延迟到第一个 onAuthStatedChanged。
# main.js
// wait for first firebase auth change before setting up vue
import { AUTH_SUCCESS, AUTH_LOGOUT } from "@/store/actions/auth";
import { utils } from "@/store/modules/auth";
let app;
firebase.auth().onAuthStateChanged(async user => {
if (!app) {
if (user) {
await store.dispatch(AUTH_SUCCESS, utils.mapUser(user));
} else {
await store.dispatch(AUTH_LOGOUT);
}
app = new Vue({
router,
store,
i18n,
render: h => h(App)
}).$mount("#app");
}
});
然后在我的路线中,我会正常检查,如果它们最终进入登录路线,我会将它们推送到我的概览页面,这是我的仪表板页面。
#router.js
router.beforeEach((to, from, next) => {
let authenticated = store.getters.isAuthenticated;
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!authenticated) {
next({
name: "Login",
query: { redirect: to.fullPath }
});
} else {
next();
}
} else {
// doesn't require auth, but if authenticated already and hitting login then go to overview
if (authenticated && to.name === "Login") {
next({
name: "Overview"
});
}
next(); // make sure to always call next()!
}
});
基于
main.js
//initialize firebase
firebase.initializeApp(config);
let app: any;
firebase.auth().onAuthStateChanged(async user => {
if (!app) {
//wait to get user
var user = await firebase.auth().currentUser;
//start app
app = new Vue({
router,
created() {
//redirect if user not logged in
if (!user) {
this.$router.push("/login");
}
},
render: h => h(App)
}).$mount("#app");
}
});
router.js
//route definitions
//...
router.beforeEach((to, from, next) => {
const currentUser = firebase.auth().currentUser;
const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
if (requiresAuth && !currentUser) {
const loginpath = window.location.pathname;
next({ name: 'login', query: { from: loginpath } });
} else if (!requiresAuth && currentUser) {
next("defaultView");
} else {
next();
}
});
您有两个选择:
1) 使用组件中的 beforeRouteEnter:
export default {
name: "example",
....
beforeRouteEnter(to, from, next){
if (authService.authenticated()) {
next()
} else {
next('/login')
}
},
}
2) 从路由器使用 beforeResolve。
router.beforeResolve((to, from, next) => {
if(to.fullPath === '/example' && !authService.authenticated()){
next('/login')
}else{
next()
}
})
要延迟授权状态,您需要做的就是
- a) 应用挂载前,使用firebase auth onAuthStateChanged 方法。
- b) 在您的路由器文件中,将元添加到要添加登录保护的父路由
- c) 使用 router.beforeEach 进行身份验证以及您希望如何重定向用户 to.I 将为以下每种情况提供示例: 阶段 a)
firebase.auth().onAuthStateChanged(function(user) {
console.log(user)
new Vue({
router,
render: h => h(App),
}).$mount('#app')
}
});
阶段 b)
....
....
....
{
path: "/dashboard",
name: "dashboard",
component: Dashboard,
meta: { requiresAuth: true },//Add this
children: [
{
path: "products",
name: "products",
component: Products,
},
],
....
....
....
阶段 c)
router.beforeEach((to, from, next) => {
const requiresAuth = to.matched.some(record => record.meta.requiresAuth)
const currentUser = firebase.auth().currentUser
if(requiresAuth && !currentUser) {
next("/")
} else if(requiresAuth && currentUser) {
next()
}else{
next()
}
})
我相信你走这条路很好。