在小程序, 根据实践和资料 确定一个事实: 小程序没有Session机制, 但小程序可通过'标识'来实现登陆状态
在微信小程序中,我们可能涉及到以下三类登录方式:
- 自有的账号注册和登录
- 使用其他第三方平台账号登录
- 使用微信账号登录(即直接使用当前已登录的微信账号来作为小程序的用户进行登录)
先做一个测试, 获取SessionID:
@Controller
public class GetSessionTestController {
@RequestMapping("/getSession")
public void getSessionTest(HttpServletRequest request) {
HttpSession session = request.getSession();
System.out.println(session.getId());
}
}
使用小程序多次发如下请求, 得到SessionID都不相同
wx.request({
url: 'http://localhost:4444/getSession',
})
使用浏览器多次发如下请求, 得到的SessionID是相同的
http://localhost:4444/getSession
原理是这样的: 使用浏览器发请求, SessionID会来回传递, 使用的是同一个Session, 所以SessionID相同
而使用小程序发请求, 就不一样了, 小程序与开发者服务器之间还有微信服务器
所以它不是单纯地小程序与开发者服务器之间来回传递, 因此小程序与开发者服务器之间的SessionID动态变化的
如下是第三种方式(使用微信账号)登陆流程:
1.获取临时登陆凭证
小程序使用开放接口: wx.login() 先获取用户临时登陆凭证code
临时登陆凭证是一个用户进入小程序时, 小程序分配给用户的一个"号码牌" (以下称为号码牌)
这个号码牌, 只能使用一次, 使用后就过期
//app.js
App({
onLaunch: function () {
// 登录
wx.login({
success: res => {
console.log("res.code : " + res.code);
// 下一步: 发送 res.code 到后台换取 openId, sessionKey, unionId
}
})
)}
我把这个值打印出来: 033uxPHm0nXOgk1WjcEm0uiDHm0uxPHX
当另一个用户进入该小程序时, 这个号码牌显然是不一样的
2.发起网络请求
当小程序获取到这个号码牌, 则需要向开发者服务器发起请求
wx.login({
success: res => {
if (res.code) {
//发起网络请求
wx.request({
url: 'https://xxx/xxx', // 开发者服务器的登陆url
data: {
code: res.code // 发送的数据是号码牌
}
})
} else {
console.log('登录失败!' + res.errMsg)
}
}
})
3.换取session_key 和 openid
当开发者服务器获取到了号码牌, 需要对其进行校验, 地址:
这个请求地址是在开发者服务器中发送
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
请求的数据:
参数: appid | 必填 | 小程序唯一标识 |
参数: secret | 必填 | 小程序的 app secret |
参数: js_code | 必填 | 登录时获取的 code |
参数:grant_type | 必填 | 填写为 authorization_code |
校验成功时返回的json, 包括session_key和openid, 还可能包含UnionID
失败时返回的json, 包括errorcode和errmsg
session_key是会话密钥(开发者服务器应该对其保密)
openid是用户唯一的标识(标识的是一个微信号)
UnionID是用户在开放平台的唯一标识符(只有在满足UnionID的下发条件才能获取到)
{ //正常返回的JSON数据包
"openid": "OPENID",
"session_key": "SESSIONKEY",
}
{ //满足UnionID返回条件时,返回的JSON数据包
"openid": "OPENID",
"session_key": "SESSIONKEY",
"unionid": "UNIONID"
}
{ //错误时返回JSON数据包(示例为Code无效)
"errcode": 40029,
"errmsg": "invalid code"
}
4.生成一个会话标识
得到openid和session_key后, 还需要根据openid生成一个"会话标识" - session_token, 返回给小程序
可以这么做:
为用户生成一个唯一的字符串作为键,可以将session_key和openid作为值,存入redis中,并设置超时时间
5. 在处理需要登陆的业务时, 小程序发起的请求需要带上session_token,后端校验:
.储存session_token:
success: res => {
wx.setStorageSync("session_token", res.header.session_token);
}
.获取session_token:
var s = wx.getStorageSync("session_token");
.在时候带上session_token(可以放在header中传递):
click : function() {
var session_token = wx.getStorageSync("session_token");
wx.request({
url: 'http://127.0.0.1:4040/get3rdSession',
header: {
"session_token" : session_token
}
})
}
.开发者服务器中校验:
@RequestMapping("/isLogin")
public Object isLogin(HttpServletRequest request) throws Exception {
// 从header中取出session_token
String session_token = request.getHeader("session_token");
// 校验等操作, 如果没有登陆, 则..., 如果登陆了则...
}
// 有所执着