当前位置:网站首页>[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
边栏推荐
猜你喜欢
Seven join join queries of MySQL
Label exchange experiment
level18
level17
Realize the attention function of the article in the applet
首席信息官如何利用业务分析构建业务价值?
windows下Redis-cluster集群搭建
Mxnet imports various libcudarts * so、 libcuda*. So not found
Live broadcast preview | container service ack elasticity prediction best practice
Discussion on the dimension of confrontation subspace
随机推荐
PHP读取ini文件并修改内容写入
How to remove installed elpa package
直播預告 | 容器服務 ACK 彈性預測最佳實踐
[PCL self study: feature9] global aligned spatial distribution (GASD) descriptor (continuously updated)
American 5g open ran suffered another major setback, and its attempt to counter China's 5g technology has failed
Decryption function calculates "task state and lifecycle management" of asynchronous task capability
Neural networks and deep learning Chapter 6: Circular neural networks reading questions
PR video clip (project packaging)
Raki's notes on reading paper: code and named entity recognition in stackoverflow
web资源部署后navigator获取不到mediaDevices实例的解决方案(navigator.mediaDevices为undefined)
Live broadcast preview | container service ack elasticity prediction best practice
这是一个不确定的时代
Qt蓝牙:搜索蓝牙设备的类——QBluetoothDeviceDiscoveryAgent
A solution to the problem that variables cannot change dynamically when debugging in keil5
[untitled]
Learning MVVM notes (1)
Setting up redis cluster cluster under Windows
Sequence diagram of single sign on Certification Center
How should programmers learn mathematics
[crampon game] MC tutorial - first day of survival