详解Go-JWT-RESTful身份认证教程
介绍
本文将详细介绍如何使用Go语言实现一套基于JWT(JSON Web Token)的RESTful身份认证系统。RESTful是一种基于HTTP协议、符合REST原则的接口设计风格,使用JWT进行身份认证和授权可以轻松实现API的状态无关性和安全性。
实现步骤
生成JWT Token
生成JWT Token是RESTful身份认证系统的第一步。JWT Token可以在身份验证后与请求一起发送到API端点。Token包含已验证用户的信息,以及一些指定的元数据,并且这些元数据是经过数字签名的。我们可以使用golang-jwt库来生成JWT Token,具体操作如下:
import (
"github.com/dgrijalva/jwt-go"
)
type User struct {
UserID int
Name string
Email string
}
func generateJWT(user *User, privateKey []byte) (string, error) {
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["userID"] = user.UserID
claims["name"] = user.Name
claims["email"] = user.Email
tokenString, err := token.SignedString(privateKey)
if err != nil {
return "", err
}
return tokenString, nil
}
将上述代码复制到项目中并进行一些必要的修改,例如:对传递进来的privateKey
变量进行替换。
在上述代码中,我们先创建一个JWT Token对象,使用HS256算法对Token进行签名。我们还设置了一些已验证用户的信息。最后,我们使用私钥对Token进行签名,在调用SignedString
函数时,私钥会自动应用于Token签名。最终,通过generateJWT
函数返回的Token String将用于与其他API请求一起发送到API端点。
验证JWT Token
使用RESTful身份认证系统时,一旦JWT Token发出,接下来的步骤就是在每个请求上对Token进行验证,以确保Token还是有效的,且未被更改或伪造。在Go语言中,我们可以使用以下代码进行JWT Token验证:
import (
"github.com/dgrijalva/jwt-go"
)
func validateJWT(tokenString string, secretKey []byte) (*User, error) {
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 secretKey, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
user := &User{
UserID: claims["userID"].(int),
Name: claims["name"].(string),
Email: claims["email"].(string),
}
return user, nil
}
return nil, fmt.Errorf("Invalid token")
}
在上述代码中,我们使用jwt.Parse
函数进行Token解析,为了确保Token有效,我们首先检查Token是否是经过HS256算法签名的。接下来,我们使用提供的私钥对Token进行验证。如果Token在验证过程中是有效的,并包含所需的元数据,则我们可以创建一个User对象并返回它。如果Token无效,或者不包含所需的元数据,则将返回“Invalid token”错误消息。
实现RESTful API
有了JWT Token的生成和验证方法后,我们现在来介绍如何实现RESTful身份认证系统。在Go语言中,可以使用gofiber库来构建RESTful API。在下面的示例中,我们将展示一个基于gofiber的RESTful API,该API仅向被身份验证的用户提供访问权限。
import (
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
api := app.Group("/api")
api.Use(authenticate)
api.Get("/user", getCurrentUser)
app.Listen(":3000")
}
func authenticate(c *fiber.Ctx) error {
tokenString := c.Get("Authorization")
if tokenString == "" {
return fiber.ErrUnauthorized
}
tokenString = strings.TrimPrefix(tokenString, "Bearer ")
user, err := validateJWT(tokenString, []byte("secretKey"))
if err != nil {
return fiber.ErrUnauthorized
}
c.Locals("user", user)
return c.Next()
}
func getCurrentUser(c *fiber.Ctx) error {
user := c.Locals("user").(*User)
return c.JSON(user)
}
在上述代码中,我们首先创建了一个名为api
的组,它的URL前缀是/api
,并使用了authenticate
中间件进行身份认证。我们将getCurrentUser
函数作为响应API请求的控制器。
在authenticate
函数中,我们使用validateJWT
函数解析并验证Bearer Token(也就是JWT Token)。如果验证成功,则在请求全局对象(也就是CTX对象)中将已验证的用户对象存储为Locals,以便其他随后的请求处理函数使用。
注意,在实际应用程序中,您需要将密钥保存在数据库中,并在每个请求上使用该密钥进行Token验证。
示例
示例1
我们可以通过使用以下cURL命令来测试我们的RESTful API:
curl -H "Authorization: Bearer <Token String>" http://localhost:3000/api/user
这将返回当前用户的信息。如果令牌无效,则API将返回401(Unauthorized)错误消息。
示例2
下面是如何使用Go语言实现一个基于RESTful身份认证系统的网站:
package main
import (
"fmt"
"html/template"
"log"
"net/http"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Static("/static", "./static")
app.Get("/", home)
app.Post("/authenticate", authenticate)
app.Get("/dashboard", dashboard)
app.Listen(":3000")
}
func home(c *fiber.Ctx) error {
return c.Render("index", nil)
}
func dashboard(c *fiber.Ctx) error {
user := c.Locals("user").(*User)
return c.Render("dashboard", struct{ User *User }{User: user})
}
func authenticate(c *fiber.Ctx) error {
email := c.FormValue("email")
password := c.FormValue("password")
user, err := validateUser(email, password)
if err != nil {
return c.Render("index", struct{ Error string }{Error: "Invalid email or password"})
}
token, err := generateJWT(user, []byte("secretKey"))
if err != nil {
return c.Render("index", struct{ Error string }{Error: "Failed to generate token"})
}
c.Cookie(&fiber.Cookie{
Name: "jwt",
Value: token,
})
return c.Redirect("/dashboard")
}
func validateUser(email string, password string) (*User, error) {
// validate user's credentials
}
type User struct {
UserID int
Name string
Email string
}
type TemplateFactory struct {
Templates map[string]*template.Template
}
func (tf *TemplateFactory) Get(name string) (*template.Template, error) {
if t := tf.Templates[name]; t != nil {
return t, nil
}
t, err := template.ParseFiles(fmt.Sprintf("%s.html", name))
if err != nil {
return nil, err
}
tf.Templates[name] = t
return t, nil
}
func NewTemplateFactory() *TemplateFactory {
return &TemplateFactory{
Templates: make(map[string]*template.Template),
}
}
func init() {
tf := NewTemplateFactory()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
t, err := tf.Get("index")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
t.Execute(w, nil)
})
http.HandleFunc("/authenticate", func(w http.ResponseWriter, r *http.Request) {
email := r.PostFormValue("email")
password := r.PostFormValue("password")
user, err := validateUser(email, password)
if err != nil {
http.Redirect(w, r, "/?error=invalid_credentials", http.StatusTemporaryRedirect)
return
}
token, err := generateJWT(user, []byte("secretKey"))
if err != nil {
http.Redirect(w, r, "/?error=generate_token_failed", http.StatusTemporaryRedirect)
return
}
http.SetCookie(w, &http.Cookie{
Name: "jwt",
Value: token,
})
http.Redirect(w, r, "/dashboard", http.StatusTemporaryRedirect)
})
http.HandleFunc("/dashboard", func(w http.ResponseWriter, r *http.Request) {
token, err := r.Cookie("jwt")
if err != nil {
http.Redirect(w, r, "/?error=unauthorized", http.StatusTemporaryRedirect)
return
}
user, err := validateJWT(token.Value, []byte("secretKey"))
if err != nil {
http.Redirect(w, r, "/?error=validate_token_failed", http.StatusTemporaryRedirect)
return
}
t, err := tf.Get("dashboard")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
t.Execute(w, struct{ User *User }{User: user})
})
log.Fatal(http.ListenAndServe(":3000", nil))
}
在上面的示例中,我们使用了http
库而不是gofiber
库,但实现原理相同。我们为网站创建了一个主页并使用JWT保护了所有需要进行身份验证的页面。我们还在authenticate
路由上实现了一个用于用户身份验证的控制器。在我们验证用户身份后,我们使用generateJWT
函数为该用户生成JWT Token并保存该Token用于随后的所有请求中。
最后,在dashboard
中我们使用JWT Token来验证用户已登录,并在验证完成后向用户展示一个保护的用户仪表板。
这样,我们的RESTful身份认证系统就完成了搭建。
结论
本文介绍了如何使用Go语言和JWT来实现RESTful身份验证系统。我们使用gofiber库来创建API和Web App,并使用golang-jwt库来生成和验证JWT Token。本文还提供了两个示例代码,从根本上说明了如何生成JWT Token、如何验证JWT Token,并如何实现基于RESTful身份认证的Web应用程序。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Go-JWT-RESTful身份认证教程 - Python技术站