当前位置:网站首页>[goweb development] Introduction to authentication modes based on cookies, sessions and JWT tokens
[goweb development] Introduction to authentication modes based on cookies, sessions and JWT tokens
2022-07-05 04:34:00 【Hu Maomao_ March】
Catalog
User authentication
HTTP Is a stateless protocol , After a request , Next time, the sending server will not know who sent the request ( The same IP Does not represent the same user ), stay Web Application , User authentication and authentication are very important , There are many options available in practice , And each has its own merits .
Cookie- Session Authentication mode
stay Web The early days of application development , Most of them are based on Cookie-Session Session management mode , The logic is as follows .
The client uses the user name 、 Password Authentication
Server authentication user name 、 After the password is correct, it is generated and stored Session, take SessionID adopt Cookie Return to the client
When the client accesses the interface that needs authentication, it is in Cookie Middle carry SessionlD
Server through SessionID lookup Session And authenticate , Return to the data required by the client
be based on Session There are many problems with the way .
The server needs to store Session, And because of Session You need to find it often and quickly , Usually stored in memory or memory database , At the same time, when there are many online users, it needs to occupy a lot of server resources .
When you need to expand , establish Session Your server may not be an authentication server Session Server for , So you need to put all Session Store and share separately .
Because the client uses Cookie Storage SessionlD, Compatibility processing is required in cross domain scenarios , At the same time, this way is also difficult to prevent CSRF attack .
Token Authentication mode
In view of Session The session management mode of has the above disadvantages , be based on Token Stateless session management was born , So called stateless , That is, the server can no longer store information , Even no longer store Session, The logic is as follows .
The client uses the user name 、 Password Authentication
Server authentication user name 、 Generate after the password is correct Token Return to the client
Client save Token, When accessing an interface requiring authentication URL Parameter or HTTP Header Add Token
The server decodes Token To authorize , Return to the data required by the client
JWT Introduce
JWT yes JSON Web Token Abbreviation , Is a kind of implementation based on the JSON Open standards for ((RFC7519).JWT There is no technical implementation defined by itself , It just defines a method based on Token The rules of session management , cover Token The standard content to be included and Token Generation process of , Especially for single sign in of distributed sites (SSO) scene .
One JWT Token Just like this. :
eyJhbGcioiJIUzI1NiIsInR5cCI6IkpxVCJ9
.eyJ1c2VyX2lkIjoyODAxODcyNzQ4ODMyMZU4NSwiZXhwIjoxNTkONTQwMjkxLCJpc3MiOiJibHVlYmVsbCJ9
.lk_ZrAtYGCeZhK3iupHxP1kgjBTzQTVTtX0izYFx9wU
It is from . Separated by three parts , The three parts are :
Head (Header)
load (Payload)
Signature (Signature)
Head and load in JSON The form , This is it. JWT Medium JSON, The contents of the three parts have been separately Base64 code , With . To join together into one JWT Token.
Header
JWT Of Header The encryption algorithm and Token type .
{
"alg": "HS256",
"TYP": "jwt"
}
Payload
Payload Indicates the load ( take Token As a carrier , Express Token What's inside ), Also a JSON object ,JWT Specifies the 7 There are two official fields to choose from ,
iss (issuer)︰ Issued by people
exp ( expiration time): Expiration time
sub ( subject)︰ The theme
aud (audience)︰ Audience
nbf (Not Before): entry-into-force time
iat ( Issued At)︰ The issuance of time
jti(JwT ID)∶ Number
Except for official fields , Developers can also specify their own fields and contents , For example, the following .
{
"sub" : "1234567890",
"name " : "John Doe" ,
"admin " : true
}
Be careful ,JWT It is not encrypted by default , Anyone can read it , So don't put secret information in this part . This JSON Objects also use Base64URL Algorithm to string .
Signature
Signature Part is the signature of the first two parts , Prevent data tampering .
First , A key needs to be specified (secret). This key is only known to the server , Do not disclose to users . then , Use Header The signature algorithm specified in ( The default is HMAC SHA256), Follow the formula below to generate a signature .
HMACSHA256(base64UrlEncode ( header) + "." + base64UrlEncode(payload) ,secret)
JWT Advantages and disadvantages
JWT Based on Token All the advantages of session management , Do not rely on Cookie, So that it can prevent CSRF attack , You can also disable Cookie Normal operation in the browser environment .
and JWT The biggest advantage of is that the server no longer needs storage Session, So that the server authentication business can be easily expanded , Avoid storage Session What needs to be introduced Redis And so on , It reduces the complexity of system architecture . But it's also JWT The biggest disadvantage , Because the validity period is stored in Token in ,JWTToken Once issued , Will remain available for the duration of the validity period , Cannot be abolished on the server , When the user logs out , You can only rely on the client to delete the locally stored JWT Token, Disable the user if necessary , Just use JWT You can't do it .
be based on jwt Implement certification practices
As mentioned earlier Token, All are Access Token, That is, what is needed to access the resource interface Token, There's another one
Token,Refresh Token, Usually ,Refresh Token Will be valid for a long time , and Access Token The validity period of , When Access Token Failure due to expiration , Use Refresh Token You can get new Access Token If Refresh Token It's not working , The user can only log in again .
stay JWT In the practice of , introduce Refresh Token, Improve the session management process as follows .
The client uses the user name and password for authentication
The server generates a message with a shorter effective time Access Token( for example 10 minute ), And longer effective time RefreshToken( for example 7 God )
When the client accesses the interface that needs authentication , carry Access Token
If Access Token No expired , After authentication, the server returns the required data to the client
If carrying Access Token Authentication failed when accessing the interface requiring authentication ( Such as return 401 error ), Then the client uses Refresh Token Apply to the refresh interface for a new Access Token
If Refresh Token No expired , The server sends a new message to the client Access Token The client uses the new Access Token Access the interface that requires authentication
The backend needs to provide a refresh Token The interface of , The front end needs to implement a when Access Token Automatically request refresh when it expires Token Interface got
Get new Access Token Ballast of .
gin Frame usage jwt
jwt-go See : stay gin Use... In the framework JWT
package jwt
import (
"errors"
"time"
"github.com/dgrijalva/jwt-go"
)
// MyClaims Customize the declaration structure and embed jwt.StandardClaims
// jwt It comes with you jwt.StandardClaims Only official fields are included
// We need to record an additional UserID Field , So you need to customize the structure
// If you want to save more information , Can be added to this structure
type MyClaims struct {
UserID uint64 `json:"user_id"`
Username string `json:"username"`
jwt.StandardClaims
}
// Definition Secret
var mySecret = []byte(" Summer and summer slip by ")
func keyFunc(_ *jwt.Token) (i interface{
}, err error) {
return mySecret, nil
}
// Definition JWT The expiration time of
const TokenExpireDuration = time.Hour * 2
/** * @Author huchao * @Description //TODO Generate JWT * @Date 9:42 2022/2/11 **/
// GenToken Generate access token and refresh token
func GenToken(userID uint64,username string) (aToken, rToken string, err error) {
// Create our own statement
c := MyClaims{
userID, // Custom field
"username", // Custom field
jwt.StandardClaims{
// JWT Stipulated 7 Official fields
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // Expiration time
Issuer: "bluebell", // Issued by people
},
}
// Encrypt and get the complete encoded string token
aToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, c).SignedString(mySecret)
// refresh token There is no need to save any custom data
rToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Second * 30).Unix(), // Expiration time
Issuer: "bluebell", // Issued by people
}).SignedString(mySecret)
// Use specified secret Sign and get the complete encoded string token
return
}
//GenToken Generate Token
func GenToken2(userID uint64, username string) (Token string, err error) {
// Create our own statement
c := MyClaims{
userID, // Custom field
"username", // Custom field
jwt.StandardClaims{
// JWT Stipulated 7 Official fields
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // Expiration time
Issuer: "bluebell", // Issued by people
},
}
// Encrypt and get the complete encoded string token
Token, err = jwt.NewWithClaims(jwt.SigningMethodHS256, c).SignedString(mySecret)
// refresh token There is no need to save any custom data
//rToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
// ExpiresAt: time.Now().Add(time.Second * 30).Unix(), // Expiration time
// Issuer: "bluebell", // Issued by people
//}).SignedString(mySecret) // Use specified secret Sign and get the complete encoded string token
return
}
/** * @Author huchao * @Description //TODO analysis JWT * @Date 9:43 2022/2/11 **/
func ParseToken(tokenString string) (claims *MyClaims, err error) {
// analysis token
var token *jwt.Token
claims = new(MyClaims)
token, err = jwt.ParseWithClaims(tokenString, claims, keyFunc)
if err != nil {
return
}
if !token.Valid {
// check token
err = errors.New("invalid token")
}
return
}
// RefreshToken Refresh AccessToken
func RefreshToken(aToken, rToken string) (newAToken, newRToken string, err error) {
// refresh token Invalid direct return
if _, err = jwt.Parse(rToken, keyFunc); err != nil {
return
}
// From the old access token Resolve in claims data It is concluded that payload Load information
var claims MyClaims
_, err = jwt.ParseWithClaims(aToken, &claims, keyFunc)
v, _ := err.(*jwt.ValidationError)
// When access token Is an expiration error also refresh token Create a new one before it expires access token
if v.Errors == jwt.ValidationErrorExpired {
return GenToken(claims.UserID,claims.Username)
}
return
}
Authentication middleware development
const (
ContextUserIDKey = "userID"
)
var (
ErrorUserNotLogin = errors.New(" The current user is not logged in ")
)
// JWTAuthMiddleware be based on JWT Authentication middleware for
func JWTAuthMiddleware() func(c *gin.Context) {
return func(c *gin.Context) {
// The client carries Token There are three ways 1. Put it on the request 2. Put in request body 3. Put it in URI
// It is assumed that Token Put it in Header Of Authorization in , And use Bearer start
// The specific implementation method here should be determined according to your actual business situation
authHeader := c.Request.Header.Get("Authorization")
if authHeader == "" {
controller.ResponseErrorWithMsg(c, controller.CodeInvalidToken, " The request header is missing Auth Token")
c.Abort()
return
}
// Split by space
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
controller.ResponseErrorWithMsg(c, controller.CodeInvalidToken, "Token Wrong format ")
c.Abort()
return
}
// parts[1] It's got tokenString, We use the previously defined parsing JWT To parse it
mc, err := jwt.ParseToken(parts[1])
if err != nil {
fmt.Println(err)
controller.ResponseError(c, controller.CodeInvalidToken)
c.Abort()
return
}
// Will the currently requested userID Save the information to the context of the request c On
c.Set(.ContextUserIDKey, mc.UserID)
c.Next() // Subsequent processing functions can be used c.Get(ContextUserIDKey) To get the currently requested user information
}
}
Generate access token and refresh token
// GenToken Generate access token and refresh token
func GenToken(userID uint64, username string) (Token string, err error) {
// Create our own statement
c := MyClaims{
userID, // Custom field
"username", // Custom field
jwt.StandardClaims{
// JWT Stipulated 7 Official fields
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // Expiration time
Issuer: "bluebell", // Issued by people
},
}
// Encrypt and get the complete encoded string token
Token, err = jwt.NewWithClaims(jwt.SigningMethodHS256, c).SignedString(mySecret)
// refresh token There is no need to save any custom data
//rToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
// ExpiresAt: time.Now().Add(time.Second * 30).Unix(), // Expiration time
// Issuer: "bluebell", // Issued by people
//}).SignedString(mySecret) // Use specified secret Sign and get the complete encoded string token
return
}
analysis access token
// analysis JWT
func ParseToken(tokenString string) (claims *MyClaims, err error) {
// analysis token
var token *jwt.Token
claims = new(MyClaims)
token, err = jwt.ParseWithClaims(tokenString, claims, keyFunc)
if err != nil {
return
}
if !token.Valid {
// check token
err = errors.New("invalid token")
}
return
}
refresh token
// RefreshToken Refresh AccessToken
func RefreshToken(aToken, rToken string) (newAToken, newRToken string, err error) {
// refresh token Invalid direct return
if _, err = jwt.Parse(rToken, keyFunc); err != nil {
return
}
// From the old access token Resolve in claims data
var claims MyClaims
_, err = jwt.ParseWithClaims(aToken, &claims, keyFunc)
v, _ := err.(*jwt.ValidationError)
// When access token Is an expiration error also refresh token Create a new one before it expires access token
if v.Errors == jwt.ValidationErrorExpired {
return GenToken(claims.UserID)
}
return
}
Related reference links
- https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
边栏推荐
- 概率论与数理统计考试重点复习路线
- 【科普】热设计基础知识:5G光器件之散热分析
- Advanced length of redis -- deletion strategy, master-slave replication, sentinel mode
- 网络安全-记录web漏洞修复
- User behavior collection platform
- Machine learning -- neural network
- WeNet:面向工业落地的E2E语音识别工具
- [finebi] the process of making custom maps using finebi
- 函数(易错)
- English topic assignment (26)
猜你喜欢
Matplotlib draws three-dimensional scatter and surface graphs
Sword finger offer 04 Search in two-dimensional array
[phantom engine UE] only six steps are needed to realize the deployment of ue5 pixel stream and avoid detours! (the principles of 4.26 and 4.27 are similar)
Burpsuite grabs app packets
2022-2028 global and Chinese video coding and transcoding Market Research Report
2022-2028 global and Chinese virtual data storage Market Research Report
[phantom engine UE] the difference between running and starting, and the analysis of common problems
windows下Redis-cluster集群搭建
How should programmers learn mathematics
Qt蓝牙:搜索蓝牙设备的类——QBluetoothDeviceDiscoveryAgent
随机推荐
蛇形矩阵
Uncover the seven quirky brain circuits necessary for technology leaders
Learning notes 8
直播预告 | 容器服务 ACK 弹性预测最佳实践
【虛幻引擎UE】實現UE5像素流部署僅需六步操作少走彎路!(4.26和4.27原理類似)
指针函数(基础)
Interview related high-frequency algorithm test site 3
Scope of package class package
取余操作是一个哈希函数
Chapter 6 text processing tools for shell programming (awk)
学习MVVM笔记(一)
level17
Hexadecimal to decimal
Neural networks and deep learning Chapter 4: feedforward neural networks reading questions
How to remove installed elpa package
How can CIOs use business analysis to build business value?
CUDA Programming atomic operation atomicadd reports error err:msb3721, return code 1
Convert Boolean to integer value PHP - Convert Boolean to integer value PHP
All in one 1413: determine base
【FineBI】使用FineBI制作自定义地图过程