Golang基于JWT与Casbin身份验证授权实例详解

目录
  • JWT
    • Header
    • Payload
    • Signature
    • JWT的优势
    • JWT的使用场景
  • Casbin
    • Casbin可以做什么
    • Casbin不可以做什么
    • Casbin的工作原理
  • 实践
    • 登录接口请求
    • Token实现
    • 使用Redis存储Token信息
    • 用Casbin做授权管理
    • 实现Casbin的策略
    • 创建Todo

JWT

JSON Web Toekn(JWT)是一个开放标准RFC 7519,以JSON的方式进行通信,是目前最流行的一种身份验证方式之一。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

下图是通过JWT.io解码,查看JWT token的组成

可以看出JWT是由下面三个部分组成的:

  • 头部(Header)
  • 载荷(Payload)
  • 签名(Signature)

Header

Header是Token的构成的第一部分,包含了Token类型、Token使用的加密算法(加密算法可以是HMAC, SHA256或者是RSA等)。在某些场景下,可以使用kid字段,用来标识一个密钥的ID

Payload

Payload是token的第二部分,由JWT标准中注册的、公共的、私有的声明三部分组成。Payload通常包含一些用户的声明信息,比如签发者、过期时间、签发时间等。其中最常见的是issuer, expiration, subject。

  • issuer被用来标识token的颁发人
  • expiration是token的过期时间
  • subject被用来标识token主体部分

Signature

Signature是由头部和载荷加密后连接起来的,程序通过验证Signature是否合法来决定认证是否通过

JWT的优势

  • 体积小。JWT是采用JSON进行通信的,JSON比XML更加简洁,因此在对其编码时,JWT的体积比SAML更小(SAML是一种基于XML的开放标准,用在身份提供者和服务提供者之间交换身份验证和授权的数据,SAML的一个重要的应用就是基于Web的单点登录)
  • 更加安全。JWT能够使用公钥或者私钥对证书进行加密或解密,虽然SAML也可以使用JWT等公钥或私钥进行加密或解密,但是与JSON相比,使用XML数字签名容易引进比较晦涩的安全漏洞
  • 更加通用。JSON可以转换成很多语言的对象方式,而XML没有一种可以转为对象的映射
  • 更容易处理。不管是在PC端还是在移动端,JSON都能够很好的进行通信

JWT的使用场景

  • 身份验证。
  • 授权
  • 信息交换

需要注意的是不要将敏感信息存在Token里面!!!

Casbin

Casbin是一个强大的、高效的、开源的权限访问控制库,它提供了多种权限控制访问模型,比如ACL(权限控制列表)、RBAC(基于角色的访问控制)、ABAC(基于属性的权限验证)等。除此之外Casbin还支持多种编程语言

Casbin可以做什么

  • 通过经典的{subject, object, action}或者自定义的模式执行想要的策略,同时支持allow和deny两种授权方式
  • 处理控制访问存储和权限
  • 管理用户-角色-资源权限控制访问映射(RBAC)
  • 支持超级管理员授权方式
  • 可以使用内置的函数配置访问规则

Casbin不可以做什么

  • 使用用户名或密码登录的身份验证
  • 管理用户或者角色列表,这些由系统本身管理更加方便,casbin主要是用来作为用户-角色的一种权限访问控制映射

Casbin的工作原理

在Casbin中,访问控制模型被抽象为PERM(Policy, Effect, Request, Matcher)的一个文件

  • Request

定义请求参数。基本请求时一个元组对象,至少需要主题(访问实体), 对象(访问资源), 动作(访问方式),例如r={sub, obj, act},它实际定义了我们应该提供访问控制匹配功能的参数名称和顺序

  • Policy

定义访问策略模式,例如p={sub, obj, act}或p={sub, obj, act, eff}, 它定义字段的名称和顺序

  • Matcher

