Commit 13b5ad127ae2c5dfd47a289ad68e8ce66da7a503
1 parent
e1c08c46
1.同步调整到OAuth2方式登录 2.微信小程序一键登录;
Showing
11 changed files
with
177 additions
and
62 deletions
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
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
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 | +} |