avatar

Vue.js 接入OAuth2

Vue.js 接入OAuth2

什么是OAuth2

OAuth(开放授权)是一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAuth2.0是OAuth协议的延续版本

应用场景

  • 第三方应用授权登录:在APP或者网页接入一些第三方应用时,时长会需要用户登录另一个合作平台,比如QQ,微博,微信的授权登录。
  • 原生app授权:app登录请求后台接口,为了安全认证,所有请求都带token信息,如果登录验证、请求后台数据。
  • 前后端分离单页面应用(spa):前后端分离框架,前端请求后台数据,需要进行oauth2安全认证,比如使用vue、react后者h5开发的app。

演示代码

本机演示环境如下:
服务端: https://laravel.test (Laravel(6.x)+passport PHP环境)
客户端: http://localhost:8020 (Vue.js基础模板项目)

安装 vue-auth-client vue-axios axios

1
2
3
npm i axios
npm i vue-axios
npm i vue-auth-client

设置devServer

接入OAuth2服务端一般都涉及到跨域,需要设置下devServer,允许跨域访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module.exports = {
productionSourceMap: false,
devServer: {
host: '0.0.0.0',
port: 8020,
disableHostCheck: true,
headers: {
'Access-Control-Allow-Origin': '*'
},
proxy: {
'/v1': {
target: 'https://api.laravel.test',
secure: false,
changeOrigin: true
},
'/oauth': {
target: 'https://api.laravel.test',
secure: false,
changeOrigin: true
}
}
}
}

编辑一个axios拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*
* @Author: pengweifu
* @Date: 2020-11-08 10:10:56
* @Last Modified by: pengweifu
* @Last Modified time: 2020-11-10 14:37:03
*/
import axios from 'axios';
import store from '../store';

const instance = axios.create({
baseURL: '/v1',
timeout: 1000,
headers: {
'Accept': 'application/vnd.api+json',
'Content-Type': 'application/vnd.api+json',
},
});

instance.interceptors.request.use(config => {
config.headers.Authorization = store.getters.getToken;
if (!store.state.oauth2.expires_in) {
store.commit('loadOauth2');
}
store.state.loading = true;
return config;
}, error => {
return Promise.reject(error);
});

instance.interceptors.response.use(response => {
store.state.loading = false;
if (response.status === 200) {
return response.data;
}
if (response.status === 401) {
store.dispatch('login');
return;
}
return Promise.reject(response);
}, error => {
store.state.loading = false;
if (error && error.response && error.response.status === 401) {
store.dispatch('login');
return;
}
return Promise.reject(error);
});

export default instance;

定义好API接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
* @Author: pengweifu
* @Date: 2020-11-08 13:40:04
* @Last Modified by: pengweifu
* @Last Modified time: 2020-11-08 15:33:28
*/
import request from '../utils/request.js';

export function exampleList(data) {
return request({
url: '/example',
method: 'get',
data
})
}

在vuex实例化的时候,实例化vue-auth-client,并设置好参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import Vue from 'vue';
import Vuex from 'vuex';
import VueAxios from 'vue-axios';
import { VueAuthenticate } from 'vue-auth-client';
import axios from 'axios';

Vue.use(Vuex);
Vue.use(VueAxios, axios);
const vueAuth = new VueAuthenticate(Vue.prototype.$http, {
baseUrl: '', // Your API domain
providers: {
oauth2: {
url: '/oauth/token',
authorizationEndpoint: 'https://laravel.test/oauth/authorize',
clientId: '4',
responseParams: {
code: 'code',
grant_type: 'authorization_code',
client_secret: 'WOfm88oUTYQgYdCB0RhndBY7tgUYh4LGg3vP4nof',
clientId: 'client_id',
redirectUri: 'redirect_uri'
}
},
},
});

export default new Vuex.Store({
state: {
loading: false,
oauth2: {
access_token: '',
expires_in: '',
refresh_token: '',
},
},
getters: {
getToken: state => {
let oauth2 = Object.assign({}, state.oauth2)
if (!oauth2.expires_in) {
oauth2 = window.sessionStorage.getItem('oauth2')
oauth2 = oauth2 ? JSON.parse(oauth2) : state.oauth2
}
if (new Date().getTime() < oauth2.expires_in) {
return oauth2.access_token;
}
return;
},
},
mutations: {
setLoading (state, payload) {
state.loading = payload;
},
setOauth2(state, payload) {
window.sessionStorage.setItem('oauth2', JSON.stringify(payload))
state.oauth2 = payload;
},
loadOauth2(state) {
let oauth2 = window.sessionStorage.getItem('oauth2')
oauth2 = oauth2 ? JSON.parse(oauth2) : state.oauth2
state.oauth2 = oauth2;
},
},
actions: {
login (context) {
vueAuth.authenticate('oauth2').then(response => {
if (response.status === 200) {
context.commit('setOauth2', {
access_token: `${response.data.token_type} ${response.data.access_token}`,
expires_in: new Date().getTime() + response.data.expires_in * 1000,
refresh_token: response.data.refresh_token,
});
}
});
},
setLoading(context, payload) {
context.commit('setLoading', payload);
},
},
modules: {}
});

在vue渲染前检测是否有从服务端获取token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<div class="content"><router-view /></div>
</div>
</template>

<script>
export default {
beforeMount() {
if (!this.$store.getters.getToken) {
this.login();
}
},
methods: {
login () {
this.$store.dispatch('login');
},
},
}
</script>
文章作者: pengweifu
文章链接: https://www.pengwf.com/2020/11/15/web/JS-vue-Oauth2/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 麦子的博客
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论