Uniapp实现优雅且安全的登录流程

165 阅读1分钟

在Uniapp中结合uni-simple-router和Vuex实现一个优雅且安全的登录流程。

1. 使用uni-simple-router进行路由管理

配置路由守卫,检查用户登录状态:

import store from './store';

export const routes = [
  {
    path: '/index',
    component: () => import('@/pages/Index.vue'),
    beforeEnter: (to, from, next) => {
      if (store.state.user.isLoggedIn) {
        next();
      } else {
        store.commit('SET_REDIRECT_URL', to.fullPath);
        next('/login');
      }
    }
  },
  {
    path: '/login',
    component: () => import('@/pages/Login.vue')
  }
];

2. 在登录页面实现登录逻辑

// 登录页面 methods
methods: {
  login() {
    wx.login({
      success: res => {
        if (res.code) {
          uni.request({
            url: 'https://yourserver.com/api/login',
            method: 'POST',
            data: { code: res.code },
            success: res => {
              const token = res.data.token;
              this.$store.dispatch('loginSuccess', token).then(() => {
                const redirectUrl = this.$store.state.user.redirectUrl;
                if (redirectUrl) {
                  uni.redirectTo({ url: redirectUrl });
                } else {
                  uni.switchTab({ url: '/pages/index/index' });
                }
              });
            },
            fail: () => {
              console.log('获取token失败');
            }
          });
        } else {
          console.log('登录失败:' + res.errMsg);
        }
      }
    });
  }
}

3. 使用Vuex管理全局状态

定义Vuex store:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    user: {
      isLoggedIn: false,
      token: '',
      redirectUrl: ''
    }
  },
  mutations: {
    SET_TOKEN(state, token) {
      state.user.token = token;
      uni.setStorageSync('token', token);
    },
    SET_REDIRECT_URL(state, url) {
      state.user.redirectUrl = url;
    },
    LOGOUT(state) {
      state.user.isLoggedIn = false;
      state.user.token = '';
      state.user.redirectUrl = '';
      uni.removeStorageSync('token');
    }
  },
  actions: {
    loginSuccess({ commit }, token) {
      return new Promise((resolve) => {
        commit('SET_TOKEN', token);
        commit('SET_REDIRECT_URL', '');
        commit('SET_IS_LOGGED_IN', true);
        resolve();
      });
    },
    logout({ commit }) {
      commit('LOGOUT');
    }
  }
});

4. 在请求拦截器中添加token

uni.addInterceptor({
  request: (options) => {
    const token = this.$store.state.user.token;
    if (token) {
      options.header = options.header || {};
      options.header.Authorization = 'Bearer ' + token;
    }
    return options;
  },
  response: (options) => {
    if (options.statusCode === 401) {
      this.$store.dispatch('logout');
      uni.reLaunch({ url: '/pages/login/login' });
    }
    return options;
  }
});

5. 处理token刷新机制(可选)

// 在请求拦截器中处理token刷新
uni.addInterceptor({
  request: (options) => {
    const token = this.$store.state.user.token;
    if (token) {
      options.header = options.header || {};
      options.header.Authorization = 'Bearer ' + token;
    }
    return options;
  },
  response: (options) => {
    if (options.statusCode === 401) {
      // 使用refresh token刷新access token
      this.$store.dispatch('refreshToken').then(() => {
        // 重试请求
        return uni.request(options);
      }).catch(() => {
        this.$store.dispatch('logout');
        uni.reLaunch({ url: '/pages/login/login' });
      });
    }
    return options;
  }
});

6. 完善Vuex中的refreshToken action

actions: {
  refreshToken({ state, commit }) {
    return new Promise((resolve, reject) => {
      uni.request({
        url: 'https://yourserver.com/api/refresh',
        method: 'POST',
        data: { refreshToken: state.user.refreshToken },
        success: res => {
          commit('SET_TOKEN', res.data.token);
          resolve();
        },
        fail: () => {
          reject();
        }
      });
    });
  }
}