当前位置:网站首页>Beego learning - JWT realizes user login and registration
Beego learning - JWT realizes user login and registration
2022-07-03 09:14:00 【Fill your head with water】
Catalog
start-up :
bee run -gendoc=true -downdoc=true
Some notes in the article are personal understandings , May not be rigorous
Use mysql Remember to import the package if necessary :_ "github.com/go-sql-driver/mysql"
1. models package
1.1 jwt.go
// JWT : header payload signature
// json web token: header Payload Signature
const (
SecretKEY string = "JWT-Secret-Key"
DEFAULT_EXPIRE_SECONDS int = 600 // Default 10 minute
PasswordHashBytes = 16
)
// MyCustomClaims
// This struct is the payload
// This structure is the payload
type MyCustomClaims struct {
UserID int `json:"userID"`
jwt.StandardClaims
}
// JwtPayload
// This struct is the parsing of token payload
// This structure is right token Payload parsing
type JwtPayload struct {
Username string `json:"username"`
UserID int `json:"userID"`
IssuedAt int64 `json:"iat"` // Release date
ExpiresAt int64 `json:"exp"` // Expiration time
}
// GenerateToken
// @Title GenerateToken
// @Description " Generate token"
// @Param loginInfo *models.LoginRequest " Login request "
// @Param userID int " user ID"
// @Param expiredSeconds int " Expiration time "
// @return tokenString string " Encoding token"
// @return err error " error message "
func GenerateToken(loginInfo *LoginRequest, userID int, expiredSeconds int) (tokenString string, err error) {
// If the expiration time is not set , The default is DEFAULT_EXPIRE_SECONDS 600s
if expiredSeconds == 0 {
expiredSeconds = DEFAULT_EXPIRE_SECONDS
}
// Create a statement
mySigningKey := []byte(SecretKEY)
// Expiration time = current time (/s)+ expiredSeconds(/s)
expireAt := time.Now().Add(time.Second * time.Duration(expiredSeconds)).Unix()
logs.Info("Token Will expire on :", time.Unix(expireAt, 0))
user := *loginInfo
claims := MyCustomClaims{
userID,
jwt.StandardClaims{
Issuer: user.Username, // publisher
IssuedAt: time.Now().Unix(), // Release time
ExpiresAt: expireAt, // Expiration time
},
}
// Use the declaration created above Generate token
// NewWithClaims( Signature algorithm SigningMethod, Statement Claims) *Token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Using key pairs token Signature
tokenStr, err := token.SignedString(mySigningKey)
if err != nil {
return "",
errors.New(" error : token Generate failure !")
}
return tokenStr, nil
}
// ValidateToken
// @Title ValidateToken
// @Description " verification token"
// @Param tokenString string " Encoding token"
// @return *JwtPayload "Jwt Payload parsing "
// @return error " error message "
func ValidateToken(tokenString string) (*JwtPayload, error) {
// Get the before encoding token Information
token, err := jwt.ParseWithClaims(tokenString,
&MyCustomClaims{
},
func(token *jwt.Token) (interface{
}, error) {
return []byte(SecretKEY), nil
})
// obtain payload- The content of the statement
claims, ok := token.Claims.(*MyCustomClaims)
if ok && token.Valid {
logs.Info("%v %v",
claims.UserID,
claims.StandardClaims.ExpiresAt, // Expiration time
)
logs.Info("Token Will expire on :",
time.Unix(claims.StandardClaims.ExpiresAt, 0),
)
return &JwtPayload{
Username: claims.StandardClaims.Issuer, // user name : publisher
UserID: claims.UserID,
IssuedAt: claims.StandardClaims.IssuedAt,
ExpiresAt: claims.StandardClaims.ExpiresAt,
}, nil
} else {
logs.Info(err.Error())
return nil, errors.New(" error : token Validation failed ")
}
}
// RefreshToken
// @Title RefreshToken
// @Description " to update token"
// @Param tokenString string " Encoding token"
// @return newTokenString string " Coded new token"
// @return err error " error message "
func RefreshToken(tokenString string) (newTokenString string, err error) {
// Get previous token
token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{
},
func(token *jwt.Token) (interface{
}, error) {
return []byte(SecretKEY), nil
})
// Get previous token Of payload- Statement
claims, ok := token.Claims.(*MyCustomClaims)
if !ok || !token.Valid {
return "", err
}
// Create a new declaration
mySigningKey := []byte(SecretKEY)
expireAt := time.Now().Add(time.Second * time.Duration(DEFAULT_EXPIRE_SECONDS)).Unix() //new expired
newClaims := MyCustomClaims{
claims.UserID,
jwt.StandardClaims{
Issuer: claims.StandardClaims.Issuer, //name of token issue
IssuedAt: time.Now().Unix(), //time of token issue
ExpiresAt: expireAt,
},
}
// Use new statements , Generate a new token
newToken := jwt.NewWithClaims(jwt.SigningMethodHS256, newClaims)
// Use the signature algorithm for new token To sign
tokenStr, err := newToken.SignedString(mySigningKey)
if err != nil {
return "", errors.New(" error : New new json web token Generate failure !")
}
return tokenStr, nil
}
// GenerateSalt
// @Title GenerateSalt
// @Description " Generate the user's encrypted key |generate salt"
// @return salt string " Generate the user's encrypted key "
// @return err error " error message "
func GenerateSalt() (salt string, err error) {
buf := make([]byte, PasswordHashBytes)
if _, err := io.ReadFull(rand.Reader, buf); err != nil {
return "", errors.New("error: failed to generate user's salt")
}
return fmt.Sprintf("%x", buf), nil
}
// GeneratePassHash
// @Title GenerateSalt
// @Description " Encrypt password |generate password hash"
// @Param password string " User login password "
// @Param salt string " User's encrypted key "
// @return hash string " Encrypted password "
// @return err error " error message "
func GeneratePassHash(password string, salt string) (hash string, err error) {
h, err := scrypt.Key([]byte(password), []byte(salt), 16384, 8, 1, PasswordHashBytes)
if err != nil {
return "", errors.New("error: failed to generate password hash")
}
return fmt.Sprintf("%x", h), nil
}
1.2 user.go
// TabUser Define user format
type TabUser struct {
Id int `json:"id" orm:"column(id);auto"`
UserName string `json:"username" orm:"column(username);size(128)"`
Password string `json:"password" orm:"column(password);size(128)"`
Salt string `json:"salt" orm:"column(salt);size(128)"`
}
// LoginRequest Define the login request format
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
// LoginResponse Define login response
type LoginResponse struct {
Username string `json:"username"`
UserID int `json:"userID"`
Token string `json:"token"`
}
//CreateRequest Define the format for creating user requests
type CreateRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
//CreateResponse Define create user response
type CreateResponse struct {
UserID int `json:"userID"`
Username string `json:"username"`
}
// DoLogin
// @Title DoLogin
// @Description " The user login "
// @Param lr *LoginRequest " Login request "
// @return *LoginResponse " Login response "
// @return int " Status code "
// @return error " error message "
func DoLogin(lr *LoginRequest) (*LoginResponse, int, error) {
// Get user name and password
username := lr.Username
password := lr.Password
// Verify that the user name and password are empty
if len(username) == 0 || len(password) == 0 {
return nil,
http.StatusBadRequest,
errors.New("error: The user name or password is empty ")
}
// Connect to database
o := orm.NewOrm()
// Check if the user name exists
user := &TabUser{
UserName: username}
err := o.Read(user, "username")
if err != nil {
return nil,
http.StatusBadRequest, // 400
errors.New("error: The username does not exist ")
}
// Generate hash Encrypted password
hash, err := GeneratePassHash(password, user.Salt)
if err != nil {
return nil,
http.StatusBadRequest, // 400
err
}
// Compare passwords entered by users + Generated by the user's encryption key hash password And Stored in the database hash password
if hash != user.Password {
return nil,
http.StatusBadRequest,
errors.New(" error : Wrong password !")
}
// Generate token
tokenString, err := GenerateToken(lr, user.Id, 0)
if err != nil {
return nil,
http.StatusBadRequest,
err
}
// Generated token Back to the front end
return &LoginResponse{
Username: user.UserName,
UserID: user.Id,
Token: tokenString,
}, http.StatusOK, nil
}
// DoCreateUser
// @Title DoCreateUser
// @Description " Create user "
// @Param cr *CreateRequest " The user creates the request "
// @return *CreateResponse " User created response "
// @return int " Status code "
// @return error " error message "
func DoCreateUser(cr *CreateRequest) (*CreateResponse, int, error) {
// Connect to database
o := orm.NewOrm()
// Check if the user name exists
userNameCheck := TabUser{
UserName: cr.Username}
err := o.Read(&userNameCheck, "username")
if err == nil {
return nil, http.StatusBadRequest, errors.New("username has already existed")
}
// Generate User's encrypted key
saltKey, err := GenerateSalt()
if err != nil {
logs.Info(err.Error())
return nil, http.StatusBadRequest, err
}
// Generate hash Encrypted password
hash, err := GeneratePassHash(cr.Password, saltKey)
if err != nil {
logs.Info(err.Error())
return nil, http.StatusBadRequest, err
}
// Create user
user := TabUser{
}
user.UserName = cr.Username
user.Password = hash
user.Salt = saltKey
_, err = o.Insert(&user)
if err != nil {
logs.Info(err.Error())
return nil, http.StatusBadRequest, err
}
return &CreateResponse{
UserID: user.Id,
Username: user.UserName,
}, http.StatusOK, nil
}
// mapping mysql data
func init() {
orm.RegisterModel(new(TabUser))
}
2. controller package
2.1 user.go
// UserController Handle user related requests
// user API
type UserController struct {
beego.Controller
}
// Resolve request , And store the request body in v in
// unmarshalPayload
// @Param v interface{} true " Receive the variables of the parsed request body "
func (c *UserController) unmarshalPayload(v interface{
}) error {
// json analysis
// Unmarshal(data []byte, v interface{})
// take json The string is decoded to the corresponding data structure
err := json.Unmarshal(c.Ctx.Input.RequestBody, &v)
if err != nil {
logs.Error("RequestBody Parse failure !")
}
if err != nil {
logs.Error("unmarshal payload of %s error: %s", c.Ctx.Request.URL.Path, err)
}
return nil
}
// respond
// @Title respond
// @Description Information returned to the front end
// @Param code int true " Status code "
// @Param message string true " Return information "
// @Param data ...interface{} true " data "
func (c *UserController) respond(code int, message string, data ...interface{
}) {
c.Ctx.Output.SetStatus(code)
var d interface{
}
if len(data) > 0 {
d = data[0]
}
c.Data["json"] = struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{
} `json:"data,omitempty"`
}{
Code: code,
Message: message,
Data: d,
}
c.ServeJSON()
}
// Login
// @Title Login
// @Description Processing login requests
func (c *UserController) Login() {
lr := new(models.LoginRequest)
if err := c.unmarshalPayload(lr); err != nil {
c.respond(http.StatusBadRequest, err.Error())
return
}
lrs, statusCode, err := models.DoLogin(lr)
if err != nil {
c.respond(statusCode, err.Error())
return
}
// take token Set to Header
c.Ctx.Output.Header("Authorization", lrs.Token)
c.respond(http.StatusOK, "", lrs)
}
// CreateUser
// @Title CreateUser
// @Description New users
// @Success 200
// @router /register [post]
func (c *UserController) CreateUser() {
cu := new(models.CreateRequest)
// obtain request body
if err := c.unmarshalPayload(cu); err != nil {
c.respond(http.StatusBadRequest, err.Error())
}
createUser, statusCode, err := models.DoCreateUser(cu)
if err != nil {
c.respond(statusCode, err.Error())
return
}
c.respond(http.StatusOK, "", createUser)
}
3.routers package
3.1 router.go
func init() {
ns := beego.NewNamespace("/api",
beego.NSNamespace("/user",
beego.NSRouter("/login", &controllers.UserController{
}, "post:Login"),
beego.NSRouter("/register", &controllers.UserController{
}, "post:CreateUser"),
),
)
beego.AddNamespace(ns)
}
4. conf package
appname = Project name
httpport = 8080
runmode = dev
autorender = false
copyrequestbody = true
EnableDocs = true
sqlconn =
[mysql]
dbHost = "localhost"
dbPort = "3306"
dbUser = " user name "
dbName = " Database name "
dbPassword = " Database password "
5. main.go
func main() {
if beego.BConfig.RunMode == "dev" {
beego.BConfig.WebConfig.DirectoryIndex = true
beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
}
beego.Run()
}
func init() {
// mysql
dbHost := beego.AppConfig.String("dbHost")
dbPort := beego.AppConfig.String("dbPort")
dbUser := beego.AppConfig.String("dbUser")
dbPassword := beego.AppConfig.String("dbPassword")
dbName :=beego.AppConfig.String("dbName")
dsn := dbUser + ":" + dbPassword +"@tcp("+ dbHost +":"+ dbPort +")/"+ dbName +"?charset=utf8"
// Set the basic information of the database , It is equivalent to connecting to the database
_ = orm.RegisterDataBase("default","mysql",dsn,30)
// To generate table
_ = orm.RunSyncdb("default", false, true)
}
边栏推荐
- Instant messaging IM is the countercurrent of the progress of the times? See what jnpf says
- 【点云处理之论文狂读前沿版9】—Advanced Feature Learning on Point Clouds using Multi-resolution Features and Learni
- LeetCode 715. Range 模块
- 网络安全必会的基础知识
- Memory search acwing 901 skiing
- Divide candy (circular queue)
- LeetCode 515. 在每个树行中找最大值
- Low code momentum, this information management system development artifact, you deserve it!
- The "booster" of traditional office mode, Building OA office system, was so simple!
- LeetCode 535. Encryption and decryption of tinyurl
猜你喜欢

