提交 9642de24 编写于 作者: W wuhanqing

ADD API CheckUserToken, RefreshUserToken

上级 cbc6757c
......@@ -36,30 +36,74 @@ func GetDefaultAvatar() string {
return "https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png"
}
func (u User) getJwt(expiresin int) string {
jwt := miniutils.NewJwt(u.Salt)
info := map[string]interface{}{
"id": u.ID,
"account": u.Account,
"avatar": u.Avatar,
func (u User) Logout(token string) error {
claims, err := u.CheckTokenGrantType(token, ACCESS_TOKEN)
if err != nil {
return err
}
u, err = u.GetUserByJwt(token, claims)
if err != nil {
return err
}
return u.UpdateSalt()
}
func (u *User) UpdateSalt() error {
u.ResetSalt()
_, err := UpdateModel(u, map[string]interface{}{"salt": u.Salt})
return err
}
func (u User) CheckTokenGrantType(token string, match string) (claims map[string]interface{}, err error) {
claims, err = u.DecodeJwt(token)
if err != nil {
return
}
grantType, ok := claims[GRANT_TYPE]
if !ok {
err = fmt.Errorf("grant_type not found in token claims")
return
}
if grantType.(string) != match {
err = fmt.Errorf("the token must be " + match)
}
return
}
func (u User) getJwt(expiresin int, info map[string]interface{}) string {
jwt := miniutils.NewJwt(u.Salt)
token, _ := jwt.Create(info, time.Second*time.Duration(expiresin))
return token
}
func (u User) GetUserByJwt(jwtStr string) (user User, err error) {
var segInfo map[string]interface{}
func (u User) DecodeJwt(jwtToken string) (map[string]interface{}, error) {
jwt := miniutils.NewJwt("")
segInfo, err = jwt.Decode(jwtStr)
return jwt.Decode(jwtToken)
}
// GetUserByJwt 根据JWT字符串获取用户对象。
// claims 可以为 nill
func (u User) GetUserByJwt(jwtStr string, claims map[string]interface{}) (user User, err error) {
if claims == nil {
claims, err = u.DecodeJwt(jwtStr)
if err != nil {
return
}
}
jsUid := claims["id"].(json.Number)
var uid int64
uid, err = jsUid.Int64()
if err != nil {
return
}
jsUid := segInfo["id"].(json.Number)
uid, _ := jsUid.Int64()
user.ID = uid
GetModel(&user) // user.Department, user.Position empty
log.Println("---FoundUser--By--Jwt---user.Salt------", user.Salt)
jwt = miniutils.NewJwt(user.Salt)
if user.Account == "" {
err = fmt.Errorf("user not found")
user = User{}
return
}
jwt := miniutils.NewJwt(user.Salt)
_, err = jwt.Parse(jwtStr)
if err != nil {
log.Println("--GetUserByJwt--Error:", err)
......@@ -67,13 +111,45 @@ func (u User) GetUserByJwt(jwtStr string) (user User, err error) {
return
}
const (
ACCESS_TOKEN = "access_token"
REFRESH_TOKEN = "refresh_token"
GRANT_TYPE = "grant_type"
)
func (u User) GetTokenExpiresIn(grantType string) int {
if grantType == ACCESS_TOKEN {
return 7200 // 有效期 2 小时
}
if grantType == REFRESH_TOKEN {
return 2592000 // 有效期 30 天. 超长有效期的refresh_token有效防止泄露用户密码
}
return 0
}
func (u User) GetAccessToken() string {
tokenInfo := map[string]interface{}{
"id": u.ID,
"account": u.Account,
GRANT_TYPE: ACCESS_TOKEN,
}
return u.getJwt(u.GetTokenExpiresIn(ACCESS_TOKEN), tokenInfo)
}
func (u User) GetRefreshToken() string {
refreshInfo := map[string]interface{}{
"id": u.ID,
"account": u.Account,
GRANT_TYPE: REFRESH_TOKEN,
}
return u.getJwt(u.GetTokenExpiresIn(REFRESH_TOKEN), refreshInfo)
}
func (u User) GetJwtInfo() JwtInfo {
expiresin := 7200 // 有效期 2 小时
refreshExpiresin := 2592000 // 有效期 30 天. 超长有效期的refresh_token有效防止泄露用户密码
return JwtInfo{
AccessToken: u.getJwt(expiresin),
RefreshToken: u.getJwt(refreshExpiresin),
Expiresin: expiresin,
AccessToken: u.GetAccessToken(),
RefreshToken: u.GetRefreshToken(),
Expiresin: u.GetTokenExpiresIn(ACCESS_TOKEN),
}
}
......@@ -108,6 +184,18 @@ func (u User) Register(password string) (User, error) {
if u.Account == "" && u.Mobile == "" {
return User{}, fmt.Errorf("登录账号不能为空")
}
if u.Account == "" {
u.Account = u.Mobile
}
if u.Mobile == "" {
u.Mobile = u.Account
}
if u.Email == "" {
u.Email = u.Account + "@example.com"
}
if u.Avatar == "" {
u.Avatar = GetDefaultAvatar()
}
user.Account = u.Account
user.Mobile = u.Mobile
user.Avatar = u.Avatar
......
......@@ -28,13 +28,13 @@ func (r Response) getHeader() []string {
fmt.Sprintf("Content-Length: %d", bodyLen),
"Connection: close", // Connection: keep-alive
"Server: easyim",
// // ALLOW CORS START
// "Access-Control-Allow-Credentials: true",
// "Access-Control-Allow-Headers: Origin, Content-Length, Content-Type, Accept, Token, Auth-Token, X-Requested-With",
// "Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, UPDATE",
// "Access-Control-Allow-Origin: *",
// "Access-Control-Expose-Headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type",
// // ALLOW CORS END
// ALLOW CORS START
"Access-Control-Allow-Credentials: true",
"Access-Control-Allow-Headers: Origin, Content-Length, Content-Type, Accept, Token, Auth-Token, X-Requested-With",
"Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, UPDATE",
"Access-Control-Allow-Origin: *",
"Access-Control-Expose-Headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type",
// ALLOW CORS END
// "Keep-Alive: timeout=4",
// "Date: Wed, 22 Feb 2023 09:58:51 GMT",
}
......
......@@ -2,6 +2,9 @@ package handler
import (
"fmt"
"time"
"encoding/json"
"github.com/iotames/easyim/database"
"github.com/iotames/easyim/model"
......@@ -11,8 +14,8 @@ var HttpApiRoute map[string]func(req *model.Request) error = map[string]func(req
"/api/user/register": userRegister,
"/api/user/login": userLogin,
"/api/user/logout": userLogout,
"/api/user/access_token": getUserAccessToken,
"/api/user/refresh_token": getUserRefreshToken,
"/api/user/check_token": checkUserToken,
"/api/user/refresh_token": userRefreshToken,
}
func HttpHandler(req *model.Request) error {
......@@ -21,7 +24,7 @@ func HttpHandler(req *model.Request) error {
if ok {
return handler(req)
}
data := map[string]interface{}{
data := model.JsonObject{
"protocol": hreq.Proto,
"method": hreq.Method,
"host": hreq.Host,
......@@ -52,7 +55,7 @@ type UserRegisterForm struct {
/**
* @api {post} /api/user/register 用户注册
* @apiName 用户注册接口
* @apiGroup 注册登录
* @apiGroup 用户相关
* @apiUse LoginParams
* @apiBody {String} nickname 用户昵称
* @apiUse PublicCommonParams
......@@ -77,7 +80,7 @@ func userRegister(req *model.Request) error {
/**
* @api {post} /api/user/login 用户登录
* @apiName 用户登录
* @apiGroup 注册登录
* @apiGroup 用户相关
* @apiUse LoginParams
* @apiUse PublicCommonParams
* @apiErrorExample {json} 请求异常示例
......@@ -118,7 +121,7 @@ func loginOrRegisterSuccess(u database.User, req model.Request) error {
data := model.JsonObject{
"id": fmt.Sprintf("%d", u.ID),
"nickname": u.Nickname,
"avatar": database.GetDefaultAvatar(),
"avatar": u.Avatar,
"access_token": jwtInfo.AccessToken,
"expires_in": jwtInfo.Expiresin,
"refresh_token": jwtInfo.RefreshToken,
......@@ -126,47 +129,140 @@ func loginOrRegisterSuccess(u database.User, req model.Request) error {
return model.ResponseApi(data, "success", 200).Write(req)
}
type UserLogoutData struct {
Account string `json:"account"`
AccessToken string `json:"access_token"`
type PostToken struct {
ResetSecret bool `json:"reset_secret"`
GrantType string `json:"grant_type"`
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
/**
* @api {post} /api/user/logout 退出登录
* @apiGroup 注册登录
* @apiGroup 用户相关
* @apiBody {String} access_token 通讯凭证
* @apiUse PublicCommonParams
* @apiErrorExample {json} 请求异常示例
* {"code":400,"msg":"access_token不正确","data":{}}
* @apiSuccessExample {json} 请求成功示例
* {}
* {"code":200,"msg":"退出登录成功","data":{}}
*/
func userLogout(req *model.Request) error {
postData := UserLogoutData{}
postData := PostToken{}
err := req.GetHttpBodyToJson(&postData)
if err != nil {
return req.ResponseJson(model.ResponseQueryArgsError(err.Error()))
}
u := database.User{}
err = u.Logout(postData.AccessToken)
if err != nil {
return model.ResponseFail(err.Error(), 400).Write(*req)
}
return model.ResponseOk("登出成功").Write(*req)
}
/**
* @api {get} /api/user/check_token 校验token
* @apiGroup 用户相关
* @apiQuery {String} token 通讯凭证: access_token 或 refresh_token 的值
* @apiSuccess {integer} code 状态码(请求成功为200)
* @apiSuccess {string} msg 请求成功提示信息
* @apiSuccess {Object} data 响应数据
* @apiError {integer} code 请求异常状态码
* @apiError {string} msg 请求异常提示信息
* @apiSuccess {Number} data.expires_in 有效期剩余时间。单位:秒
* @apiSuccess {String} data.grant_type token授权模式。值为 access_token 或 refresh_token
* @apiErrorExample {json} 请求异常示例
* {"code":400,"msg":"access_token不正确","data":{}}
* @apiSuccessExample {json} 请求成功示例
* {"code":200,"msg":"success","data":{"expires_in":7130,"grant_type":"access_token"}}
*/
func checkUserToken(req *model.Request) error {
query := req.GetHttpRequest().URL.Query()
token := query.Get("token")
u := database.User{}
u, err = u.GetUserByJwt(postData.AccessToken)
claims, err := u.DecodeJwt(token)
if err != nil {
return model.ResponseFail(err.Error(), 400).Write(*req)
}
u.ResetSalt()
_, err = database.UpdateModel(&u, map[string]interface{}{"salt": u.Salt})
grantType, ok := claims[database.GRANT_TYPE]
if !ok {
return model.ResponseFail("token 不正确", 400).Write(*req)
}
_, err = u.GetUserByJwt(token, claims)
if err != nil {
return model.ResponseServerError().Write(*req)
return model.ResponseFail(err.Error(), 400).Write(*req)
}
return model.ResponseOk("退出登录成功").Write(*req)
}
func getUserAccessToken(req *model.Request) error {
return nil
expiredAt, _ := claims["exp"].(json.Number).Int64()
expiresIn := expiredAt - time.Now().Unix()
data := model.JsonObject{
"grant_type": grantType,
"expires_in": expiresIn,
}
return model.ResponseApi(data, "success", 200).Write(*req)
}
func getUserRefreshToken(req *model.Request) error {
return nil
/**
* @api {post} /api/user/refresh_token 续期token
* @apiGroup 用户相关
* @apiSuccess {integer} code 状态码(请求成功为200)
* @apiSuccess {string} msg 请求成功提示信息
* @apiSuccess {Object} data 响应数据
* @apiError {integer} code 请求异常状态码
* @apiError {string} msg 请求异常提示信息
* @apiBody {String} grant_type 作用域. 值为: access_token或refresh_token. 告诉服务器刷新哪个token.
* @apiBody {String} refresh_token 用于刷新access_token或refresh_token
* @apiBody {Boolean} [reset_secret] 是否重置密钥。默认否false. 重置密钥后,此前使用的access_token和refresh_token都会失效。
* @apiSuccess {Number} data.expires_in 有效期(秒)。从当前时间开始算起。过完失效。
* @apiSuccess {String} data.grant_type 当前token的授权模式。值为 access_token 或 refresh_token
* @apiSuccess {String} data.token 刷新后的 access_token 或 refresh_token 的值
* @apiErrorExample {json} 请求异常示例
* {"code":400,"msg":"token不正确","data":{}}
* @apiSuccessExample {json} 请求成功示例
*{
* "code": 200,
* "msg": "success",
* "data": {
* "expires_in": 7200,
* "grant_type": "access_token",
* "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50Ijoid2hxNzgxNjQiLCJleHAiOjE2Nzc1NzYwOTksImdyYW50X3R5cGUiOiJhY2Nlc3NfdG9rZW4iLCJpZCI6MTYzMDM4MTM4ODg5NTA5NjgzMn0.2Tykk-RhXSyy7KoaxqTCEUEOausy3be_QFh-RIY0Lag"
* }
*}
*/
func userRefreshToken(req *model.Request) error {
postData := PostToken{}
err := req.GetHttpBodyToJson(&postData)
if err != nil {
return req.ResponseJson(model.ResponseQueryArgsError(err.Error()))
}
// 验证refresh_token Begin
u := new(database.User)
claims, err := u.CheckTokenGrantType(postData.RefreshToken, database.REFRESH_TOKEN)
if err != nil {
return model.ResponseFail(err.Error(), 400).Write(*req)
}
uu, err := u.GetUserByJwt(postData.RefreshToken, claims)
if err != nil {
return model.ResponseFail(err.Error(), 400).Write(*req)
}
// 验证refresh_token End
if postData.ResetSecret {
// 重置 salt, 使此前颁发的所有token失效
u = &uu
u.UpdateSalt()
}
data := model.JsonObject{
"grant_type": postData.GrantType,
"expires_in": uu.GetTokenExpiresIn(postData.GrantType),
}
if postData.GrantType == database.ACCESS_TOKEN {
data["token"] = uu.GetAccessToken()
return model.ResponseApi(data, "success", 200).Write(*req)
}
if postData.GrantType == database.REFRESH_TOKEN {
data["token"] = uu.GetRefreshToken()
return model.ResponseApi(data, "success", 200).Write(*req)
}
return model.ResponseFail("grant_type不正确", 400).Write(*req)
}
/**
......@@ -174,7 +270,7 @@ func getUserRefreshToken(req *model.Request) error {
* @apiSuccess {integer} code 状态码(请求成功为200)
* @apiSuccess {string} msg 请求成功提示信息
* @apiSuccess {Object} data 响应数据
* @apiSuccess {integer} code 请求异常状态码
* @apiError {integer} code 请求异常状态码
* @apiError {string} msg 请求异常提示信息
*/
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册