当前位置:网站首页>[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
边栏推荐
- Sequence diagram of single sign on Certification Center
- Network security - record web vulnerability fixes
- 直播预告 | 容器服务 ACK 弹性预测最佳实践
- Data security -- 14 -- Analysis of privacy protection governance
- Hypothesis testing -- learning notes of Chapter 8 of probability theory and mathematical statistics
- Advanced length of redis -- deletion strategy, master-slave replication, sentinel mode
- All in one 1413: determine base
- This is an age of uncertainty
- 这是一个不确定的时代
- [phantom engine UE] realize the animation production of mapping tripod deployment
猜你喜欢
[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)
托管式服务网络:云原生时代的应用体系架构进化
Function (error prone)
Network layer - forwarding (IP, ARP, DCHP, ICMP, network layer addressing, network address translation)
TPG x AIDU | AI leading talent recruitment plan in progress!
CSDN body auto generate directory
Fonction (sujette aux erreurs)
Sword finger offer 04 Search in two-dimensional array
Realize the attention function of the article in the applet
Wenet: E2E speech recognition tool for industrial implementation
随机推荐
2022-2028 global and Chinese equipment as a Service Market Research Report
Cookie learning diary 1
Live broadcast preview | container service ack elasticity prediction best practice
OWASP top 10 vulnerability Guide (2021)
MacBook installation postgresql+postgis
You Li takes you to talk about C language 7 (define constants and macros)
Burpsuite grabs app packets
Neural networks and deep learning Chapter 2: machine learning overview reading questions
How to remove installed elpa package
Rk3399 platform development series explanation (network debugging) 7.29 summary of network performance tools
Here comes the Lantern Festival red envelope!
Network layer - forwarding (IP, ARP, DCHP, ICMP, network layer addressing, network address translation)
The remainder operation is a hash function
[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)
【FineBI】使用FineBI制作自定义地图过程
Matplotlib draws three-dimensional scatter and surface graphs
CUDA Programming atomic operation atomicadd reports error err:msb3721, return code 1
【虚幻引擎UE】运行和启动的区别,常见问题分析
User behavior collection platform
Invalid bound statement (not found) in idea -- problem solving