Move anaconda, pycharm and jupyter notebook to mobile hard disk

In the digital transformation, what problems will occur in enterprise equipment management? Jnpf may be the "optimal solution"

状态压缩DP AcWing 291. 蒙德里安的梦想

AcWing 785. 快速排序(模板)

即时通讯IM,是时代进步的逆流?看看JNPF怎么说

Binary tree sorting (C language, char type)

How to check whether the disk is in guid format (GPT) or MBR format? Judge whether UEFI mode starts or legacy mode starts?

Save the drama shortage, programmers' favorite high-score American drama TOP10

LeetCode 508. 出现次数最多的子树元素和

AcWing 787. Merge sort (template)
随机推荐
Low code momentum, this information management system development artifact, you deserve it!
剑指 Offer II 091. 粉刷房子
Education informatization has stepped into 2.0. How can jnpf help teachers reduce their burden and improve efficiency?
【点云处理之论文狂读前沿版12】—— Adaptive Graph Convolution for Point Cloud Analysis
Facial expression recognition based on pytorch convolution -- graduation project
The less successful implementation and lessons of RESNET
2022-2-13 learn the imitation Niuke project - Project debugging skills
What is an excellent fast development framework like?
推荐一个 yyds 的低代码开源项目
LeetCode 535. TinyURL 的加密与解密
The "booster" of traditional office mode, Building OA office system, was so simple!
String splicing method in shell
Problems in the implementation of lenet
Method of intercepting string in shell
数位统计DP AcWing 338. 计数问题
AcWing 787. 归并排序(模板)
LeetCode 57. Insert interval
Discussion on enterprise informatization construction
PIC16F648A-E/SS PIC16 8位 微控制器,7KB(4Kx14)
MySQL installation and configuration (command line version)