【问题标题】:How to get something to run before vue-router如何在 vue-router 之前运行一些东西
【发布时间】:2018-12-10 10:19:21
【问题描述】:

我正在构建我的第一个 SPA,但我遇到了一些问题。它是这样设计的:

  1. Laravel 和 Laravel 视图处理登录和注册相关页面。
  2. SPA 从用户登录页面开始。
  3. 我的 app.js 定义了一个默认的 VueJS 应用程序,我在其中使用 mounted() 方法设置登录用户的状态 (VUEX)。理想情况下,它所做的只是通过 axios 调用 Laravel 后端获取用户详细信息并填充 VUEX 状态。
  4. 我在路线定义中使用beforeEnter() 方法来确保只有授权的人才能导航到路线。

这就是我面临的问题。当用户登录时,似乎在设置 vuex 之前执行了路由器。假设我有一个 url /dashboard 和 /user/1。当我尝试转到 user/1 时,如果它是在我加载应用程序之后完美运行。但是,如果我在 user/1 中刷新网页,则路由器 beforeEnter 找不到用户的 vuex 状态,因此它将用户重定向到仪表板。这是因为当路由器运行beforeEnter 时,如果是新的页面加载,它将无法访问用户 Vuex 状态或者它可以访问,但该值尚未设置。

因此,我最大的问题是我无法直接链接到路线页面,因为它总是登陆仪表板,然后用户必须转到路线才能工作。我该如何处理这种情况?

【问题讨论】:

  • 也许您应该使用本地存储 - 一个 cookie 来存储身份验证凭据,然后您将可以在 beforeEnter 方法中访问它。
  • 存在一个 cookie,其中包含用户详细信息的加密数据(默认由 Laravel 创建)。但我不确定如何使用 js 解密它。此外,我能够在前端使用 js 解密它不是安全问题吗?如果可能的话,我真的很想使用其他选项!本地存储可能是可能的,但我希望它在会话到期时过期。本地存储不是长期存储吗?
  • 在路由上,您可以将cookie发送到服务器解密并做出相应的响应。

标签: vuejs2 vuex vue-router


【解决方案1】:

这就是我最终要做的。我定义了一个用于初始化 Vue 的函数。

在 app.js 结束时,我使用 Axios 通过 Ajax 检索当前用户。在 Promise 的 then 方法中,我将 store 设置为我在 Promise 中收到的用户详细信息,然后调用我在上面为 Vue 初始化定义的函数。这样当 vue 初始化时,store 用户已经有了数据。

代码更改非常小,我不必更改现有的 axios 实现。

这是我的新实现:

Axios.get('/api/user/info')
    .then(response => {
        (new Vue).$store.commit('setUser', response.data);
        initializeVue();
    })
    .catch(error => initializeVue());

function initializeVue()
{
    window.app = new Vue({
        el: '#app',
        router,
        components: {
            UserCard,
            Sidebar,
        },
        methods: mapMutations(['setUser']),
        computed: mapState(['user']),
    });
}