匹配请求和策略的规则,例如m = r.sub == p.sub && r.obj == p.obj && r.act == p.act,它的意思是如果请求的参数被匹配,那么结果就会被返回

  • Effect

匹配后的结果会储存于Effect当中,可以对匹配结果再次做出逻辑判断,例如e = some (where (p.eft == allow))

Casbin中最基本的model是ACL,下面是ACL的model配置

[request_definition]
r = sub, obj, act
# Policy definition
[policy_definition]
p = sub, obj, act
# Policy effect
[policy_effect]
e = some(where (p.eft == allow))
# Matchers
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

实践

编写一个简单的TODO RESTful API。

创建一个simple-jwt-auth的目录,然后通过go mod管理依赖 go mod init simple-jwt-auth 建立的目录结构如下:

在model定义UserTodo的结构体

// models/model.go
type User struct {
   ID       string `json:"id"`
   UserName string `json:"username"`
   Password string `json:"password"`
}
type Todo struct {
   UserID string `json:"user_id"`
   Title  string `json:"title"`
   Body   string `json:"body"`
}
// SetPassword sets a new password stored as hash.
func (m *User) SetPassword(password string) error {
   if len(password) < 6 {
      return fmt.Errorf("new password for %s must be at least 6 characters", m.UserName)
   }
   m.Password = password
   return nil
}
// InvalidPassword returns true if the given password does not match the hash.
func (m *User) InvalidPassword(password string) bool {
   if password == "" {
      return true
   }
   if m.Password != password {
      return true
   }
   return false
}

登录接口请求

当用户通过用户名和密码等信息登录系统服务时,需要验证是否已注册、密码是否正确等,然后返回信息, 下面在api层实现Login的接口:

// api/auth_api.go
func Login(c *gin.Context) {
   var u models.User
   if err := c.ShouldBindJSON(&u); err != nil {
      c.JSON(http.StatusUnprocessableEntity, "Invalid json provided")
      return
   }
   //find user with username
   user, err := models.UserRepo.FindByID(1)
   //compare the user from the request, with the one we defined:
   if user.UserName != u.UserName || user.Password != u.Password {
      c.JSON(http.StatusUnauthorized, "Please provide valid login details")
      return
   }
   c.JSON(http.StatusOK, "Login successfully")
}
func Logout(c *gin.Context) {
   c.JSON(http.StatusOK, "Successfully logged out")
}

在真实的项目中,数据都是存在数据库中。在该教程中,为了方便,创建一个mock文件user_repository.go

// models/user_repository.go
var us = []User{
   {
      ID:       "2",
      UserName: "users",
      Password: "pass",
   }, {
      ID:       "3",
      UserName: "username",
      Password: "password",
   },
}
var UserRepo = UserRepository{
   Users: us,
}
type UserRepository struct {
   Users []User
}
func (r *UserRepository) FindAll() ([]User, error) {
   return r.Users, nil
}
func (r *UserRepository) FindByID(id int) (User, error) {
   for _, v := range r.Users {
      uid, err := strconv.Atoi(v.ID)
      if err != nil {
         return User{}, err
      }
      if uid == int(id) {
         return v, nil
      }
   }
   return User{}, errors.New("Not found")
}
func (r *UserRepository) Save(user User) (User, error) {
   r.Users = append(r.Users, user)
   return user, nil
}
func (r *UserRepository) Delete(user User) {
   id := -1
   for i, v := range r.Users {
      if v.ID == user.ID {
         id = i
         break
      }
   }
   if id == -1 {
      log.Fatal("Not found user ")
      return
   }
   r.Users[id] = r.Users[len(r.Users)-1] // Copy last element to index i.
   r.Users[len(r.Users)-1] = User{}      // Erase last element (write zero value).
   r.Users = r.Users[:len(r.Users)-1]    // Truncate slice.
   return
}

为了不让Login函数变得臃肿,生成token的逻辑放在auth目录中, 下面实现token验证逻辑

Token实现

