Golang Gin系列-7:认证和授权

在本章中,我们将探讨Gin框架中身份验证和授权的基本方面。这包括实现基本的和基于令牌的身份验证,使用基于角色的访问控制,应用中间件进行授权,以及使用HTTPS和漏洞防护保护应用程序。

在这里插入图片描述

实现身份认证

Basic 认证

Basic 认证是内置于HTTP协议中的简单身份验证方案。它包括在每个请求中发送用户名和密码。尽管它很容易实现,但出于安全考虑,不建议将其用于生产环境应用程序。

示例:Basic 认证

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()authorized := r.Group("/", gin.BasicAuth(gin.Accounts{"admin": "password123",}))authorized.GET("/protected", func(c *gin.Context) {user := c.MustGet(gin.AuthUserKey).(string)c.JSON(http.StatusOK, gin.H{"user": user, "message": "Welcome to the protected route!"})})r.Run()
}

在这个例子中,杜松子酒。BasicAuth中间件检查有效的用户名和密码。如果凭证正确,则继续请求;否则,它返回401 Unauthorized状态。

Token-based认证

Token-based认证,特别是使用JSON Web令牌(JWT),是一种更安全、可扩展的方法。jwt是无状态的,这意味着服务器不需要存储会话信息。
在这里插入图片描述

示例:Token-based认证

在本例中,“login”端点为有效凭证生成JWT,“authenticateJWT”中间件检查token的有效性。

package mainimport ("github.com/gin-gonic/gin""github.com/dgrijalva/jwt-go""net/http""time"
)var jwtKey = []byte("my_secret_key")type Claims struct {Username string `json:"username"`jwt.StandardClaims
}func main() {r := gin.Default()r.POST("/login", login)r.GET("/protected", authenticateJWT(), protected)r.Run()
}func login(c *gin.Context) {var creds struct {Username string `json:"username"`Password string `json:"password"`}if err := c.ShouldBindJSON(&creds); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})return}if creds.Username == "admin" && creds.Password == "password123" {expirationTime := time.Now().Add(5 * time.Minute)claims := &Claims{Username: creds.Username,StandardClaims: jwt.StandardClaims{ExpiresAt: expirationTime.Unix(),},}token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)tokenString, err := token.SignedString(jwtKey)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"})return}c.JSON(http.StatusOK, gin.H{"token": tokenString})} else {c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})}
}func authenticateJWT() gin.HandlerFunc {return func(c *gin.Context) {tokenString := c.GetHeader("Authorization")claims := &Claims{}token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {return jwtKey, nil})if err != nil || !token.Valid {c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})c.Abort()return}c.Set("username", claims.Username)c.Next()}
}func protected(c *gin.Context) {username := c.MustGet("username").(string)c.JSON(http.StatusOK, gin.H{"message": "Welcome to the protected route!", "user": username})
}
  • r.GET("/protected", authenticateJWT(), protected):定义 GET 请求的路由/protected,这是受保护的路由,请求会先经过authenticateJWT中间件进行 JWT 验证,验证通过后再由protected函数处理。
  • login()方法首先定义一个匿名结构体creds用于接收客户端发送的 JSON 格式的用户名和密码。
  • c.ShouldBindJSON(&creds):尝试将客户端发送的 JSON 数据绑定到creds结构体上,如果绑定失败则返回 HTTP 400 错误。
  • 检查用户名和密码是否正确,如果正确则生成一个 JWT。
    • expirationTime:设置 JWT 的过期时间为当前时间加上 5 分钟。
    • claims:创建一个Claims结构体实例,包含用户名和过期时间。
    • jwt.NewWithClaims(jwt.SigningMethodHS256, claims):使用 HS256 算法创建一个新的 JWT。
    • token.SignedString(jwtKey):使用jwtKey对 JWT 进行签名,生成 JWT 字符串。
    • 如果签名成功,将 JWT 字符串作为响应返回给客户端;否则返回 HTTP 500 错误。
  • 如果用户名或密码不正确,返回 HTTP 401 错误。

授权技术

在这里插入图片描述

基于角色的访问控制

