Commit 13b5ad127ae2c5dfd47a289ad68e8ce66da7a503

Authored by sfmind
1 parent e1c08c46

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

yudao-ui-app/api/auth.js
@@ -3,11 +3,13 @@ const { http } = uni.$u @@ -3,11 +3,13 @@ const { http } = uni.$u
3 3
4 //使用手机 + 密码登录 4 //使用手机 + 密码登录
5 export const passwordLogin = data => http.post('/member/auth/login', data) 5 export const passwordLogin = data => http.post('/member/auth/login', data)
6 -//退出登录  
7 -export const logout = data => http.post('/member/auth/logout', data)  
8 //发送手机验证码 6 //发送手机验证码
9 export const sendSmsCode = data => http.post('/member/auth/send-sms-code', data) 7 export const sendSmsCode = data => http.post('/member/auth/send-sms-code', data)
10 //使用手机 + 验证码登录 8 //使用手机 + 验证码登录
11 export const smsLogin = data => http.post('/member/auth/sms-login', data) 9 export const smsLogin = data => http.post('/member/auth/sms-login', data)
12 -//社交登录,使用 (手机号授权)code + 用户信息  
13 -export const socialLogin = data => http.post('/member/auth/social-login', data) 10 +//微信小程序的一键登录
  11 +export const weixinMiniAppLogin = data => http.post('/member/auth/weixin-mini-app-login', data)
  12 +//刷新令牌
  13 +export const refreshToken = data => http.post('/member/auth/refresh-token', data)
  14 +//退出登录
  15 +export const logout = data => http.post('/member/auth/logout', data)
