Преглед на файлове

1.同步调整到OAuth2方式登录 2.微信小程序一键登录;

sfmind преди 2 години
родител
ревизия
13b5ad127a

+ 6 - 4
yudao-ui-app/api/auth.js

@@ -3,11 +3,13 @@ const { http } = uni.$u
 
 //使用手机 + 密码登录
 export const passwordLogin = data => http.post('/member/auth/login', data)
-//退出登录
-export const logout = data => http.post('/member/auth/logout', data)
 //发送手机验证码
 export const sendSmsCode = data => http.post('/member/auth/send-sms-code', data)
 //使用手机 + 验证码登录
 export const smsLogin = data => http.post('/member/auth/sms-login', data)
-//社交登录,使用 (手机号授权)code + 用户信息
-export const socialLogin = data => http.post('/member/auth/social-login', data)
+//微信小程序的一键登录
+export const weixinMiniAppLogin = data => http.post('/member/auth/weixin-mini-app-login', data)
+//刷新令牌
+export const  refreshToken = data => http.post('/member/auth/refresh-token', data)
+//退出登录
+export const logout = data => http.post('/member/auth/logout', data)

+ 4 - 0
yudao-ui-app/common/config.js

@@ -1,6 +1,10 @@
 module.exports = {
   //后端接口地址
   baseUrl: 'http://127.0.0.1:48080/app-api',
+  // 超时
+  timeout: 30000,
+  // 禁用 Cookie 等信息
+  withCredentials: false,
   header: {
     //租户ID
     'tenant-id': 1

+ 1 - 1
yudao-ui-app/main.js

@@ -21,6 +21,6 @@ const app = new Vue({
 })
 
 // 引入请求封装
-require('./util/request/index')(app)
+require('./utils/request/index')(app)
 
 app.$mount()

+ 22 - 7
yudao-ui-app/pages/login/mobile.vue

@@ -139,15 +139,30 @@ export default {
     },
     handleSubmit() {
       this.$refs.form.validate().then(res => {
-        this.$store.dispatch('Login', { type: this.currentModeIndex, data: this.formData }).then(res => {
-          uni.$u.toast('登录成功')
-          setTimeout(() => {
-            uni.switchTab({
-              url: '/pages/user/user'
-            })
-          }, 300)
+        uni.login({
+          provider: 'weixin',
+          success: res => {
+            let data = this.formData
+            data.socialType = 34 //WECHAT_MINI_APP 先指定固定值
+            data.socialCode = res.code
+            data.socialState = Math.random() // 该参数没有实际意义暂时传随机数
+            this.mobileLogin(data)
+          },
+          fail: res => {
+            this.mobileLogin(this.formData)
+          }
         })
       })
+    },
+    mobileLogin(data){
+      this.$store.dispatch('Login', { type: this.currentModeIndex, data: data }).then(res => {
+        uni.$u.toast('登录成功')
+        setTimeout(() => {
+          uni.switchTab({
+            url: '/pages/user/user'
+          })
+        }, 300)
+      })
     }
   }
 }

+ 3 - 3
yudao-ui-app/pages/login/social.vue

@@ -33,7 +33,7 @@ export default {
   onReady() {},
   methods: {
     getPhoneNumber(e) {
-      let code = e.detail.code
+      let phoneCode = e.detail.code
       if (!e.detail.code) {
         uni.showModal({
           title: '授权失败',
@@ -50,10 +50,10 @@ export default {
           }
         })
       } else {
-        uni.getUserInfo({
+        uni.login({
           provider: 'weixin',
           success: res => {
-            this.$store.dispatch('Login', { type: 2, data: { code: code, userData: res } }).then(res => {
+            this.$store.dispatch('Login', { type: 2, data: { phoneCode: phoneCode, loginCode: res.code } }).then(res => {
               uni.$u.toast('登录成功')
               setTimeout(() => {
                 uni.switchTab({

+ 22 - 17
yudao-ui-app/store/index.js

@@ -2,24 +2,25 @@ import Vue from 'vue'
 import Vuex from 'vuex'
 import { logout } from '@/api/auth'
 import { getUserInfo } from '@/api/user'
-import { passwordLogin, smsLogin, socialLogin } from '@/api/auth'
+import { passwordLogin, smsLogin, weixinMiniAppLogin } from '@/api/auth'
 
-const TokenKey = 'App-Token'
+const AccessTokenKey = 'ACCESS_TOKEN'
+const RefreshTokenKey = 'REFRESH_TOKEN'
 
 Vue.use(Vuex) // vue的插件机制
 
 // Vuex.Store 构造器选项
 const store = new Vuex.Store({
   state: {
-    openExamine: false, // 是否开启审核状态。用于小程序、App 等审核时,关闭部分功能。TODO 芋艿:暂时没找到刷新的地方
-    token: uni.getStorageSync(TokenKey), // 用户身份 Token
-    userInfo: {}, // 用户基本信息
-    timerIdent: false // 全局 1s 定时器,只在全局开启一个,所有需要定时执行的任务监听该值即可,无需额外开启 TODO 芋艿:需要看看
+    accessToken: uni.getStorageSync(AccessTokenKey), // 访问令牌
+    refreshToken: uni.getStorageSync(RefreshTokenKey), // 刷新令牌
+    userInfo: {}
   },
   getters: {
-    token: state => state.token,
+    accessToken: state => state.accessToken,
+    refreshToken: state => state.refreshToken,
     userInfo: state => state.userInfo,
-    hasLogin: state => !!state.token
+    hasLogin: state => !!state.accessToken
   },
   mutations: {
     // 更新 state 的通用方法
@@ -32,12 +33,14 @@ const store = new Vuex.Store({
         state[param.key] = param.val
       }
     },
-    // 更新token
+    // 更新令牌
     SET_TOKEN(state, data) {
-      // 设置 Token
-      const { token } = data
-      state.token = token
-      uni.setStorageSync(TokenKey, token)
+      // 设置令牌
+      const { accessToken, refreshToken } = data
+      state.accessToken = accessToken
+      state.refreshToken = refreshToken
+      uni.setStorageSync(AccessTokenKey, accessToken)
+      uni.setStorageSync(RefreshTokenKey, refreshToken)
 
       // 加载用户信息
       this.dispatch('ObtainUserInfo')
@@ -46,10 +49,12 @@ const store = new Vuex.Store({
     SET_USER_INFO(state, data) {
       state.userInfo = data
     },
-    // 清空 Token 和 用户信息
+    // 清空令牌 和 用户信息
     CLEAR_LOGIN_INFO(state) {
-      uni.removeStorageSync(TokenKey)
-      state.token = ''
+      uni.removeStorageSync(AccessTokenKey)
+      uni.removeStorageSync(RefreshTokenKey)
+      state.accessToken = ''
+      state.refreshToken = ''
       state.userInfo = {}
     }
   },
@@ -65,7 +70,7 @@ const store = new Vuex.Store({
           commit('SET_TOKEN', res.data)
         })
       } else {
-        return socialLogin(data).then(res => {
+        return weixinMiniAppLogin(data).then(res => {
           commit('SET_TOKEN', res.data)
         })
       }

+ 0 - 29
yudao-ui-app/util/request/responseInterceptors.js

@@ -1,29 +0,0 @@
-/**
- * 响应拦截
- * @param {Object} http
- */
-module.exports = vm => {
-  uni.$u.http.interceptors.response.use(
-    res => {
-      //对响应成功做点什么 可使用async await 做异步操作
-      //可以根据业务情况做相应的处理
-      if (res.data.code === 0) {
-        return res.data
-      } else if(res.data.code === 401) {
-        //用户未登录或登录token已过期
-        vm.$store.commit('CLEAR_LOGIN_INFO')
-      } else {
-        console.log(res)
-        //其他错误信息统一处理
-        uni.$u.toast(res.data.msg)
-        return Promise.reject(res)
-      }
-    },
-    err => {
-      //对响应错误做点什么 (statusCode !== 200)
-      console.log(err)
-      uni.$u.toast('响应错误' + err.statusCode)
-      return Promise.reject(err)
-    }
-  )
-}

+ 6 - 0
yudao-ui-app/utils/request/errorCode.js

@@ -0,0 +1,6 @@
+export default {
+  '401': '认证失败,无法访问系统资源',
+  '403': '当前操作没有权限',
+  '404': '访问资源不存在',
+  'default': '系统未知错误,请反馈给管理员'
+}

+ 14 - 0
yudao-ui-app/utils/request/index.js

@@ -0,0 +1,14 @@
+// 引入配置
+import config from '@/common/config'
+// 初始化请求配置
+uni.$u.http.setConfig((defaultConfig) => {
+    /* defaultConfig 为默认全局配置 */
+    defaultConfig.baseURL = config.baseUrl /* 根域名 */
+    defaultConfig.header = config.header
+    return defaultConfig
+})
+
+module.exports = (vm) => {
+    require('./requestInterceptors')(vm)
+    require('./responseInterceptors')(vm)
+}

+ 1 - 1
yudao-ui-app/util/request/requestInterceptors.js → yudao-ui-app/utils/request/requestInterceptors.js

@@ -9,7 +9,7 @@ module.exports = vm => {
       // 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{}
       config.data = config.data || {}
       if (vm.$store.getters.hasLogin) {
-        config.header.authorization = 'Bearer ' + vm.$store.getters.token
+        config.header.Authorization = 'Bearer ' + vm.$store.getters.accessToken
       }
       return config
     },

+ 98 - 0
yudao-ui-app/utils/request/responseInterceptors.js

@@ -0,0 +1,98 @@
+import errorCode from '@/utils/request/errorCode'
+import { refreshToken } from '@/api/auth'
+
+// 需要忽略的提示。忽略后,自动 Promise.reject('error')
+const ignoreMsgs = [
+  '无效的刷新令牌', // 刷新令牌被删除时,不用提示
+  '刷新令牌已过期' // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面
+]
+
+// 请求队列
+let requestList = []
+// 是否正在刷新中
+let isRefreshToken = false
+
+/**
+ * 响应拦截
+ * @param {Object} http
+ */
+module.exports = vm => {
+  uni.$u.http.interceptors.response.use(
+    async res => {
+      const code = res.data.code || 0
+      const msg = res.data.msg || errorCode[code] || errorCode['default']
+
+      if (ignoreMsgs.indexOf(msg) !== -1) {
+        // 如果是忽略的错误码,直接返回 msg 异常
+        return Promise.reject(msg)
+      } else if (code === 401) {
+        // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
+        if (!isRefreshToken) {
+          isRefreshToken = true
+          // 1. 如果获取不到刷新令牌,则只能执行登出操作
+          if (!vm.$store.getters.refreshToken()) {
+            vm.$store.commit('CLEAR_LOGIN_INFO')
+            return Promise.reject(res)
+          }
+          // 2. 进行刷新访问令牌
+          try {
+            const refreshTokenRes = await refreshToken()
+            // 2.1 刷新成功,则回放队列的请求 + 当前请求
+            vm.$store.commit('SET_TOKEN', refreshTokenRes.data)
+            requestList.forEach(cb => cb())
+            return uni.$u.http.request(res.config)
+          } catch (e) {
+            // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
+            // 2.2 刷新失败,只回放队列的请求
+            requestList.forEach(cb => cb())
+            // 登出。即不回放当前请求!不然会形成递归
+            vm.$store.commit('CLEAR_LOGIN_INFO')
+            return Promise.reject(res)
+          } finally {
+            requestList = []
+            isRefreshToken = false
+          }
+        } else {
+          // 添加到队列,等待刷新获取到新的令牌
+          return new Promise(resolve => {
+            requestList.push(() => {
+              res.config.header.Authorization = 'Bearer ' + vm.$store.getters.accessToken // 让每个请求携带自定义token 请根据实际情况自行修改
+              resolve(uni.$u.http.request(res.config))
+            })
+          })
+        }
+      } else if (code === 500) {
+        uni.$u.toast(msg)
+        return Promise.reject(res)
+      } else if (code === 901) {
+        uni.$u.toast('演示模式,无法进行写操作')
+        return Promise.reject(res)
+      } else if (code !== 0) {
+        if (msg === '无效的刷新令牌') {
+          // hard coding:忽略这个提示,直接登出
+          console.log(msg)
+        } else {
+          uni.$u.toast(msg)
+        }
+        return Promise.reject(res)
+      } else {
+        return res.data
+      }
+    },
+    err => {
+      console.log(err)
+      let { message } = err
+      if (!message) {
+        message = '系统发生未知错误'
+      }else if (message === 'Network Error') {
+        message = '后端接口连接异常'
+      } else if (message.includes('timeout')) {
+        message = '系统接口请求超时'
+      } else if (message.includes('Request failed with status code')) {
+        message = '系统接口' + message.substring(message.length - 3) + '异常'
+      }
+      uni.$u.toast(message)
+      return Promise.reject(err)
+    }
+  )
+}