JWT实现的系统中,用户登录后,系统会生成并返回一个token给用户,下次请求时将会带上该token进行身份验证。token有以下问题需要处理:

  • 用户退出登录的时候,需要使token失效
  • token有可能被黑客劫持和使用
  • token过期后需要用户重新登录,体验不友好

上面的问题可以通过以下两种方式解决:

  • 使用Redis存储token的信息。当用户退出时,使token失效, 这在一定程度上提高的安全性
  • 在token过期的时候,使用刷新token的方式重新生成一个token, 不用用户退出登录,提高用户体验

使用Redis存储Token信息

使用uuid作为redis中的key, token信息作为value, 下面定义TokenManager结构体,通过接口的方式实现token

type TokenManager struct{}
func NewTokenService() *TokenManager {
   return &TokenManager{}
}
type TokenInterface interface {
   CreateToken(userId, userName string) (*TokenDetails, error)
   ExtractTokenMetadata(*http.Request) (*AccessDetails, error)
}
//Token implements the TokenInterface
var _ TokenInterface = &TokenManager{}
func (t *TokenManager) CreateToken(userId, userName string) (*TokenDetails, error) {
   td := &TokenDetails{}
   td.AtExpires = time.Now().Add(time.Minute * 30).Unix() //expires after 30 min
   td.TokenUuid = uuid.NewV4().String()
   td.RtExpires = time.Now().Add(time.Hour * 24 * 7).Unix()
   td.RefreshUuid = td.TokenUuid + "++" + userId
   var err error
   //Creating Access Token
   atClaims := jwt.MapClaims{}
   atClaims["access_uuid"] = td.TokenUuid
   atClaims["user_id"] = userId
   atClaims["user_name"] = userName
   atClaims["exp"] = td.AtExpires
   at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims)
   td.AccessToken, err = at.SignedString([]byte(os.Getenv("ACCESS_SECRET")))
   if err != nil {
      return nil, err
   }
   //Creating Refresh Token
   td.RtExpires = time.Now().Add(time.Hour * 24 * 7).Unix()
   td.RefreshUuid = td.TokenUuid + "++" + userId
   rtClaims := jwt.MapClaims{}
   rtClaims["refresh_uuid"] = td.RefreshUuid
   rtClaims["user_id"] = userId
   rtClaims["user_name"] = userName
   rtClaims["exp"] = td.RtExpires
   rt := jwt.NewWithClaims(jwt.SigningMethodHS256, rtClaims)
   td.RefreshToken, err = rt.SignedString([]byte(os.Getenv("REFRESH_SECRET")))
   if err != nil {
      return nil, err
   }
   return td, nil
}
func (t *TokenManager) ExtractTokenMetadata(r *http.Request) (*AccessDetails, error) {
   token, err := VerifyToken(r)
   if err != nil {
      return nil, err
   }
   acc, err := Extract(token)
   if err != nil {
      return nil, err
   }
   return acc, nil
}
func TokenValid(r *http.Request) error {
   token, err := VerifyToken(r)
   if err != nil {
      return err
   }
   if _, ok := token.Claims.(jwt.Claims); !ok && !token.Valid {
      return err
   }
   return nil
}
func VerifyToken(r *http.Request) (*jwt.Token, error) {
   tokenString := ExtractToken(r)
   token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
      if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
         return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
      }
      return []byte(os.Getenv("ACCESS_SECRET")), nil
   })
   if err != nil {
      return nil, err
   }
   return token, nil
}
//get the token from the request body
func ExtractToken(r *http.Request) string {
   bearToken := r.Header.Get("Authorization")
   strArr := strings.Split(bearToken, " ")
   if len(strArr) == 2 {
      return strArr[1]
   }
   return ""
}
func Extract(token *jwt.Token) (*AccessDetails, error) {
   claims, ok := token.Claims.(jwt.MapClaims)
   if ok && token.Valid {
      accessUuid, ok := claims["access_uuid"].(string)
      userId, userOk := claims["user_id"].(string)
      userName, userNameOk := claims["user_name"].(string)
      if ok == false || userOk == false || userNameOk == false {
         return nil, errors.New("unauthorized")
      } else {
         return &AccessDetails{
            TokenUuid: accessUuid,
            UserId:    userId,
            UserName:  userName,
         }, nil
      }
   }
   return nil, errors.New("something went wrong")
}
func ExtractTokenMetadata(r *http.Request) (*AccessDetails, error) {
   token, err := VerifyToken(r)
   if err != nil {
      return nil, err
   }
   acc, err := Extract(token)
   if err != nil {
      return nil, err
   }
   return acc, nil
}