RBAC (Role-based access control)是一种基于角色的访问控制技术。这种方法可以有效地管理不同级别用户的权限。

示例:基于角色的访问控制

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()admin := r.Group("/admin", roleMiddleware("admin"))admin.GET("/dashboard", adminDashboard)r.Run()
}func roleMiddleware(role string) gin.HandlerFunc {return func(c *gin.Context) {userRole := c.GetHeader("Role")if userRole != role {c.JSON(http.StatusForbidden, gin.H{"error": "Access forbidden"})c.Abort()return}c.Next()}
}func adminDashboard(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "Welcome to the admin dashboard!"})
}

在这个例子中,roleMiddleware 检查用户是否有合适的角色来访问路由。

授权的中间件

中间件功能可用于集中处理授权逻辑,从而更容易管理整个应用程序的访问控制。

示例:授权的中间件

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.Use(authMiddleware)r.GET("/profile", userProfile)r.Run()
}func authMiddleware(c *gin.Context) {token := c.GetHeader("Authorization")if token != "valid-token" {c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})c.Abort()return}c.Next()
}func userProfile(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "User profile"})
}

在本例中,authMiddleware检查请求是否为有效的授权 token。

应用程序安全

HTTPS设置

设置HTTPS对于通过加密客户机和服务器之间交换的数据来保护应用程序至关重要。

示例:HTTPS设置

要设置HTTPS,你需要有效的SSL证书。出于开发目的,可以使用自签名证书。

package mainimport ("github.com/gin-gonic/gin""log"
)func main() {r := gin.Default()// Your routes here// Replace with your certificate and key fileserr := r.RunTLS(":443", "server.crt", "server.key")if err != nil {log.Fatal("Failed to start server: ", err)}
}

在本例中,RunTLS方法使用提供的证书和密钥文件以HTTPS启动服务器。

预防常见安全漏洞

防止SQL注入

始终使用参数化查询或ORM库来防止SQL注入攻击。

package mainimport ("github.com/gin-gonic/gin""net/http""database/sql"_ "github.com/go-sql-driver/mysql"
)func main() {r := gin.Default()db, err := sql.Open("mysql", "user:password@/dbname")if err != nil {panic(err)}r.GET("/user/:id", func(c *gin.Context) {id := c.Param("id")var name stringerr := db.QueryRow("SELECT name FROM users WHERE id = ?", id).Scan(&name)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "User not found"})return}c.JSON(http.StatusOK, gin.H{"name": name})})r.Run()
}

在本例中,查询使用参数化语句来防止SQL注入。这里的 ? 是占位符,db.QueryRow 方法会先把 SQL 语句发送给数据库进行预编译,之后再把 id 作为参数传递给数据库。数据库会把参数当作普通的数据来处理,而不会将其作为 SQL 语句的一部分进行解析,这样就能有效防止恶意用户通过构造特殊输入来改变 SQL 语句的逻辑,进而避免 SQL 注入。

如果代码没有使用预编译语句,而是直接把用户输入拼接到 SQL 语句中,例如:

query := "SELECT name FROM users WHERE id = " + id
err := db.QueryRow(query).Scan(&name)

这种情况下,若恶意用户将 id 设置为特殊的值,如 1 OR 1=1,那么拼接后的 SQL 语句就会变成 SELECT name FROM users WHERE id = 1 OR 1=1,这样会使查询条件恒为真,从而可能导致数据库中的敏感信息被泄露,这就是 SQL 注入的危害。

防止跨站脚本(XSS)

清除任何用户输入以防止XSS攻击。使用像“blumonday”这样的库来清理HTML输入。

package mainimport ("github.com/gin-gonic/gin""github.com/microcosm-cc/bluemonday"
)func main() {r := gin.Default()r.POST("/comment", func(c *gin.Context) {comment := c.PostForm("comment")policy := bluemonday.UGCPolicy()sanitizedComment := policy.Sanitize(comment)c.JSON(200, gin.H{"comment": sanitizedComment})})r.Run()
}

在本例中,用户输入在使用之前被清理。

最后总结

