用户信息表
// User model
type User struct {
Id string `json:"id" form:"id" bson:"_id"` // 用户id,直接用mongo的_id
Username string `json:"username" form:"username" bson:"username"` // 登录账号
Password string `json:"password" form:"password" bson:"password"` // 密码
Nickname string `json:"nickname" form:"nickname" bson:"nickname"` // 昵称
Avator string `json:"avator,omitempty" form:"avator" bson:"avator"` // 个性化头像
Sign string `json:"sign,omitempty" form:"sign" bson:"sign"` // 签名
}
注册
// Register 注册
func Register(register *RegisterForm) (*mongo.InsertOneResult, error) {
filter := bson.M{
"username": register.Username,
}
result := app.MongoDBClient.Database(app.MongoDataBaseName).Collection("user").FindOne(context.TODO(), filter)
if err := result.Err(); err != nil {
if err == mongo.ErrNoDocuments {
return app.MongoDBClient.Database(app.MongoDataBaseName).Collection("user").InsertOne(context.TODO(), register)
}
return nil, err
}
return nil, errors.New("username exist")
}
登录
type LoginForm struct {
Username string `json:"username" form:"username" bson:"username"`
Password string `json:"password" form:"password" bson:"password"`
}
// Login 验证登录信息
func Login(filter LoginForm) (User, error) {
var user User
err := app.MongoDBClient.Database(app.MongoDataBaseName).Collection("user").FindOne(context.TODO(), filter).Decode(&user)
if err != nil {
return user, err
}
return user, nil
}
注册、登录那都常规操作咯,下面来实现 gin+jwt 的鉴权
登录信息验证通过后,会生成一个token返回给客户端,之后的访问资源需要携带token
jwt中间件 util/jwt/jwt.go
package jwt
import (
"errors"
"net/http"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
)
var (
ErrorTokenExpired error = errors.New("token is expired")
ErrorTokenNotValidYet error = errors.New("token not active yet")
ErrorTokenMalformed error = errors.New("that's not even a token")
ErrorTokenInvalid error = errors.New("couldn't handle this token")
SignKey string = "MySignKey"
)
// Auth middleware, check the token
func Auth() gin.HandlerFunc {
return func(context *gin.Context) {
// Get the token in the request context
token := context.Request.Header.Get("token")
if token == "" {
context.JSON(http.StatusOK, gin.H{
"code": 10000,
"msg": "No token, illegal access",
})
context.Abort()
return
}
jwt := NewJWT()
// parseToken 解析token包含的信息
claims, err := jwt.ParseToken(token)
if err != nil {
context.JSON(http.StatusOK, gin.H{
"code": 10001,
"msg": err.Error(),
})
context.Abort()
return
}
// Proceed to the next route and pass on the parsed information
context.Set("claims", claims)
}
}
// JWT signature structure
type JWT struct {
SigningKey []byte
}
// Create a new JWT instance
func NewJWT() *JWT {
return &JWT{
SigningKey: []byte(GetSignKey()),
}
}
// Access to signKey
func GetSignKey() string {
return SignKey
}
//
func SetSignKey(key string) string {
SignKey = key
return SignKey
}
// Payloads, you can add the information you need
type CustomClaims struct {
Name string `json:"name"`
jwt.StandardClaims
}
// CreateToken generates a token
func (j *JWT) CreateToken(claims CustomClaims) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(j.SigningKey)
}
// ParseToken
func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return j.SigningKey, nil
})
// There is a problem with token resolution
if err != nil {
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
return nil, ErrorTokenMalformed
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
// Token is expired
return nil, ErrorTokenExpired
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
return nil, ErrorTokenNotValidYet
} else {
return nil, ErrorTokenInvalid
}
}
}
if token != nil {
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
return claims, nil
}
}
return nil, ErrorTokenInvalid
}
生成token
import (
"fmt"
"im-demo/application/model"
"net/http"
"time"
myjwt "im-demo/util/jwt"
jwtgo "github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
)
func newToken(user model.LoginForm) (string, error) {
jwt := myjwt.NewJWT()
claims := myjwt.CustomClaims{
Name: user.Username,
StandardClaims: jwtgo.StandardClaims{
NotBefore: int64(time.Now().Unix() - 1000), // Effective time of signature
ExpiresAt: int64(time.Now().Unix() + 36000), // Expiration time is one hour
Issuer: "footmark", // Signed issuer
},
}
token, err := jwt.CreateToken(claims)
return token, err
}
携带token请求用户信息
gin路由,以及使用jwt中间件
// 分组路由
infoGroup := engine.Group("/info")
infoGroup.Use(jwt.Auth())
{
infoGroup.GET("/userinfo", api.GetUserInfo)
}
ajax请求,在请求头header中携带token
$.ajax({
url: "/info/userinfo",
type: "GET",
headers: {
"token": token
},
data: {
"owner_id": owner_id
},
success: function(res) {
console.log(res)
},
error: function(res) {
console.log(res)
}
})
IM即时通讯从0到1的实践,相关文章:
IM即时通讯-从0到1的实践(一)
IM即时通讯-项目框架搭建(二)
IM即时通讯-用户注册登录,及gin+JWT鉴权(三)<本文>
IM即时通讯-核心结构体设计(四)
IM即时通讯-消息id(五)
IM即时通讯-会话列表和会话信箱(六)
IM即时通讯-1.0版成果展示与后续扩展(七)