上面的代码设置token的有效时间为30分钟,30分钟过后token将失效,用户不能使用该token进行正确验证。

另外,使用了从.env配置文件获取的密钥(ACCESS_SECRET)签名。在真实的项目,不能在代码中公开这个密钥!!!

REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
ACCESS_SECRET=98hbun98hsdfsdwesdfs
REFRESH_SECRET=786dfdbjhsbsdfsdfsdf
PORT=8081

定义AuthInterface处理会话

package auth
import (
   "errors"
   "fmt"
   "github.com/go-redis/redis/v7"
   "time"
)
type AccessDetails struct {
   TokenUuid string
   UserId    string
   UserName  string
}
type TokenDetails struct {
   AccessToken  string
   RefreshToken string
   TokenUuid    string
   RefreshUuid  string
   AtExpires    int64
   RtExpires    int64
}
type AuthInterface interface {
   CreateAuth(string, *TokenDetails) error
   FetchAuth(string) (string, error)
   DeleteRefresh(string) error
   DeleteTokens(*AccessDetails) error
}
type RedisAuthService struct {
   client *redis.Client
}
var _ AuthInterface = &RedisAuthService{}
func NewAuthService(client *redis.Client) *RedisAuthService {
   return &RedisAuthService{client: client}
}
//Save token metadata to Redis
func (tk *RedisAuthService) CreateAuth(userId string, td *TokenDetails) error {
   at := time.Unix(td.AtExpires, 0) //converting Unix to UTC(to Time object)
   rt := time.Unix(td.RtExpires, 0)
   now := time.Now()
   atCreated, err := tk.client.Set(td.TokenUuid, userId, at.Sub(now)).Result()
   if err != nil {
      return err
   }
   rtCreated, err := tk.client.Set(td.RefreshUuid, userId, rt.Sub(now)).Result()
   if err != nil {
      return err
   }
   if atCreated == "0" || rtCreated == "0" {
      return errors.New("no record inserted")
   }
   return nil
}
//Check the metadata saved
func (tk *RedisAuthService) FetchAuth(tokenUuid string) (string, error) {
   userid, err := tk.client.Get(tokenUuid).Result()
   if err != nil {
      return "", err
   }
   return userid, nil
}
//Once a user row in the token table
func (tk *RedisAuthService) DeleteTokens(authD *AccessDetails) error {
   //get the refresh uuid
   refreshUuid := fmt.Sprintf("%s++%s", authD.TokenUuid, authD.UserId)
   //delete access token
   deletedAt, err := tk.client.Del(authD.TokenUuid).Result()
   if err != nil {
      return err
   }
   //delete refresh token
   deletedRt, err := tk.client.Del(refreshUuid).Result()
   if err != nil {
      return err
   }
   //When the record is deleted, the return value is 1
   if deletedAt != 1 || deletedRt != 1 {
      return errors.New("something went wrong")
   }
   return nil
}
func (tk *RedisAuthService) DeleteRefresh(refreshUuid string) error {
   //delete refresh token
   deleted, err := tk.client.Del(refreshUuid).Result()
   if err != nil || deleted == 0 {
      return err
   }
   return nil
}

