Auto SignIn with Vuex and Vuex-persistedstate

一曲冷凌霜 提交于 2020-01-22 17:07:05

问题


I would like to auto-sign-in user when the page has been refreshed. I've read that I should use vuex-persistedstate to persist the token in localstorage. Here's my vuex store:

store: {
    user: null
},
actions: {
    autoSignIn ({commit}, payload) {
      commit('setUser', { id: payload.token })
    }
},
mutations: {
    setUser (state, payload) {
      state.user = payload;
    }
},
plugins: [ createPersistedState({
  getState: (key) => localStorage.getItem(key),
  setState: (key, state) => localStorage.setItem('user_token', key)
 }) ]

I also have signIn action where I create a newUser with token.

signUserIn ({commit, getters, state}, payload) {
  let data = {
    _username: payload.email,
    _password: payload.password
  }
  Vue.http.post(
    'url',
      data,
    { channel: 'default' },
    { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
  ).then(response => {
      const newUser = {
        id: response.body.token
      }
      localStorage.setItem('user_token', response.body.token)
      commit('setUser', newUser)
  })
}

Then in main.js - created() I would like to check if the token is valid, afterwards - sign user in.

created() {
    let token = localStorage.getItem('user_token')
    if(token) {
      this.$store.dispatch('autoSignIn', token)
    }
}

The last part doesn't work, I know I should use getState, setState from createPersistedState but I have no idea how to do it. How do I make it work?


回答1:


If the only use case for using vuex-persistedstate is to remember the access token then you should avoid using it in the first place and save yourself a few Kb from the final build file.

It would make more sense using it if you were to provide offline experience to your users.

If all you do is set state.user with the locally stored token then you could just do.

// if localStorage contains a serialized object with a 'token' attribute
const userToken = JSON.parse(window.localStorage.getItem('user_token'));

const state = {
    user: userToken ? userToken.token || null : null
};

const mutations = {};
const actions = {};

export default {
   state,
   mutations,
   actions,
}

Whenever you refresh the page and the store is being instantiated state.user will either take as default value the locally stored token or null if missing/undefined

However if i were you i would replace

const state = {
    user: null
};

with

const state = {
    accessToken: null
};

since all you store is the accessToken and not the user itself so its kind misleading.

update to answer the question in comments "... I need to check if the state has changed and use setUser mutation but don't how to achieve it."

There are 3 ways I can think of.

first of all change state to

const userToken = JSON.parse(window.localStorage.getItem('user_token'));

const state = {
    accessToken: userToken ? userToken.token || null : null,
    user: null,
};

then

The Simplest of all

on your App.vue component add a mounted method like the following

import { mapState, mapActions } from 'vuex';

export default {
    ...
    computed: {
        ...mapState([
           'accessToken',
           'user',
        ])
    },
    mounted() {
        if (this.accessToken && !this.user)
            this.getAuthUser();
    },
    methods: {
        ...mapActions([
            'getAuthUser',
        ]),
    },
}

So on every refresh when the App is mounted and we have an accessToken but not a user we call getAuthUser() action which makes an ajax call and stores the received user with a setUser mutation

The Router Guard way

If you have a router and you only need to check for an authenticated user on certain routes then you can use route guards. for example

import store from '@/store';

export default new Router({
    routes: [
        ...
        {
            path: '/admin',
            component: Admin,
            beforeEnter: (to, from, next) => {
                if (!store.state.accessToken) return next('/login');
                if (store.state.accessToken && !store.state.user) {
                    return store.dispatch('getAuthUser')
                        .then(() => {
                            // user was retrieved and stored and
                            // we can proceed
                            next();
                        })
                        .catch(() => {
                            // we couldn't fetch the user maybe because the token
                            // has expired.
                            // We clear the token
                            store.commit('accessToken', null);

                            // And go to login page
                            next('/login');
                        });
                },
                return next();
            },
        },
        ...
    ],
});

Using Vuex plugins

This is a method I've recently learned.

const storeModerator = (store, router) {
    // listen to mutations
    store.subscribe(({ type, payload }, state) => {
        // if commit('setAccessToken') was called dispatch 'getAuthUser'
        if (type === 'setAccessToken') {
            store.dispatch('getAuthUser');
        }
    });
};

export default new Vuex.Store({
    ...,
    plugins: [storeModerator]
});

You can learn more by checking:

Vue-router navigation guards

Vuex Plugins

Decouple Vuex modules with the Mediator pattern



来源:https://stackoverflow.com/questions/51897898/auto-signin-with-vuex-and-vuex-persistedstate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!