小程序之登录

一、登录流程

技术图片

  • 小程序内通过wx.login接口获得code
  • code传入后台,后台对微信服务器发起一个https请求换取openidsession_key(解密encryptedDataiv得到的)
  • 后台生成一个自身的3rd_session(以此为key值保持openidsession_key),返回给前端。PS:微信方的openidsession_key并没有发回给前端小程序
  • 小程序拿到3rd_session之后保持在本地
  • 小程序请求登录区内接口,通过wx.checkSession检查登录态,如果失效重新走上述登录流程,否则待上3rd_session到后台进行登录验证

通过上面wx.loginwx.getUserInfo两个api拿到相应的信息,并通过上方接口传给自己的服务器.

登录获取用户信息

1
2
3
4
5
6
7
wx.login({
success(res){
console.log(res)

//errMsg:"login:ok"
}
})
1
2
3
4
5
wx.getUserInfo({
success(res){
console.log(res)
}
})

返回的信息

userInfo

需要传输的信息有7个参数

1
2
3
4
5
6
7
8
9
10
11
appid 小程序唯一标识
secret 小程序的 app secret
js_code //wx.login登录时获取的 code,用于后续获取session_key

//下面两个参数用户服务器端签名校验用户信息的
signature 使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息。
rawData 不包括敏感信息的原始数据字符串,用于计算签名。

//下面两个参数是用于解密获取openId和UnionId的
encryptedData 包括敏感数据在内的完整用户信息的加密数据
iv 加密算法的初始向量
  • 可精简为以下三个参数.
  • 其余的签名校验的参数可省略,而appidsecret可以直接写在服务器.
1
2
3
js_code // wx.login登录时获取的 code,用于后续获取session_key
encryptedData 包括敏感数据在内的完整用户信息的加密数据
iv 加密算法的初始向量

服务端处理返回token、sessionId过程省略…

二、登录态校验

主要用到checkSession

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
wx.checkSession({
success: (res) => {
console.log('warning wx.checkSession OK, but no viewerId', res);
},
fail: (res) => {
console.log('wx.checkSession failed:', res);
},
complete: () => {
wx.login({
success: (res) => {
console.log('wx.login success:', res);
// 登录自有系统
API.login.wechat({
js_code: res.code
}, d => {
console.log('private login response:', d);
if (d.code === 0) {
console.log('private login success:', d);

let viewerId = d.data.user.user_id;
_m.globalData.viewerId = viewerId;

wx.setStorageSync('user_id', viewerId);

callback && callback();
} else {
console.error('get user_id error');
}
}, {
ignoreError: true
});
},
fail: (res) => {
console.log('wx.login failed:', res);
}
});
}
});

