详解Go-JWT-RESTful身份认证教程

yizhihongxing

详解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技术站

(0)
上一篇 2023年6月11日
下一篇 2023年6月11日

相关文章

  • Vue中如何把hash模式改为history模式

    Vue中可以通过修改路由模式的方式,从默认的hash模式改为history模式。下面是具体的操作步骤: 1. 修改路由模式 在Vue项目中找到router文件夹,打开index.js文件,找到路由实例的创建代码部分(通常代码会类似如下): import Vue from ‘vue’ import Router from ‘vue-router’ import…

    JavaScript 2023年6月11日
    00
  • AngularJS实现表单手动验证和表单自动验证

    以下是关于“AngularJS实现表单手动验证和表单自动验证”的完整攻略: 一、表单手动验证 1.创建表单 首先,我们需要创建一个表单来进行手动验证。可以使用HTML的form标签、AngularJS的ngForm指令、ngModel指令和ngSubmit指令来完成这个步骤。 示例代码: <form name="myForm" ng…

    JavaScript 2023年6月10日
    00
  • async/await实现Promise.all()的方式

    使用async/await实现Promise.all()的方式,需要结合async函数和await关键字来实现,具体步骤如下: 定义一个异步函数asyncPromiseAll,接收一个Promise数组作为参数,并在该函数内部使用await关键字等待所有Promises的执行结果。 通过使用try-catch代码块,捕获异常并将其抛出以确保异步函数能够正常执…

    JavaScript 2023年5月27日
    00
  • 让alert不出现弹窗的两种方法

    下面我来详细讲解“让alert不出现弹窗的两种方法”。 方式一:使用 console.log 在 JavaScript 中,除了 alert 还有一个常用的调试工具就是 console.log。它可以在浏览器的控制台打印输出信息,便于我们查看函数的执行情况和数据的变化情况。 示例代码如下: function showMessage() { console.l…

    JavaScript 2023年6月11日
    00
  • 详解vue的hash跳转原理

    下面我将详细讲解“详解Vue的Hash跳转原理”的完整攻略。 什么是Hash路由 Hash路由是现代前端路由中最早出现的一种路由模式。它利用URL中的#字符来实现页面跳转而无需刷新页面。由于Hash前的URL部分不会发送到服务器,所以可以避免页面的重载,比较适合单页应用的开发。 Hash路由原理 在Hash路由模式下,我们使用JavaScript操作URL中…

    JavaScript 2023年6月11日
    00
  • vue实现微信浏览器左上角返回按钮拦截功能

    介绍:Vue可以通过使用路由导航守卫来拦截某些操作,其中之一就是拦截微信浏览器左上角的返回按钮。本攻略将详细介绍如何使用Vue及路由导航守卫实现微信浏览器左上角返回按钮的拦截功能。 步骤: 1.安装Vue Router 安装Vue Router是实现路由动态跳转的必要前提。使用npm或yarn,运行以下命令: npm install vue-router O…

    JavaScript 2023年6月11日
    00
  • javascript实现最长公共子序列实例代码

    下面是关于“javascript实现最长公共子序列实例代码”的完整攻略。 完整任务说明 本任务要求实现一个javascript代码,用于寻找两个字符串的最长公共子序列。 功能要求 输入两个字符串,比如”abcdfg”和”abdfg”,程序需要输出它们的最长公共子序列。 实现的算法需要支持对长度为m和n的字符串进行快速计算,时间复杂度需要为 O(m*n)。 背…

    JavaScript 2023年5月28日
    00
  • JavaScript中的16进制字符(改进)

    JavaScript中的16进制字符(改进) 在JavaScript中,我们可以使用16进制字符来表示字符或者数字。而由于16进制字符的特殊性,有时会导致一些诡异的问题,本文将为大家介绍如何使用JavaScript中的16进制字符。 1. 什么是16进制字符 在JavaScript中,16进制字符是以\x开头,后跟两个16进制数字所代表的字符。例如,’\x4…

    JavaScript 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部