在本教程中,我们将学习如何使用Iris使用JWT(JSON Web令牌)身份验证,这是一个快速而简单的Web框架。 JWT是将当事方之间安全传输信息作为JSON对象的标准。它可用于身份验证用户并保护API端点免受未经授权的访问。
先决条件
要遵循本教程,您将需要:
- 在计算机上安装1.20或更高
- 对GO和IRIS的基本理解
- 您选择的文本编辑器或IDE(例如VS Code)
创建一个新的虹膜项目
首先,我们将使用go mod
命令创建一个新的IRIS项目。为此,运行以下命令:
$ mkdir iris-jwt
$ cd iris-jwt
$ go mod init iris-jwt
$ go get github.com/kataras/iris/v12@latest
这将创建一个名为iris-jwt
的新文件夹,其中包含以下文件:
iris-jwt
├── go.mod
└── go.sum
go.mod
文件包含模块名称和对虹膜的依赖性。 go.sum
文件包含依赖项的校验和。
接下来,我们将在同一文件夹中创建一个名为main.go
的文件,然后编写一些基本代码以在端口8080上启动iRIS服务器。我们稍后将修改此文件以添加我们的JWT逻辑。
package main
import "github.com/kataras/iris/v12"
func main() {
// Create a new Iris application.
app := iris.New()
// Register a simple GET handler at the root path.
app.Get("/", func(ctx iris.Context) {
ctx.WriteString("Hello, world!")
})
// Start the server at http://localhost:8080.
app.Listen(":8080")
}
运行服务器,运行以下命令:
$ go run main.go
您应该在终端中看到类似的东西:
Now listening on: http://localhost:8080
Application started. Press CTRL+C to shut down.
您也可以在浏览器中访问http://localhost:8080,并查看消息“ Hello,World!”。
定义用户模型和JWT索赔
现在,我们将在我们的main.go
文件中定义一个简单的用户模型和一个自定义的JWT声明结构。用户模型将表示用户的数据,例如用户名和密码。 JWT声称结构将包含我们要存储在JWT令牌中的信息,例如用户ID和到期时间。
我们还将为我们的秘密钥匙和令牌到期持续时间定义一些常数。
package main
import (
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/jwt"
)
// User is a simple user model.
type User struct {
ID int64 `json:"id"`
Username string `json:"username"`
Password string `json:"password"`
}
// Claims is a custom JWT claims struct.
type Claims struct {
jwt.Claims // embeds standard claims iat, exp and sub.
UserID int64 `json:"user_id"`
}
// Define some constants for our secret keys and token expiration durations.
const (
accessSecret = "my-access-secret"
refreshSecret = "my-refresh-secret"
accessExpire = 15 * time.Minute
refreshExpire = 24 * time.Hour
)
为JWT创建签名和验证者
接下来,我们将使用Iris Middleware/JWT软件包为JWT创建一个签名者和一个验证器。签名者将使用我们的秘密键和主张来生成和签名JWT令牌。验证程序将用于从客户端请求验证和验证JWT令牌。
我们将创建两个签名者和一个验证者,一对用于访问令牌,一个签名者用于刷新令牌。我们还将使用加密来添加额外的安全层。
package main
import (
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/jwt"
)
// ...
// Create a signer for the access token.
accessSigner := jwt.NewSigner(jwt.HS256, accessSecret, accessExpire).
// Use encryption for the access token.
WithEncryption([]byte("my-access-encryption-key"), nil)
// Create a signer for the refresh token.
refreshSigner := jwt.NewSigner(jwt.HS256, refreshSecret, refreshExpire).
// Use encryption for the refresh token.
WithEncryption([]byte("my-refresh-encryption-key"), nil)
// Create a verifier for the access token.
accessVerifier := jwt.NewVerifier(jwt.HS256, accessSecret).
// Use decryption for the access token.
WithDecryption([]byte("my-access-encryption-key"), nil)
// Use a blocklist to revoke tokens.
// .WithDefaultBlocklist()
创建一些模拟用户和登录处理程序
为了简单起见,我们将在地图中创建一些模拟用户,并使用它们来模拟身份验证。在真实应用程序中,您将使用数据库或其他存储系统存储和检索用户。
我们还将创建一个登录处理程序,该处理程序将从客户端请求中获取用户名和密码,检查它们是否与我们的一个模拟用户匹配,如果是的,请为该用户生成访问令牌和刷新令牌他们回到客户。
package main
import (
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/jwt"
)
// ...
// Create some mock users in a map.
users := map[string]User{
"alice": {ID: 1, Username: "alice", Password: "1234"},
"bob": {ID: 2, Username: "bob", Password: "5678"},
}
// Create a login handler that will generate JWT tokens for authenticated users.
loginHandler := func(ctx iris.Context) {
// Get the username and password from the request body.
var user User
if err := ctx.ReadJSON(&user); err != nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
// Check if the username and password match with one of our mock users.
if u, ok := users[user.Username]; !ok || u.Password != user.Password {
ctx.StopWithStatus(iris.StatusUnauthorized)
return
}
// Generate an access token with the user ID as the subject claim.
accessClaims := Claims{
Claims: jwt.Claims{Subject: u.Username},
UserID: u.ID,
}
accessToken, err := accessSigner.Sign(accessClaims)
if err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
// Generate a refresh token with the user ID as the subject claim.
refreshClaims := Claims{
Claims: jwt.Claims{Subject: u.Username},
UserID: u.ID,
}
refreshToken, err := refreshSigner.Sign(refreshClaims)
if err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
/* OR
tokenPair, err := refreshSigner.NewTokenPair(accessClaims, refreshClaims, refreshExpire)
// [handle err...]
ctx.JSON(tokenPair)
*/
// Send the tokens to the client as JSON.
ctx.JSON(iris.Map{
"access_token": string(accessToken),
"refresh_token": string(refreshToken),
"expires_in": int64(accessExpire.Seconds()),
})
}
创建一个受保护的API处理程序
接下来,我们将创建一个受保护的API处理程序,该处理程序只允许授权用户访问它。我们将使用访问验证程序作为中间件来验证和验证客户端请求的访问令牌。如果令牌有效,我们将从中提取用户ID并将其发送回客户端。
package main
import (
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/jwt"
)
// ...
// Create a protected API handler that will only allow authorized users to access it.
protectedHandler := func(ctx iris.Context) {
// Get the verified token from the context.
token := jwt.GetVerifiedToken(ctx) // important step.
// Get the custom claims from the token.
var claims Claims
if err := token.Claims(&claims); err != nil { // important step.
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
// Get the user ID from the claims.
userID := claims.UserID
// Send the user ID to the client as JSON.
// This is just an example, you can do whatever you want here.
ctx.JSON(iris.Map{
"user_id": userID,
})
}
创建刷新处理程序
最后,我们将创建一个刷新处理程序,该处理程序将允许用户使用其刷新令牌刷新其访问令牌。我们将使用刷新验证仪作为中间件来验证和验证客户端请求的刷新令牌。如果令牌有效,我们将为同一用户生成新的访问令牌和一个新的刷新令牌,并将其发送回客户端。
package main
import (
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/jwt"
)
// ...
// Create a refresh handler that will allow users to refresh their access tokens using their refresh tokens.
refreshHandler := func(ctx iris.Context) {
// Get the verified token from the context.
token := jwt.GetVerifiedToken(ctx)
// Get the custom claims from the token.
var claims Claims
if err := token.Claims(&claims); err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
// Get the user ID and username from the claims.
userID := claims.UserID
username := claims.Subject
// Generate a new access token with the same user ID and username as the subject claim.
accessClaims := Claims{
Claims: jwt.Claims{Subject: username},
UserID: userID,
}
accessToken, err := accessSigner.Sign(accessClaims)
if err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
// Generate a new refresh token with the same user ID and username as the subject claim.
refreshClaims := Claims{
Claims: jwt.Claims{Subject: username},
UserID: userID,
}
refreshToken, err := refreshSigner.Sign(refreshClaims)
if err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
// Send the new tokens to the client as JSON.
ctx.JSON(iris.Map{
"access_token": string(accessToken),
"refresh_token": string(refreshToken),
"expires_in": int64(accessExpire.Seconds()),
})
}
注册处理程序并测试应用程序
现在我们已经创建了所有处理程序,我们可以在IRIS应用程序中注册它们并测试我们的应用程序。我们将使用app.Post
方法来注册不同路径的登录,保护和刷新处理程序。我们还将使用accessVerifier.Verify
方法将验证者注册为其他受保护处理程序的中间Wares。
package main
import (
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/jwt"
)
// ...
func main() {
// Create a new Iris application.
app := iris.New()
// Register our login handler at /login path.
app.Post("/login", loginHandler)
// Register a single protected handler at /protected path.
// This handler verifies the request manually as we've seen above.
app.Post("/protected", protectedHandler)
// Register our refresh handler at /refresh path with refresh signer.
app.Post("/refresh", refreshHandler)
// You can also register the pre-defined jwt middleware for all protected routes
// which performs verification automatically and set the custom Claims to the Context
// for next handlers to use through: claims := jwt.Get(ctx).(*Claims).
app.Use(accessVerifier.Verify(func() interface{} {
return new(Claims)
}))
// [more routes...]
// Start the server at http://localhost:8080.
app.Listen(":8080")
}
要测试我们的应用程序,我们可以使用Curl或Postman等工具将HTTP请求发送到我们的服务器。以下是如何做到这一点的一些示例:
- 要登录Alice并获取访问令牌和刷新令牌,我们可以使用以下JSON机构将邮政请求发送给http://localhost:8080/login:
{
"username": "alice",
"password": "1234"
}
我们应该得到这样的回应:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzYwNjQ3MzEsImlhdCI6MTYzNjA2MzIzMSwic3ViIjoiYWxpY2UiLCJ1c2VySWQiOjF9.8f7a7b8f7a7b8f7a7b8f7a7b8f7a7b8f7a7b8f7a7b8f7a7b8f7a7b8f",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzYxNTAzMzEsImlhdCI6MTYzNjA2MzIzMSwic3ViIjoiYWxpY2UiLCJ1c2VySWQiOjF9.9f8a8b9f8a8b9f8a8b9f8a8b9f8a8b9f8a8b9f8a8b9f8a8b9f8a8b9f",
"expires_in": 900
}
- 要使用访问令牌访问受保护的API端点,我们可以使用以下标头向http://localhost:8080/protected发送邮政请求:
Authorization: Bearer <access-token>
我们应该得到这样的回应:
{
"user_id": 1
}
- 要使用刷新令牌刷新访问令牌,我们可以使用以下标头发送给http://localhost:8080/refresh的邮政请求:
Authorization: Bearer <refresh-token>
我们应该得到这样的回应:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzYwNjQ4NTMsImlhdCI6MTYzNjA2MzM1Mywic3ViIjoiYWxpY2UiLCJ1c2VySWQiOjF9.9g7a7c9g7a7c9g7a7c9g7a7c9g7a7c9g7a7c9g7a7c9g7a7c9g7a7c9g",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzYxNTA0NTMsImlhdCI6MTYzNjA2MzM1Mywic3ViIjoiYWxpY2UiLCJ1c2VySWQiOjF9.aG8aBbaG8aBbaG8aBbaG8aBbaG8aBbaG8aBbaG8aBbaG8aBbaG8aBbaG",
"expires_in": 900
}
结论
在本教程中,我们学会了如何使用iris使用JWT身份验证,这是一个快速而简单的Web框架。我们学会了如何为JWT创建签名者和验证器,如何生成和验证JWT令牌,如何使用JWT保护API端点以及如何刷新JWT令牌。我们还学会了如何使用虹膜中间件/JWT软件包,该软件包为JWT身份验证提供了常见的Iris处理程序。
您可以在github上找到本教程的完整代码:https://github.com/kataras/iris/tree/master/_examples/auth/jwt/tutorial和https://github.com/kataras/jwt。
如果您想了解更多有关IRIS的信息,则可以访问其官方网站:https://www.iris-go.com/
如果您想了解有关JWT的更多信息,则可以访问其官方网站:https://jwt.io/
我希望您喜欢这个教程,并发现它很有用。谢谢您的阅读。