三、完整登录代码示例

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
const CONFIG = require('./config.js')
App({
globalData:{
viewerId:null,
userInfo:null
大专栏  小程序之登录 },
onLaunch(){
// 注册当前用户
this.register()
},
login: function(callback) {
let _m = this

// 开发环境重复使用就好
if (!viewerId && CONFIG.IS_DEBUG) {
viewerId = wx.getStorageSync('user_id');
}

// 先检查是否有登录态,且获取过用户数据;否则触发一次登录
if (viewerId) {
_m.globalData.viewerId = viewerId;
callback && callback();
} else {
wx.checkSession({
success: (res) => {
console.log('warning wx.checkSession OK, but no viewerId', res);
},
fail: (res) => {
console.log('wx.checkSession failed:', res);
},
complete: () => {
wx.login({
success: (res) => {
console.log('wx.login success:', res);
// 登录自有系统
API.login.wechat({
js_code: res.code
}, d => {
console.log('private login response:', d);
if (d.code === 0) {
console.log('private login success:', d);

let viewerId = d.data.user.user_id;
_m.globalData.viewerId = viewerId;

wx.setStorageSync('user_id', viewerId);

callback && callback();
} else {
console.error('get user_id error');
}
}, {
ignoreError: true
});
},
fail: (res) => {
console.log('wx.login failed:', res);
}
});
}
});
}
},
register: function(needTry, callback){
!callback && (callback = function(){});

this.login(()=>{
// 如果曾经授权过,则不用再请求了
/*try {
let registedTime = wx.getStorageSync('REGISTED.'+ this.globalData.viewerId);
// 7天内授权过的不再请求,不再更新资料
if (registedTime && ((new Date).getTime()-registedTime) < 604800000) {
callback();
return;
}
} catch (e) {}*/

wx.getUserInfo({
success: (res) => {
let params = {};

this.globalData.userInfo = res.userInfo;
params.owner = {
id: this.globalData.viewerId,

connected_profile: {
nickname : res.userInfo.nickName||'', // 用户昵称
profile_pic_url: res.userInfo.avatarUrl||'', // 头像, avatarUrl
language : res.userInfo.language||'', // 语言, "zh_TW"
gender : res.userInfo.gender,
geo: {
country : res.userInfo.country,
province: res.userInfo.province,
city : res.userInfo.city
}
}
}
API.profile.update(params, (d) => {
// 静默注册
if(d.code === 0) {
try {
wx.setStorageSync('USERINFO.'+ this.globalData.viewerId, this.globalData.userInfo);
wx.setStorageSync('REGISTED.'+ this.globalData.viewerId, (new Date).getTime());
} catch (e) {}

callback();
}
}, {
ignoreError: true
});
},
fail: () => {
console.log('get user info failed: not authorized.', arguments);

// 强制弹一次授权
if (needTry) {
wx.openSetting({
success: (res)=> {
if (res.authSetting['scope.userInfo']) {
wx.showToast({
title: LANG.AuthorizeSuccess,
duration: CONFIG.SHOWTOAST_DURATION,
});
}
},
fail: (res)=> {
console.log('user not permit to authorize.', arguments);
}
});
}
},
withCredentials: false // 不包含openid 等敏感信息
});
});
},
init: function(callback) {
this.login(()=>{
// 塞入常规环境数据
let pageInstance = this.getCurrentPageInstance(),
context, screenWidth, screenHeight;

/*if (this.globalData.device.system_info) {
screenWidth = this.globalData.device.system_info.screen_width;
screenHeight = this.globalData.device.system_info.screen_height;
} else {
let systemInfo = wx.getSystemInfoSync();
if (systemInfo) {
screenWidth = systemInfo.screenWidth;
screenHeight = systemInfo.screenHeight;
}
}*/
context = {
LANG : LANG,
CDN : CONFIG.CDN_HOST,
isNoContent : false,
HashtagType : CONFIG.HashtagType,
VerbType : CONFIG.VerbType,
GridImageWidthMode : CONFIG.GridImageWidthMode,
STICKER_MAKER_ENABLED: CONFIG.STICKER_MAKER_ENABLED,
UGC_ENABLED : CONFIG.UGC_ENABLED,
UGC_IMAGE_COUNT_LIMIT: CONFIG.UGC_IMAGE_COUNT_LIMIT,
ReviewStateText : CONFIG.ReviewStateText,

networkType : this.globalData.device.network ? this.globalData.device.network.network_type : NetworkType.UNKNOWN,

IS_DEV : CONFIG.IS_DEV,
IS_SHOW_CONSOLE : CONFIG.IS_SHOW_CONSOLE,
DEBUG_DATA : [],

// 全部配置都放开读
CONFIG : CONFIG,

videoPlayStatus : {},

CURRENT_PAGE : pageInstance.data.PAGE,

hideVideo : false, // 因为小程序中video不能被任何元素遮挡,所以增加此变量,用于一些浮层展示时,隐藏视频

updated_time : (new Date).getTime() // 页面上次更新时间
};

pageInstance.setData({
context: context
});

this.sendLaunchEvent();

callback && callback();
})
}
})

相关文章