【讨论】:

    【解决方案2】:

    我使用 $root 作为总线,最后求助于 VueX,这是我从我正在开发的插件中删除的一些代码,我对它进行了一些调整,以便您直接进入您的代码。 ., 应该让你走吧。

    此配置支持 VUE Cli。

    不用担心会话过期,拦截器会监视来自 Laravel 的 401 响应,以提示用户重新进行身份验证。

    放弃 bootstrap.js 中的 axios 配置,并用此设置替换它并配置 Access-Control-Allow-Origin,通配符将用于本地开发。

    axios.defaults.withCredentials = true;
    
    axios.defaults.headers.common = {
        'X-Requested-With': 'XMLHttpRequest',
        'X-CSRF-TOKEN': undefined,
        'Access-Control-Allow-Origin': '*'
    };
    
    axios.interceptors.response.use(
    
        function (response) {
    
            if(response.headers.hasOwnProperty('x-csrf-token')) {
                axios.defaults.headers['X-CSRF-TOKEN'] = response.headers['x-csrf-token'];
            }
    
            return response;
    
        },
    
        function (error) {
    
            if(typeof error !== 'object' || !error.response) {
                return Promise.reject(error);
            }
    
                if(error.response.hasOwnProperty('status')) {
    
                    switch(error.response.status) {
    
                        case 401:
                        case 419:
                            // DO RE-AUTHENTICATE CALL
                            break;
    
                    }
    
                }
    
            return Promise.reject(error);
    
        }
    
    );
    

    剩下的……

    在 main.js 中

    data() {
      return {
        user: {},
        authenticating: false
      }
    },
    
    computed: {
    
        isAuthenticated() {
            // Check a credential only an authorized user would have.
    
            if(this.$router.app.hasOwnProperty('user') === false || this.$router.app.user === null) {
                return false;
            }
    
            return this.$router.app.user.hasOwnProperty('id');
        }
    
    },
    
    methods: {
    
        checkAuth: function () {
    
            this.$set(this.$router.app, 'authenticating', true);
    
            axios.get('/auth/user').then(response => {
    
                this.$set(this.$router.app, 'user', response.data.user);
    
                if (this.$router.app.isAuthenticated()) {
    
                    this.$router.push(this.$router.currentRoute.query.redirect || '/', () => {
                        this.$set(this.$router.app, 'authenticating', false);
                    });
    
                }
    
            }).catch(error => {
    
                // TODO Handle error response
                console.error(error);
                this.$set(this.$router.app, 'user', {});
    
            }).finally(() => {
    
                this.$set(this.$router.app, 'authenticating', false);
    
            });
    
    
        },
    
        login: function (input) {
    
            axios.post('/login', input).then(response => {
    
                this.$set(this.$router.app, 'user', response.data.user);
    
                this.$router.push(this.$router.currentRoute.query.redirect || '/');
    
            }).catch(error => {
                // TODO Handle errors
                console.error(error);
            });
    
        },
    
        logout: function () {
    
            axios.post('/logout').then(response => {
    
                this.$set(this.$router.app, 'user', {});
    
                this.$nextTick(() => {
                    window.location.href = '/';
                });
    
            }).catch(error => {
                console.error(error);
            });
    
        },
    
    }
    
    beforeCreate: function () {
    
        this.$router.beforeResolve((to, from, next) => {
    
            if (to.matched.some(record => record.meta.requiresAuth) && !this.$router.app.isAuthenticated()) {
    
                next({
                    name: 'login',
                    query: {
                        redirect: to.fullPath
                    }
                });
    
                return;
            }
    
            next();
        });
    }
    

    在 Auth/LoginController.php 添加方法

    public final function authenticated(Request $request)
    {
        return response()->json([
            'user' => Auth::user()
        ]);
    }
    

    创建app/Http/Middleware/AfterMiddleware.php 只有当它发生变化而不是每次请求时,它才会传回一个新的 CSRF 令牌。 axios 拦截器将在检测到新令牌时摄取。

    <?php
    
    namespace App\Http\Middleware;
    
    use Closure;
    use Illuminate\Support\Facades\Cookie;
    
    class AfterMiddleware
    {
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            if(Cookie::get('X-CSRF-TOKEN',false) !== csrf_token())
                return $next($request)->header('X-CSRF-TOKEN',csrf_token());
    
            return $next($request);
        }
    }
    

    使用此设置,您可以有效地将静态登录表单替换为 Vue 登录表单。

    路由器设置如下:

    new Router({
        mode: 'history',
        routes: [
            {
                path: '/login',
                name: 'login',
                component: AuthLogin,
                meta: {
                    requiresAuth: false,
                    layout: 'auth'
                }
            },
            {
                path: '/login/recover',
                name: 'login-recover',
                component: AuthLoginRecover,
                meta: {
                    requiresAuth: false,
                    layout: 'auth'
                }
            },
            {
                path: '/',
                name: 'index',
                component: Dashboard,
                meta: {
                    requiresAuth: true,
                    layout: 'default'
                }
            },
            {
                path: '/settings',
                name: 'settings',
                component: Settings,
                meta: {
                    requiresAuth: true,
                    layout: 'default'
                }
            }
        ]
    });
    

    【讨论】:

    • 谢谢,但我找到了更简单的方法。
    • 没关系,我假设您正在努力实现 100% SPA,我使用此设置是因为我使用的是 VUE Cli 3 而不是 laravel-mix,并且生产中的 SPA 是由专用的 http 服务器提供的,而不是由 Laravel 直接提供服务,这样做会出现令牌问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-28
    • 1970-01-01
    • 2020-01-28
    • 1970-01-01
    相关资源
    最近更新 更多