通过遵循本章概述的实践,你可以在Gin应用程序中实现健壮的身份验证和授权机制,确保它们是安全可靠的。Gin,愈学习愈快乐, Go!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/68555.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CTF-web: phar反序列化+数据库伪造 [DASCTF2024最后一战 strange_php]

step 1 如何触发反序列化? 漏洞入口在 welcome.php case delete: // 获取删除留言的路径,优先使用 POST 请求中的路径,否则使用会话中的路径 $message $_POST[message_path] ? $_POST[message_path] : $_SESSION[message_path]; $msg $userMes…

C语言自定义数据类型详解(一)——结构体类型(上)

什么是自定义数据类型呢?顾名思义,就是我们用户自己定义和设置的类型。 在C语言中,我们的自定义数据类型一共有三种,它们分别是:结构体(struct),枚举(enum),联合(union)。接下来,我…

LeetCode 0219.存在重复元素 II:哈希表

【LetMeFly】219.存在重复元素 II:哈希表 力扣题目链接:https://leetcode.cn/problems/contains-duplicate-ii/ 给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 i 和 j ,满足 nums[i] nums[j] 且 abs…

Android车机DIY开发之学习篇(七)NDK交叉工具构建

Android车机DIY开发之学习篇(七)NDK交叉工具构建 1.ubuntu安装GCC sudo apt-get update sudo apt-get install gcc g sudo gcc --version sudo g --version 2.测试GCC VSCODE中新建Hello.c编译 #include <stdio.h> int main(void) { printf(“Hello, this is a progr…

前端性能优化:HMR热更新和预获取加载

最近发现项目开发&#xff0c;有点加载快&#xff0c;有点却是卡机式&#xff0c;甚至刷新导致白屏情况。于是&#xff0c;我找开发和性能优化的方法&#xff0c;找到下面几种。 本文将深入探讨 预获取&#xff08;Prefetch&#xff09;、动态导入&#xff08;Dynamic Import&…

SpringCloud系列教程:微服务的未来(十八)雪崩问题、服务保护方案、Sentinel快速入门

前言 在分布式系统中&#xff0c;雪崩效应&#xff08;Avalanche Effect&#xff09;是一种常见的故障现象&#xff0c;通常发生在系统中某个组件出现故障时&#xff0c;导致其他组件级联失败&#xff0c;最终引发整个系统的崩溃。为了有效应对雪崩效应&#xff0c;服务保护方…

升级到Mac15.1后pod install报错

升级Mac后&#xff0c;Flutter项目里的ios项目运行 pod install报错&#xff0c; 遇到这种问题&#xff0c;不要着急去百度&#xff0c;大概看一下报错信息&#xff0c;每个人遇到的问题都不一样。 别人的解决方法并不一定适合你&#xff1b; 下面是报错信息&#xff1a; #…

C语言中的函数有哪些种类型

在C语言中&#xff0c;函数可以根据多种方式进行分类。以下是一些常见的函数类型及其解释&#xff1a; 一、根据定义来源分类 库函数 也称为标准函数&#xff0c;由C标准库或其他库提供。用户无需定义&#xff0c;可直接使用&#xff0c;只需在程序前包含函数的原型声明的头文…

寻找两个正序数组的中位数:分治法与二分查找的结合

寻找两个正序数组的中位数&#xff1a;分治法与二分查找的结合 在算法领域&#xff0c;“寻找两个正序数组的中位数” 是一道经典的高频面试题&#xff08;LeetCode 第 4 题&#xff09;。它不仅考察基本的数组操作&#xff0c;还涉及二分查找与分治思想的结合。今天&#xff…

STM32 PWM驱动舵机

接线图&#xff1a; 这里将信号线连接到了开发板的PA1上 代码配置&#xff1a; 这里的PWM配置与呼吸灯一样&#xff0c;呼吸灯连接的是PA0引脚&#xff0c;输出比较单元用的是OC1通道&#xff0c;这里只需改为OC2通道即可。 完整代码&#xff1a; #include "servo.h&quo…

使用 concurrently 实现前后端一键启动

