参考:
微信官方文档 * 小程序登录流程
微信官方文档 * 小程序
本文参考以上文档, 加上自己理解整合写出.
登录流程
- 前端调用
wx.login()
获取 临时登录凭证code
,并回传到开发者服务器。 - 后端调用
auth.code2Session
接口,换取 用户唯一标识OpenID
和会话密钥 session_key
。 - 开发者服务器可以根据用
户标识
来生成自定义登录态
,用于后续业务逻辑中前后端交互时识别用户身份。
上图就是小程序登录的基本流程.
作为一个后端开发人员, 看完上述描述后, 内心是这样的:
嗯, 道理我都懂, 代码怎么写?
接下来我们用go实现这个流程:
首先code
是前端获取传到后端的, 我们不用管, 只要在HTTP请求种拿到这个参数即可.
ok, 第一步的code已经拿到.
然后第二步, 利用code获取openID
和session_key
, 这里我们看微信官方文档给的接口:
到这里有接口. 有返回值, 我们就可以通过HTTP请求获取
openID
和session_key
了.
现在开始写代码实现, 我使用的是go语言, 其实道理都是一样的.
定义返回值数据结构:
type WXLoginResp struct {
OpenId string `json:"openid"`
SessionKey string `json:"session_key"`
UnionId string `json:"unionid"`
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
定义实现函数:
// 这个函数以 code 作为输入, 返回调用微信接口得到的对象指针和异常情况
func WXLogin(code string) (*WXLoginResp, error) {
url := "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"
// 合成url, 这里的appId和secret是在微信公众平台上获取的
url = fmt.Sprintf(url, appId, secret, code)
// 创建http get请求
resp,err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// 解析http请求中body 数据到我们定义的结构体中
wxResp := WXLoginResp{}
decoder := json.NewDecoder(resp.Body)
if err := decoder.Decode(&wxResp); err != nil {
return nil, err
}
// 判断微信接口返回的是否是一个异常情况
if wxResp.ErrCode != 0 {
return nil, errors.New(fmt.Sprintf("ErrCode:%s ErrMsg:%s", wxResp.ErrCode, wxResp.ErrMsg))
}
return &wxResp, nil
}
ok, 到这里, 我们已经获得了openid
和 session_key
.
接下来实现第三步, 这一步比较灵活, 根据自己的需求生成自定义登录态, 并返回前端;
这里我们用gin
框架实现一个路由接口, 将上面的内容串联起来, 形成一个完整的流程;下面的代码仅供参考
// /wechat/applet_login?code=xxx [get] 路由
// 微信小程序登录
func AppletWeChatLogin(c *gin.Context) {
code := c.Query("code") // 获取code
// 根据code获取 openID 和 session_key
wxLoginResp,err := models.WXLogin(code)
if err != nil {
c.JSON(400, util.Fail(err.Error()))
return
}
// 保存登录态
session := sessions.Default(c)
session.Set("openid", wxLoginResp.OpenId)
session.Set("sessionKey", wxLoginResp.SessionKey )
// 这里用openid和sessionkey的串接 进行MD5之后作为该用户的自定义登录态
mySession := GetMD5Encode(wxLoginResp.OpenId + wxLoginResp.SessionKey)
// 接下来可以将openid 和 sessionkey, mySession 存储到数据库中,
// 但这里要保证mySession 唯一, 以便于用mySession去索引openid 和sessionkey
c.String(200, mySession)
}
// 将一个字符串进行MD5加密后返回加密后的字符串
func GetMD5Encode(data string) string {
h := md5.New()
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
可能代码部分不太能看懂, 这里再用文字解释一下:
- 获取路由中的
code
参数 - 利用
code
调用我们之前写好的函数获取openid
和session_key
- 利用得到的
openid
和session_key
生成我们自定义的登录态(这里方式有很多, 保证生成的值全局唯一即可, 这里我们简单化了, 直接利用openid
和session_key
进行MD5加密, 将得到的字符串作为我们的登录态(这里登录态理解为索引即可, 本质是一个字符串)) - 最后将我们定义的登录态以及
openid
和session_key
存储到数据库中, 并且保证能用自定义的登录态唯一查询到该用户的openid
和session_key
. 以便我们后期用到的时候可以查询到.
到这里微信小程序登录已经基本完成了.
这里, 微信官方还提供了一个校验接口, 用于校验小程序端获取的用户信息是否完整,这个也是我们之前获得的session_key
的意义,
这里可以参考: 微信官方文档 * 后台校验与解密数据
我们也给出校验代码, 非常简单:
实际上就是将前端提供的微信原始数据和我们之前获取的session_key进行MD5加密, 得到signature2, 与微信加密的结果进行比较, 相同即为没有改变, 不同即是原始数据发生了改变.
// 校验微信返回的用户数据
func ValidateUserInfo(rawData, sessionKey, signature string) bool {
signature2 := GetSha1(rawData + sessionKey)
return signature == signature2
}
// SHA-1 加密
func GetSha1(str string) string {
data := []byte(str)
has := sha1.Sum(data)
res := fmt.Sprintf("%x", has) //将[]byte转成16进制
return res
}
以上就是微信小程序登录, 后台涉及到的所有操作.