yudao-ui-app/common/config.js
1 module.exports = { 1 module.exports = {
2 //后端接口地址 2 //后端接口地址
3 baseUrl: 'http://127.0.0.1:48080/app-api', 3 baseUrl: 'http://127.0.0.1:48080/app-api',
  4 + // 超时
  5 + timeout: 30000,
  6 + // 禁用 Cookie 等信息
  7 + withCredentials: false,
4 header: { 8 header: {
5 //租户ID 9 //租户ID
6 'tenant-id': 1 10 'tenant-id': 1
yudao-ui-app/main.js
@@ -21,6 +21,6 @@ const app = new Vue({ @@ -21,6 +21,6 @@ const app = new Vue({
21 }) 21 })
22 22
23 // 引入请求封装 23 // 引入请求封装
24 -require('./util/request/index')(app) 24 +require('./utils/request/index')(app)
25 25
26 app.$mount() 26 app.$mount()
yudao-ui-app/pages/login/mobile.vue
@@ -139,15 +139,30 @@ export default { @@ -139,15 +139,30 @@ export default {
139 }, 139 },
140 handleSubmit() { 140 handleSubmit() {
141 this.$refs.form.validate().then(res => { 141 this.$refs.form.validate().then(res => {
142 - this.$store.dispatch('Login', { type: this.currentModeIndex, data: this.formData }).then(res => {  
143 - uni.$u.toast('登录成功')  
144 - setTimeout(() => {  
145 - uni.switchTab({  
146 - url: '/pages/user/user'  
147 - })  
148 - }, 300) 142 + uni.login({
  143 + provider: 'weixin',
  144 + success: res => {
  145 + let data = this.formData
  146 + data.socialType = 34 //WECHAT_MINI_APP 先指定固定值
  147 + data.socialCode = res.code
  148 + data.socialState = Math.random() // 该参数没有实际意义暂时传随机数
  149 + this.mobileLogin(data)
  150 + },
  151 + fail: res => {
  152 + this.mobileLogin(this.formData)
  153 + }
149 }) 154 })
150 }) 155 })
  156 + },
  157 + mobileLogin(data){
  158 + this.$store.dispatch('Login', { type: this.currentModeIndex, data: data }).then(res => {
  159 + uni.$u.toast('登录成功')
  160 + setTimeout(() => {
  161 + uni.switchTab({
  162 + url: '/pages/user/user'
  163 + })
  164 + }, 300)
  165 + })
151 } 166 }
152 } 167 }
153 } 168 }
yudao-ui-app/pages/login/social.vue
@@ -33,7 +33,7 @@ export default { @@ -33,7 +33,7 @@ export default {
33 onReady() {}, 33 onReady() {},
34 methods: { 34 methods: {
35 getPhoneNumber(e) { 35 getPhoneNumber(e) {
36 - let code = e.detail.code 36 + let phoneCode = e.detail.code
37 if (!e.detail.code) { 37 if (!e.detail.code) {
38 uni.showModal({ 38 uni.showModal({
39 title: '授权失败', 39 title: '授权失败',
@@ -50,10 +50,10 @@ export default { @@ -50,10 +50,10 @@ export default {
50 } 50 }
51 }) 51 })
52 } else { 52 } else {
53 - uni.getUserInfo({ 53 + uni.login({
54 provider: 'weixin', 54 provider: 'weixin',
55 success: res => { 55 success: res => {
56 - this.$store.dispatch('Login', { type: 2, data: { code: code, userData: res } }).then(res => { 56 + this.$store.dispatch('Login', { type: 2, data: { phoneCode: phoneCode, loginCode: res.code } }).then(res => {
57 uni.$u.toast('登录成功') 57 uni.$u.toast('登录成功')
58 setTimeout(() => { 58 setTimeout(() => {
59 uni.switchTab({ 59 uni.switchTab({
yudao-ui-app/store/index.js
@@ -2,24 +2,25 @@ import Vue from 'vue' @@ -2,24 +2,25 @@ import Vue from 'vue'
2 import Vuex from 'vuex' 2 import Vuex from 'vuex'
3 import { logout } from '@/api/auth' 3 import { logout } from '@/api/auth'
4 import { getUserInfo } from '@/api/user' 4 import { getUserInfo } from '@/api/user'
5 -import { passwordLogin, smsLogin, socialLogin } from '@/api/auth' 5 +import { passwordLogin, smsLogin, weixinMiniAppLogin } from '@/api/auth'
6 6
7 -const TokenKey = 'App-Token' 7 +const AccessTokenKey = 'ACCESS_TOKEN'
  8 +const RefreshTokenKey = 'REFRESH_TOKEN'
8 9
9 Vue.use(Vuex) // vue的插件机制 10 Vue.use(Vuex) // vue的插件机制
10 11
11 // Vuex.Store 构造器选项 12 // Vuex.Store 构造器选项
12 const store = new Vuex.Store({ 13 const store = new Vuex.Store({
13 state: { 14 state: {
14 - openExamine: false, // 是否开启审核状态。用于小程序、App 等审核时,关闭部分功能。TODO 芋艿:暂时没找到刷新的地方  
15 - token: uni.getStorageSync(TokenKey), // 用户身份 Token  
16 - userInfo: {}, // 用户基本信息  
17 - timerIdent: false // 全局 1s 定时器,只在全局开启一个,所有需要定时执行的任务监听该值即可,无需额外开启 TODO 芋艿:需要看看 15 + accessToken: uni.getStorageSync(AccessTokenKey), // 访问令牌
  16 + refreshToken: uni.getStorageSync(RefreshTokenKey), // 刷新令牌
  17 + userInfo: {}
18 }, 18 },
19 getters: { 19 getters: {
20 - token: state => state.token, 20 + accessToken: state => state.accessToken,
  21 + refreshToken: state => state.refreshToken,
21 userInfo: state => state.userInfo, 22 userInfo: state => state.userInfo,
22 - hasLogin: state => !!state.token 23 + hasLogin: state => !!state.accessToken
23 }, 24 },
24 mutations: { 25 mutations: {
25 // 更新 state 的通用方法 26 // 更新 state 的通用方法
@@ -32,12 +33,14 @@ const store = new Vuex.Store({ @@ -32,12 +33,14 @@ const store = new Vuex.Store({
32 state[param.key] = param.val 33 state[param.key] = param.val
33 } 34 }
34 }, 35 },
35 - // 更新token 36 + // 更新令牌
36 SET_TOKEN(state, data) { 37 SET_TOKEN(state, data) {
37 - // 设置 Token  
38 - const { token } = data  
39 - state.token = token  
40 - uni.setStorageSync(TokenKey, token) 38 + // 设置令牌
  39 + const { accessToken, refreshToken } = data
  40 + state.accessToken = accessToken
  41 + state.refreshToken = refreshToken
  42 + uni.setStorageSync(AccessTokenKey, accessToken)
  43 + uni.setStorageSync(RefreshTokenKey, refreshToken)
41 44
42 // 加载用户信息 45 // 加载用户信息
43 this.dispatch('ObtainUserInfo') 46 this.dispatch('ObtainUserInfo')
@@ -46,10 +49,12 @@ const store = new Vuex.Store({ @@ -46,10 +49,12 @@ const store = new Vuex.Store({
46 SET_USER_INFO(state, data) { 49 SET_USER_INFO(state, data) {
47 state.userInfo = data 50 state.userInfo = data
48 }, 51 },
49 - // 清空 Token 和 用户信息 52 + // 清空令牌 和 用户信息
50 CLEAR_LOGIN_INFO(state) { 53 CLEAR_LOGIN_INFO(state) {
51 - uni.removeStorageSync(TokenKey)  
52 - state.token = '' 54 + uni.removeStorageSync(AccessTokenKey)
  55 + uni.removeStorageSync(RefreshTokenKey)
  56 + state.accessToken = ''
  57 + state.refreshToken = ''
53 state.userInfo = {} 58 state.userInfo = {}
54 } 59 }
55 }, 60 },
@@ -65,7 +70,7 @@ const store = new Vuex.Store({ @@ -65,7 +70,7 @@ const store = new Vuex.Store({
65 commit('SET_TOKEN', res.data) 70 commit('SET_TOKEN', res.data)
66 }) 71 })
67 } else { 72 } else {
68 - return socialLogin(data).then(res => { 73 + return weixinMiniAppLogin(data).then(res => {
69 commit('SET_TOKEN', res.data) 74 commit('SET_TOKEN', res.data)
70 }) 75 })
71 } 76 }
yudao-ui-app/util/request/responseInterceptors.js deleted
1 -/**  
2 - * 响应拦截  
3 - * @param {Object} http  
4 - */  
5 -module.exports = vm => {  
6 - uni.$u.http.interceptors.response.use(  
7 - res => {  
8 - //对响应成功做点什么 可使用async await 做异步操作  
9 - //可以根据业务情况做相应的处理  
10 - if (res.data.code === 0) {  
11 - return res.data  
12 - } else if(res.data.code === 401) {  
13 - //用户未登录或登录token已过期  
14 - vm.$store.commit('CLEAR_LOGIN_INFO')  
15 - } else {  
16 - console.log(res)  
17 - //其他错误信息统一处理  
18 - uni.$u.toast(res.data.msg)  
19 - return Promise.reject(res)  
20 - }  
21 - },  
22 - err => {  
23 - //对响应错误做点什么 (statusCode !== 200)  
24 - console.log(err)  
25 - uni.$u.toast('响应错误' + err.statusCode)  
26 - return Promise.reject(err)  
27 - }  
28 - )  
29 -}  
yudao-ui-app/utils/request/errorCode.js 0 → 100644
  1 +export default {
  2 + '401': '认证失败,无法访问系统资源',
  3 + '403': '当前操作没有权限',
  4 + '404': '访问资源不存在',
  5 + 'default': '系统未知错误,请反馈给管理员'
  6 +}
yudao-ui-app/utils/request/index.js 0 → 100644
  1 +// 引入配置
  2 +import config from '@/common/config'
  3 +// 初始化请求配置
  4 +uni.$u.http.setConfig((defaultConfig) => {
  5 + /* defaultConfig 为默认全局配置 */
  6 + defaultConfig.baseURL = config.baseUrl /* 根域名 */
  7 + defaultConfig.header = config.header
  8 + return defaultConfig
  9 +})
  10 +
  11 +module.exports = (vm) => {
  12 + require('./requestInterceptors')(vm)
  13 + require('./responseInterceptors')(vm)
  14 +}
yudao-ui-app/util/request/requestInterceptors.js renamed to yudao-ui-app/utils/request/requestInterceptors.js
@@ -9,7 +9,7 @@ module.exports = vm => { @@ -9,7 +9,7 @@ module.exports = vm => {
9 // 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{} 9 // 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{}
10 config.data = config.data || {} 10 config.data = config.data || {}
11 if (vm.$store.getters.hasLogin) { 11 if (vm.$store.getters.hasLogin) {
12 - config.header.authorization = 'Bearer ' + vm.$store.getters.token 12 + config.header.Authorization = 'Bearer ' + vm.$store.getters.accessToken
13 } 13 }
14 return config 14 return config
15 }, 15 },
yudao-ui-app/utils/request/responseInterceptors.js 0 → 100644
  1 +import errorCode from '@/utils/request/errorCode'
  2 +import { refreshToken } from '@/api/auth'
  3 +
  4 +// 需要忽略的提示。忽略后,自动 Promise.reject('error')
  5 +const ignoreMsgs = [
  6 + '无效的刷新令牌', // 刷新令牌被删除时,不用提示
  7 + '刷新令牌已过期' // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面
  8 +]
  9 +
  10 +// 请求队列
  11 +let requestList = []
  12 +// 是否正在刷新中
  13 +let isRefreshToken = false
  14 +
  15 +/**
  16 + * 响应拦截
  17 + * @param {Object} http
  18 + */
  19 +module.exports = vm => {
  20 + uni.$u.http.interceptors.response.use(
  21 + async res => {
  22 + const code = res.data.code || 0
  23 + const msg = res.data.msg || errorCode[code] || errorCode['default']
  24 +
  25 + if (ignoreMsgs.indexOf(msg) !== -1) {
  26 + // 如果是忽略的错误码,直接返回 msg 异常
  27 + return Promise.reject(msg)
  28 + } else if (code === 401) {
  29 + // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
  30 + if (!isRefreshToken) {
  31 + isRefreshToken = true
  32 + // 1. 如果获取不到刷新令牌,则只能执行登出操作
  33 + if (!vm.$store.getters.refreshToken()) {
  34 + vm.$store.commit('CLEAR_LOGIN_INFO')
  35 + return Promise.reject(res)
  36 + }
  37 + // 2. 进行刷新访问令牌
  38 + try {
  39 + const refreshTokenRes = await refreshToken()
  40 + // 2.1 刷新成功,则回放队列的请求 + 当前请求
  41 + vm.$store.commit('SET_TOKEN', refreshTokenRes.data)
  42 + requestList.forEach(cb => cb())
  43 + return uni.$u.http.request(res.config)
  44 + } catch (e) {
  45 + // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
  46 + // 2.2 刷新失败,只回放队列的请求
  47 + requestList.forEach(cb => cb())
  48 + // 登出。即不回放当前请求!不然会形成递归
  49 + vm.$store.commit('CLEAR_LOGIN_INFO')
  50 + return Promise.reject(res)
  51 + } finally {
  52 + requestList = []
  53 + isRefreshToken = false
  54 + }
  55 + } else {
  56 + // 添加到队列,等待刷新获取到新的令牌
  57 + return new Promise(resolve => {
  58 + requestList.push(() => {
  59 + res.config.header.Authorization = 'Bearer ' + vm.$store.getters.accessToken // 让每个请求携带自定义token 请根据实际情况自行修改
  60 + resolve(uni.$u.http.request(res.config))
  61 + })
  62 + })
  63 + }
  64 + } else if (code === 500) {
  65 + uni.$u.toast(msg)
  66 + return Promise.reject(res)
  67 + } else if (code === 901) {
  68 + uni.$u.toast('演示模式,无法进行写操作')
  69 + return Promise.reject(res)
  70 + } else if (code !== 0) {
  71 + if (msg === '无效的刷新令牌') {
  72 + // hard coding:忽略这个提示,直接登出
  73 + console.log(msg)
  74 + } else {
  75 + uni.$u.toast(msg)
  76 + }
  77 + return Promise.reject(res)
  78 + } else {
  79 + return res.data
  80 + }
  81 + },
  82 + err => {
  83 + console.log(err)
  84 + let { message } = err
  85 + if (!message) {
  86 + message = '系统发生未知错误'
  87 + }else if (message === 'Network Error') {
  88 + message = '后端接口连接异常'
  89 + } else if (message.includes('timeout')) {
  90 + message = '系统接口请求超时'
  91 + } else if (message.includes('Request failed with status code')) {
  92 + message = '系统接口' + message.substring(message.length - 3) + '异常'
  93 + }
  94 + uni.$u.toast(message)
  95 + return Promise.reject(err)
  96 + }
  97 + )
  98 +}