登录流程
大致流程
- 前端调用wx.login获取随机code,再调用后端接口传递code。注意:code是临时的,只有5分钟的使用时间,而且只能使用一次
- 后端用获取的code与微信接口服务换取openid(用户唯一标识)与session_key(可以用于解密私密信息encrydata,现在只能获取头像和昵称),关联openid和session_key自定义登录态session,利用session生成token
- 将token返回给前端
- 前端缓存token
- 用户登录时,登录接口获取到token,再调用其他接口时,拦截器进行拦截,如果token有效,则放行请求;如果token失效(不存在、过期、格式不正确等原因),则无法访问该接口,需要重新登录。
代码
小程序代码
// 通过wx.login获取临时登录code
wx.login({
success (res) {
if (res.code) {
//发起网络请求
wx.request({
url: 'https://后端服务器/Login',
data: {
code: res.code
},
dataType:"json",
// 获取成功将后端生成的token存入storage,之后发起请求都携带token
success:function(res){
result = res.data.result
wx.setStorageSync('token', result.token)
//页面跳转 ...
},
})
} else {
console.log('登录失败!' + res.errMsg)
}
}
})
获取appid和密钥
在注册微信开发者账号后,可以在微信小程序管理后台获取appid和密钥,保存在配置文件或者系统环境中
使用Get请求调用微信服务接口
https://api.weixin.qq.com/sns/jscode2session?
//返回值
{
"session_key": "xxxxx",
"openid": "xxxxx"
}
go后端代码
v1.POST("/api/v1/user/login", api.UserLogin) //用户登陆 2
// UserLogin 用户登陆接口
// 前端点击登录按钮获取用户信息,没有从存储器找到用户信息,就携带空token到后端服务器的校验token接口,后端服务器的校验接口中间件发现没有token,就返回前端没有token的代码
// 1 无token就发起请求并携带生成的有效期验证码和用户信息发送给后端服务器 3 后端服务器携带配置文件中的小程序密钥和有效期验证码发送给微信授权服务器 4 检验成功后返回给后端服务器唯一的openid
// 5 服务器拿到openid就把用户信息存入数据库并生成token返回给前端
// 6 前端把token解析出来用户信息并存储在存储器
// 7 下次前端校验到token直接加载用户信息,不用再登录直到token过期失效
func UserLogin(c *gin.Context) {
session := sessions.Default(c)
status := 200
userID := session.Get("userId")
code := c.Request.Header.Get("AuthCode")
var loginService service.UserLoginService
if err := c.ShouldBind(&loginService); err == nil {
res := loginService.Login(userID, code, status)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
logging.Info(err)
}
}
// 用户登陆
func (service *UserLoginService) Login(userID interface{}, wx_code string, status int) serializer.Response {
var user model.User
code := e.Success
// 微信服务器接口
url := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", conf.AppID, conf.Secret, wx_code)
var session WxSession
client := &http.Client{}
// 使用Get请求调用微信服务接口
request, err := http.NewRequest("GET", url, nil)
if err != nil {
return serializer.Response{
Status: e.ErrorCodeReq,
Msg: e.GetMsg(e.ErrorCodeReq),
}
}
response, _ := client.Do(request)
body, err := ioutil.ReadAll(response.Body)
if err := json.Unmarshal(body, &session); err != nil {
return serializer.Response{
Status: e.ErrorCodeResp,
Msg: e.GetMsg(e.ErrorCodeResp),
}
}
if session.Errcode != 0 {
return serializer.Response{
Status: e.ErrorCodeOrder,
Data: nil,
Msg: session.Errmsg,
}
}
fmt.Println(session.OpenID)
err = model.DB.Where("open_id=?", session.OpenID).Find(&user).Error
//如果查询不到,就新建用户
if err != nil {
user.Avatar = service.Avatar
user.UserName = service.UserName
user.OpenID = session.OpenID
if err := model.DB.Create(&user).Error; err != nil {
logging.Info(err)
code = e.ErrorDatabase
return serializer.Response{
Status: code,
Msg: e.GetMsg(code),
Error: err.Error(),
}
}
}
// 返回生成的token
token, err := util.GenerateToken(user.ID, service.UserName, session.OpenID, 0)
if err != nil {
logging.Info(err)
code = e.ErrorAuthToken
return serializer.Response{
Status: code,
Msg: e.GetMsg(code),
}
}
return serializer.Response{
Status: code,
Data: serializer.TokenData{User: serializer.BuildUser(user), Token: token},
Msg: e.GetMsg(code),
}
}