使用 concurrently 实现前后端一键启动 本文适合&#xff1a; 前后端分离项目&#xff08;如 React Node.js&#xff09;&#xff0c;希望通过一条命令同时启动前端和后端服务。 工具链&#xff1a; Node.js、npm、concurrently。 耗时&#xff1a; 3 分钟。 文章目录 使用 c…

基于STM32的智能语音控制灯光系统设计

目录 引言系统设计 硬件设计软件设计 系统功能模块 语音识别模块灯光控制模块模式切换与场景管理模块用户交互与显示模块远程控制与数据上传模块 控制算法 语音识别与命令解析算法灯光强度与颜色调节算法数据记录与远程反馈算法 代码实现 语音识别与灯光控制代码场景模式与定时…

【NLP251】NLP RNN 系列网络

NLP251 系列主要记录从NLP基础网络结构到知识图谱的学习 &#xff11;.原理及网络结构 &#xff11;.&#xff11;&#xff32;&#xff2e;&#xff2e; 在Yoshua Bengio论文中( http://proceedings.mlr.press/v28/pascanu13.pdf )证明了梯度求导的一部分环节是一个指数模型…

OpenCV:在图像中添加噪声(瑞利、伽马、脉冲、泊松)

目录 简述 1. 瑞利噪声 2. 伽马噪声 3. 脉冲噪声 4. 泊松噪声 总结 相关阅读 OpenCV&#xff1a;在图像中添加高斯噪声、胡椒噪声-CSDN博客 OpenCV&#xff1a;高通滤波之索贝尔、沙尔和拉普拉斯-CSDN博客 OpenCV&#xff1a;图像处理中的低通滤波-CSDN博客 OpenCV&…

小智 AI 聊天机器人

小智 AI 聊天机器人 &#xff08;XiaoZhi AI Chatbot&#xff09; &#x1f449;参考源项目复现 &#x1f449; ESP32SenseVoiceQwen72B打造你的AI聊天伴侣&#xff01;【bilibili】 &#x1f449; 手工打造你的 AI 女友&#xff0c;新手入门教程【bilibili】 项目目的 本…

如何学习Java后端开发

文章目录 一、Java 语言基础二、数据库与持久层三、Web 开发基础四、主流框架与生态五、分布式与高并发六、运维与部署七、项目实战八、持续学习与提升总结路线图 学习 Java 后端开发需要系统性地掌握多个技术领域&#xff0c;从基础到进阶逐步深入。以下是一个详细的学习路线和…

亚博microros小车-原生ubuntu支持系列:16 机器人状态估计

本来想测试下gmapping建图&#xff0c;但是底层依赖了yahboomcar_bringup做底层的数据处理&#xff0c;所以先把依赖的工程导入。 程序启动后&#xff0c;会订阅imu和odom数据&#xff0c;过滤掉一部分的imu数据后&#xff0c;然后与odom数据进行融合&#xff0c;最后输出一个…

C++中的类与对象(中)

在上一节中&#xff0c;我们初步了解了一下&#xff0c;C中的类&#xff0c;这一概念&#xff0c;这一节让我们进一步深入了解一下。 文章目录 目录 前言 一、类中的默认成员函数 1.1 构造函数 构造函数的特点&#xff1a; 1.2 析构函数 析构函数的特点&#xff1a; 1.3 …

Java坦克大战

画图基础 先定义画板类, 用于画图 // 定义画板类画板&#xff0c;用于画图 class MyPanel extends JPanel{// Graphics g是一只画笔// Graphics 提供了绘画方法public void paint(Graphics g){super.paint(g);g.drawOval(0,0,100,100);}}定义窗口类,用于显示画板 // public类…

Kotlin开发(六):Kotlin 数据类,密封类与枚举类

引言 想象一下&#xff0c;你是个 Kotlin 开发者&#xff0c;敲着代码忽然发现业务代码中需要一堆冗长的 POJO 类来传递数据。烦得很&#xff1f;别急&#xff0c;Kotlin 贴心的 数据类 能帮你自动生成 equals、hashCode&#xff0c;直接省时省力&#xff01;再想想需要多种状…