用Casbin做授权管理

在Casbin中,一个权限访问控制模型的配置文件是基于PERM(Policy, Effect, Role, Matcher)的方式,因此当要修改或升级权限的时候非常方便,只需要修改配置文件就行了。使用者可以自定义配置文件,例如定义RBAC或者ACL

最基本的也是最简单的模型是ACL, 下面创建一个ACL的模型配置文件

[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

Casbin 的权限是存储在.csv文件中或者是SQL数据库中, 在该教程是通过csv文件的方式存储

p, user, resource, read
p, username, resource, read
p, admin, resource, read
p, admin, resource, write
g, alice, admin
g, bob, user

上面权限的意思是:

  • 所有的用户可以读数据,但是不能写
  • 所有的admin用户可以读数据,也可以写数据
  • alice是admin用户,bob是普通用户 因此Alice有控制整个系统数据的权限,而Bob只有读的权限

实现Casbin的策略

首先,定义一个policies的中间件

import (
   "fmt"
   "github.com/casbin/casbin"
   "github.com/casbin/casbin/persist"
   "github.com/gin-gonic/gin"
   "github.com/simple-jwt-auth/auth"
   "log"
   "net/http"
)
func TokenAuthMiddleware() gin.HandlerFunc {
   return func(c *gin.Context) {
      err := auth.TokenValid(c.Request)
      if err != nil {
         c.JSON(http.StatusUnauthorized, "unauthorized")
         c.Abort()
         return
      }
      c.Next()
   }
}
// Authorize determines if current subject has been authorized to take an action on an object.
func Authorize(obj string, act string, adapter persist.Adapter) gin.HandlerFunc {
   return func(c *gin.Context) {
      err := auth.TokenValid(c.Request)
      if err != nil {
         c.JSON(http.StatusUnauthorized, "user hasn't logged in yet")
         c.Abort()
         return
      }
      metadata, err := auth.ExtractTokenMetadata(c.Request)
      if err != nil {
         c.JSON(http.StatusUnauthorized, "unauthorized")
         return
      }
      // casbin enforces policy
      ok, err := enforce(metadata.UserName, obj, act, adapter)
      //ok, err := enforce(val.(string), obj, act, adapter)
      if err != nil {
         log.Println(err)
         c.AbortWithStatusJSON(500, "error occurred when authorizing user")
         return
      }
      if !ok {
         c.AbortWithStatusJSON(403, "forbidden")
         return
      }
      c.Next()
   }
}
func enforce(sub string, obj string, act string, adapter persist.Adapter) (bool, error) {
   enforcer := casbin.NewEnforcer("config/rbac_model.conf", adapter)
   err := enforcer.LoadPolicy()
   if err != nil {
      return false, fmt.Errorf("failed to load policy from DB: %w", err)
   }
   ok := enforcer.Enforce(sub, obj, act)
   return ok, nil
}

然后,修改上面的LoginLogout接口, 增加身份验证及授权信息

func Login(c *gin.Context) {
   var u models.User
   if err := c.ShouldBindJSON(&u); err != nil {
      c.JSON(http.StatusUnprocessableEntity, "Invalid json provided")
      return
   }
   //find user with username
   user, err := models.UserRepo.FindByID(1)
   //compare the user from the request, with the one we defined:
   if user.UserName != u.UserName || user.Password != u.Password {
      c.JSON(http.StatusUnauthorized, "Please provide valid login details")
      return
   }
   ts, err := tokenManager.CreateToken(user.ID, user.UserName)
   if err != nil {
      c.JSON(http.StatusUnprocessableEntity, err.Error())
      return
   }
   save token to redis
   saveErr := servers.HttpServer.RD.CreateAuth(user.ID, ts)
   if saveErr != nil {
      c.JSON(http.StatusUnprocessableEntity, saveErr.Error())
   }
   tokens := map[string]string{
      "access_token":  ts.AccessToken,
      "refresh_token": ts.RefreshToken,
   }
   c.JSON(http.StatusOK, tokens)
}
func Logout(c *gin.Context) {
   //If metadata is passed and the tokens valid, delete them from the redis store
   metadata, _ := tokenManager.ExtractTokenMetadata(c.Request)
   if metadata != nil {
      deleteErr := servers.HttpServer.RD.DeleteTokens(metadata)
      if deleteErr != nil {
         c.JSON(http.StatusBadRequest, deleteErr.Error())
         return
      }
   }
   c.JSON(http.StatusOK, "Successfully logged out")
}

创建Todo

定义Todo的结构体

type Todo struct {
    UserID string `json:"user_id"`
    Title string `json:"title"`
    Body string `json:"body"`
}

创建Todo的接口

package api
import (
   "github.com/gin-gonic/gin"
   "github.com/simple-jwt-auth/auth"
   "github.com/simple-jwt-auth/models"
   "net/http"
)
func CreateTodo(c *gin.Context) {
   var td models.Todo
   if err := c.ShouldBindJSON(&td); err != nil {
      c.JSON(http.StatusUnprocessableEntity, "invalid json")
      return
   }
   metadata, err := auth.ExtractTokenMetadata(c.Request)
   if err != nil {
      c.JSON(http.StatusUnauthorized, "unauthorized")
      return
   }
   td.UserID = metadata.UserId
   //you can proceed to save the  to a database
   c.JSON(http.StatusCreated, td)
}
func GetTodo(c *gin.Context) {
   metadata, err := auth.ExtractTokenMetadata(c.Request)
   if err != nil {
      c.JSON(http.StatusUnauthorized, "unauthorized")
      return
   }
   userId := metadata.UserId
   c.JSON(http.StatusOK, models.Todo{
      UserID: userId,
      Title:  "Return todo",
      Body:   "Return todo for testing",
   })
}

注册路由

func (s *Server) InitializeRoutes() {
   s.Router.POST("/login", api.Login)
   authorized := s.Router.Group("/")
   authorized.Use(gin.Logger())
   authorized.Use(gin.Recovery())
   authorized.Use(middleware.TokenAuthMiddleware())
   {
      authorized.POST("/api/todo",  middleware.Authorize("resource", "write", s.FileAdapter), api.CreateTodo)
      authorized.GET("/api/todo", middleware.Authorize("resource", "read", s.FileAdapter), api.GetTodo)
      authorized.POST("/logout", api.Logout)
      authorized.POST("/refresh", api.Refresh)
   }
}

最后在main.go文件中调用server层的Run方法即可运行

package main
import (
   "github.com/joho/godotenv"
   "github.com/simple-jwt-auth/servers"
   "log"
)
func init() {
   if err := godotenv.Load(); err != nil {
      log.Print("No .env file found")
   }
}
func main() {
   servers.Run()
   log.Println("Server exiting")
}

结果如下:

原作者仓库地址github

以上就是Golang基于JWT与Casbin身份验证授权实例详解的详细内容,更多关于Go JWT Casbin身份验证授权的资料请关注我们其它相关文章!

(0)

相关推荐

  • golang中gin框架接入jwt使用token验证身份

    目录 jwt 流程: 1.这里使用开源的 jwt-go 1.token 工具类 2. 使用该中间件 3. controller部分代码 jwt jwt的原理和session有点相像,其目的是为了解决rest api中无状态性 因为rest接口,需要权限校验.但是又不能每个请求都把用户名密码传入,因此产生了这个token的方法 流程: 用户访问auth接口,获取token 服务器校验用户传入的用户名密码等信息,确认无误后,产生一个token.这个token其实是类似于map的数据结构(jwt数据结

  • go gin+token(JWT)验证实现登陆验证

    1.准备 go get github.com/dgrijalva/jwt-go go get github.com/gin-gonic/gin  2.代码 package main import ( "errors" "fmt" "github.com/dgrijalva/jwt-go" "github.com/gin-gonic/gin" "net/http" "time" ) fun

  • go语言beego框架jwt身份认证实现示例

    目录 一 引入jwt 二 框架中引入jwt 三 使用 一 引入jwt jwt用户身份验证 go get github.com/dgrijalva/jwt-go 二 框架中引入jwt import ( "fmt" "github.com/astaxie/beego" "github.com/dgrijalva/jwt-go" "time" ) 三 使用 声明jwt需要用到的结构体 const ( KEY string = &qu

  • golang jwt+token验证的实现

    Token验证是验证用户身份的重要方式,在golang开发中具有广泛应用,文中主要阐述了利用jwt包加密后的token验证. 导入包: import ( "github.com/dgrijalva/jwt-go" ) // GenerateToken 生成Token func GenerateToken(mapClaims jwt.MapClaims, key string) (string, error) { token := jwt.NewWithClaims(jwt.Signin

  • go语言使用jwt认证的实现

    目录 加密 解密 这几天在学习nodejs,进一步了解npm,学习过程中解开了以前的一个疑惑,以前不知道token可以携带信息,只以为是用来做对比的,学到了jwt身份认证,知道了如何使用的,感觉很好用,但是我不用nodejs开发,所以就去看了下golang的,做下记录 刚学,博客内容写的可能不大对,因为基本都是自己的理解,术语用的可能也不到位,但是用起来倒是没问题,见谅 golang-jwt 项目仓库 使用以下命令获取 go get github.com/golang-jwt/jwt 加密 首先

  • 利用go-zero在Go中快速实现JWT认证的步骤详解

    关于JWT是什么,大家可以看看官网,一句话介绍下:是可以实现服务器无状态的鉴权认证方案,也是目前最流行的跨域认证解决方案. 要实现JWT认证,我们需要分成如下两个步骤 客户端获取JWT token. 服务器对客户端带来的JWT token认证. 1. 客户端获取JWT Token 我们定义一个协议供客户端调用获取JWT token,我们新建一个目录jwt然后在目录中执行 goctl api -o jwt.api,将生成的jwt.api改成如下: type JwtTokenRequest stru

  • Golang基于JWT与Casbin身份验证授权实例详解

    目录 JWT Header Payload Signature JWT的优势 JWT的使用场景 Casbin Casbin可以做什么 Casbin不可以做什么 Casbin的工作原理 实践 登录接口请求 Token实现 使用Redis存储Token信息 用Casbin做授权管理 实现Casbin的策略 创建Todo JWT JSON Web Toekn(JWT)是一个开放标准RFC 7519,以JSON的方式进行通信,是目前最流行的一种身份验证方式之一. eyJhbGciOiJIUzI1NiIs

  • JSP开发中Apache-HTTPClient 用户验证的实例详解

    JSP开发中Apache-HTTPClient 用户验证的实例详解 前言: 在微服务框架之外的系统中,我们经常会遇到使用httpClient进行接口调用的问题,除了进行白名单的设置,很多时候我们需要在接口调用的时候需要身份认证.翻了一下官方文档,解决方法很多,但是都不太符合实际业务场景,这里提供一种简单粗暴的解决方法. 解决方法:利用请求头,将验证信息保存起来. 实现代码: public class HttpClientUtils { protected static final Logger

  • Java身份证验证方法实例详解

    Java身份证验证方法实例详解 身份证号码验证 1.号码的结构 公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成.排列顺序从左至右依次为:六位数字地址码, 八位数字出生日期码,三位数字顺序码和一位数字校验码. 2.地址码(前六位数) 表示编码对象常住户口所在县(市.旗.区)的行政区划代码,按GB/T2260的规定执行. 3.出生日期码(第七位至十四位) 表示编码对象出生的年.月.日,按GB/T7408的规定执行,年.月.日代码之间不用分隔符. 4.顺序码(第十五位至十七位) 表示在同

  • js中自定义react数据验证组件实例详解

    我们在做前端表单提交时,经常会遇到要对表单中的数据进行校验的问题.如果用户提交的数据不合法,例如格式不正确.非数字类型.超过最大长度.是否必填项.最大值和最小值等等,我们需要在相应的地方给出提示信息.如果用户修正了数据,我们还要将提示信息隐藏起来. 有一些现成的插件可以让你非常方便地实现这一功能,如果你使用的是knockout框架,那么你可以借助于Knockout-Validation这一插件.使用起来很简单,例如我下面的这一段代码: ko.validation.locale('zh-CN');

  • 基于Bootstrap3表格插件和分页插件实例详解

    首先看下实现效果图,如果觉得还不错,请参考实现代码. 上面数据 下面分页 使用方法 1 导入bootstrap的css <link rel="stylesheet" href="css/v3/bootstrap.min.css"> 2 导入jquery <script src="js/jquery-1.10.1.min.js" type="text/javascript"></script>

  • Mongo复制集同步验证的实例详解

    mongo复制集同步验证的实例详解 第一步:在主节点上插入一条数据 Sql代码 rs0:PRIMARY> use imooc switched to db imooc rs0:PRIMARY> db.imooc.insert({"name":"imooc"}) WriteResult({ "nInserted" : 1 }) 第二步:在从节点查看数据,看是否同步 Sql代码 rs0:SECONDARY> use imooc sw

  • 基于python if 判断选择结构的实例详解

    代码执行结构为顺序结构.选择结构.循环结构. python判断选择结构[if] if 判断条件 #进行判断条件满足之后执行下方语句 执行语句 elif 判断条件 #在不满足上面所有条件基础上进行条件筛选匹配之后执行下方语句 执行语句 else #再不满足上面所有的添加下执行下方语句 执行语句 下面举一个简单的例子,看兜里有多少钱来决定吃什么饭. douliqian=2 if douliqian>200: print("小龙虾走起!!0.0") elif douliqian>

  • sklearn和keras的数据切分与交叉验证的实例详解

    在训练深度学习模型的时候,通常将数据集切分为训练集和验证集.Keras提供了两种评估模型性能的方法: 使用自动切分的验证集 使用手动切分的验证集 一.自动切分 在Keras中,可以从数据集中切分出一部分作为验证集,并且在每次迭代(epoch)时在验证集中评估模型的性能. 具体地,调用model.fit()训练模型时,可通过validation_split参数来指定从数据集中切分出验证集的比例. # MLP with automatic validation set from keras.mode

  • Yii2框架数据验证操作实例详解

    本文实例讲述了Yii2框架数据验证操作.分享给大家供大家参考,具体如下: 一.场景 什么情况下需要使用场景呢?当一个模型需要在不同情境中使用时,若不同情境下需要的数据表字段和数据验证规则有所不同,则需要定义多个场景来区分不同使用情境.例如,用户注册的时候需要填写email,登录的时候则不需要,这时就需要定义两个不同场景加以区分. 默认情况下模型的场景是由rules()方法申明的验证规则中使用到的场景决定的,也可以通过覆盖scenarios()方法来更具体地定义模型的所有场景,例如: public

  • Java基于享元模式实现五子棋游戏功能实例详解

    本文实例讲述了Java基于享元模式实现五子棋游戏功能.分享给大家供大家参考,具体如下: 一.模式定义 享元模式,以共享的方式高效地支持大量的细粒度对象.通过复用内存中已存在的对象,降低系统创建对象实例的性能消耗.享元的英文是Flyweight,表示特别小的对象,即细粒度对象. 二.模式举例 1. 模式分析 我们借用五子棋游戏来说明这一模式. 2. 享元模式静态类图 3. 代码示例 3.1 创建抽象棋子一AbstractChessman package com.demo.flyweight.obj

随机推荐