Go 和 JWT - 简单的身份验证

Posted

技术标签:

【中文标题】Go 和 JWT - 简单的身份验证【英文标题】:Go and JWT - Simple authentication 【发布时间】:2016-07-14 03:56:04 【问题描述】:

我目前正在制作一个 API(使用 go),并且正在处理会话部分。 在研究了会话使用什么之后,我发现 JWT 真的很有趣。

但是,在一些教程之后,我不确定如何使用它。 所以这是我的想法:

func main() 

    router := mux.NewRouter().StrictSlash(true)

    router.HandleFunc("/login", login)
    router.HandleFunc("/logout", logout)
    router.HandleFunc("/register", register)

    http.ListenAndServe(":8080", router)

 

处理完这些请求后,我创建了不同的函数。

func login(w http.ResponseWriter, r *http.Request) 
    /*                                                                                                                                                                                                   
    Here I just have to search in my database (SQL, I know how to do it). If the user is registered, I create a token and give it to him, but how can I do it?                                           
    */
 

 func logout(w http.ResponseWriter, r *http.Request) 
    /*                                                                                                                                                                                                   
    I get a token and stop/delete it?                                                                                                                                                                    
    */
 

 func register(w http.ResponseWriter, r *http.Request) 
    /*                                                                                                                                                                                                   
    I search if the user isn't register and then, if it isn't, I create a user in the database (I know how to do it). I connect him but again, how to make a new token?                                  
    */
 

网络上的很多教程似乎很难,但我只想要一些简单的东西。我只想要一个与服务包一起使用的句柄包(上面的代码),以具有类似于引擎令牌身份验证的功能。

我不确定的第二点是令牌的保存。 如果用户连接自己,那么什么是最好的?每次用户运行他们的应用程序时,应用程序都会自行连接并从保存的信息(用户/密码)中获取新令牌,还是应用程序只是永久保存令牌?那么服务器呢,令牌是用 JWT 自动管理和保存的还是我必须把它放在我的 sql 数据库中?

感谢您的帮助!

编辑 1

谢谢!所以看了你的回答后,我像这样封装了我的代码(token.go)

package services

import (
    "fmt"
    "github.com/dgrijalva/jwt-go"
    "time"
    "../models"
)

var tokenEncodeString string = "something"

func createToken(user models.User) (string, error) 
    // create the token                                                                                                                                                                                  
    token := jwt.New(jwt.SigningMethodHS256)

    // set some claims                                                                                                                                                                                   
    token.Claims["username"] = user.Username;
    token.Claims["password"] = user.Password;
    token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()

    //Sign and get the complete encoded token as string                                                                                                                                                  
    return (token.SignedString(tokenEncodeString))


func parseToken(unparsedToken string) (bool, string) 
    token, err := jwt.Parse(unparsedToken, func(token *jwt.Token) (interface, error) 
            // Don't forget to validate the alg is what you expect:                                                                                                                                      
            if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok 
                    return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
            
            return myLookupKey(token.Header["kid"]), nil
    )

    if err == nil && token.Valid 
            return true, unparsedToken
     else 
            return false, ""
    
 

但是,我收到以下错误:“token.go: undefined: myLookupKey” 我在网上找了一个封装的函数,它有这个原型:

func ExampleParse(myToken string, myLookupKey func(interface) (interface, error)) 
 /* same code in my func parseToken() */

那么我的函数和这个函数有什么区别呢?这个怎么用?

谢谢!

【问题讨论】:

使用 Claims 映射的代码示例不起作用。 API 可能同时发生了变化。 老实说我不知道​​:/我很久以前做过,今天我换了工作,所以..:/ 不建议将密码存储在令牌声明中,令牌cookie没有加密,用户可以读取 【参考方案1】:

首先,您需要在 Golang 中导入 JWT 库(去 github.com/dgrijalva/jwt-go)。您可以在下面的链接中找到该库文档。

https://github.com/dgrijalva/jwt-go

首先,你需要创建一个令牌

// Create the token
token := jwt.New(jwt.SigningMethodHS256)
// Set some claims
token.Claims["foo"] = "bar"
token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
// Sign and get the complete encoded token as a string
tokenString, err := token.SignedString(mySigningKey)

其次,解析那个令牌

token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface, error) 
    // Don't forget to validate the alg is what you expect:
    if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok 
        return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
    
    return myLookupKey(token.Header["kid"]), nil
)

if err == nil && token.Valid 
    deliverGoodness("!")
 else 
    deliverUtterRejection(":(")

此外,还有一些在 GOlang 中使用 JWT 的示例,例如 https://github.com/slok/go-jwt-example

EDIT-1

package main

import (
    "fmt"
    "time"

    "github.com/dgrijalva/jwt-go"
)

const (
    mySigningKey = "WOW,MuchShibe,ToDogge"
)

func main() 
    createdToken, err := ExampleNew([]byte(mySigningKey))
    if err != nil 
        fmt.Println("Creating token failed")
    
    ExampleParse(createdToken, mySigningKey)


func ExampleNew(mySigningKey []byte) (string, error) 
    // Create the token
    token := jwt.New(jwt.SigningMethodHS256)
    // Set some claims
    token.Claims["foo"] = "bar"
    token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
    // Sign and get the complete encoded token as a string
    tokenString, err := token.SignedString(mySigningKey)
    return tokenString, err


func ExampleParse(myToken string, myKey string) 
    token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface, error) 
        return []byte(myKey), nil
    )

    if err == nil && token.Valid 
        fmt.Println("Your token is valid.  I like your style.")
     else 
        fmt.Println("This token is terrible!  I cannot accept this.")
    

【讨论】:

在 jwt-go 之后这个解决方案被破坏了。请更新答案。【参考方案2】:

只是为了更新@massoud-afrashteh 的答案。 在 jwt-go 的第 3 版中,设置蛤蜊应该是

// Set some claims
claims := make(jwt.MapClaims)
claims["foo"] = "bar"
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
token.Claims = claims

【讨论】:

【参考方案3】:

别忘了运行命令go get github.com/dgrijalva/jwt-go

另一种创建更简单的方法:

token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims
   "foo": "bar",
   "nbf": time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(),
)
tokenString, err := token.SignedString([]byte("your key"))
fmt.Println(tokenString, err)

【讨论】:

【参考方案4】:
func GenerateToken(mySigningKey []byte, username string) (string, error) 
    // Create the token
    token := jwt.New(jwt.SigningMethodRS512)
    claims := make(jwt.MapClaims)
    claims[collections.PARAM_USER_NAME] = username
    claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
    token.Claims = claims
    return token.SignedString(mySigningKey)

【讨论】:

以上是关于Go 和 JWT - 简单的身份验证的主要内容,如果未能解决你的问题,请参考以下文章

DRF 简单 jwt 从用户实例获取身份验证令牌或更改用户名和密码组合

在 Spring Boot 中使用 JWT 进行简单的身份验证

在 ASP.NET 核心中创建 JWT 是不是有更简单的方法?又名“JWT 身份验证的最佳实践?”

使用 jwt 身份验证保护反应应用程序

如何在身份验证中使用 JWT?

使用jwt身份验证保护react应用程序