利用Gin实现简单的用户管理(2)
经过第一篇笔记的学习,相信我们都对Gin有所了解。那接下来了解一下Gin框架下的cookie。
cookie
1. 设置cookie
使用Context对象的SetCookie方法。
代码示例
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/set_cookie", func(context *gin.Context) {
// 设置 Cookie 参数:名称、值、有效时间(秒)、路径、域名、是否https-only、是否HttpOnly
context.SetCookie("user_name", "name", 3600, "/", "localhost", false, true)
context.JSON(200, gin.H{
"message": "Cookie has been set!", }) })
router.Run(":8080")
}
设置cookie解释
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
name:cookie 的名称value: cookie的值,可以是用户的用户名,也可以是用户的唯一标识符(如用户ID)。maxAge: cookie的有效时间(单位是秒),如上面的代码示例是1小时path: cookie的作用路径,"/"表示所有的路径都能访问到这个cookie。domain: cookie的域名,标识cookie仅在domain域下有效。示例代码是localhost,如果想为127.0.0.1则""即可。secure: 是否仅通过 HTTPS 连接传输此 cookie。如果是true,则表示 cookie 只会通过 HTTPS 安全协议传输。httpOnly: 设置为true的作用是限制客户端 JavaScript 访问此 Cookie,增加安全性,避免跨站脚本攻击(XSS,Cross-Site Scripting)窃取敏感的 cookie 信息。
2. 获取cookie的值
在后续的请求中,你可以通过 context.Cookie 获取已设置的 Cookie 值。通常用于检查用户是否已登录。
示例:读取Cookie
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/get_cookie", func(context *gin.Context)
{ // 获取名为 "user_name" 的 Cookie
cookie, err := c.Cookie("user_name")
if err != nil {
c.JSON(400, gin.H{
"error": "Cookie not found", })
return
}
c.JSON(200, gin.H{
"cookie_value": cookie,
})
})
r.Run(":8080")
}
3. 删除cookie
要删除cookie,可以通过设置一个同名的cookie,并将其有效时间设为负值。
代码示例
r.GET("/delete_cookie", func(c *gin.Context) {
c.SetCookie("user_name", "", -1, "/", "localhost", false, true)
c.JSON(200, gin.H{
"message": "Cookie has been deleted!", })
})
学会了Gin和Cookie后,就可以着手写一个简单的用户管理系统了。
简单的用户管理系统
1. 搭建模板
- 创建全局切片结构体存放用户信息
- 搭建Gin框架
package main
import (
"github.com/gin-gonic/gin"
)
type User struct {
Name string `json:"name"`
Passward string `json:"password"`
Age int `json:"age,omitempty"`
Phone string `json:"phone,omitempty"`
QQ string `json:"qq,omitempty"`
Email string `json:"email,omitempty"`
}
var Use []User
func main(){
router := gin.Default()
router.GET("/",index)
router.Run()
}
func index(context *gin.Context) {
context.HTML(200, "index.html", nil)
}
2. 完善功能
-
register 注册
func register(context *gin.Context) {
context.HTML(200, "register.html", nil) //渲染注册页面
}
func auth1(context *gin.Context) { //控制注册
rwMutex.Lock() //使用读写锁
defer rwMutex.Unlock()
flag := 0
name := context.PostForm("username")
password := context.PostForm("password")
for i := range Use { //遍历用户切片
if Use[i].Name == name {
flag = 1
break
}
}
if flag == 0 {
Use = append(Use, User{Name: name, Passward: password})
context.HTML(200, "register1.html", nil)
} else {
context.HTML(200, "register2.html", nil)
}
}
-
login 登录
func auth2(context *gin.Context) { //控制登录
rwMutex.RLock()
defer rwMutex.RUnlock()
name := context.PostForm("username")
password := context.PostForm("password")
flag := 0
for i := range Use {
if Use[i].Name == name && Use[i].Passward == password {
flag = 1
break
}
}
if flag == 1 {
//登录成功,设置cookie
context.SetCookie("user_name",name,3600,"/","",false,true) //使用localhost 或 127.0.0.1
//填写"localhost" 或 ""
//返回登录成功的页面
context.HTML(200, "login1.html", gin.H{
"username":name,
})
} else {
//返回登录失败的页面
context.HTML(200, "login2.html", nil)
}
}
-
inquire 查询用户信息
func inquire(context *gin.Context) {
switch context.Request.Method {
case http.MethodGet: //如果方式为GET,则渲染查询页面
context.HTML(200, "inquire.html", nil)
case http.MethodPost: //如果方式为POST,则查询用户信息
rwMutex.RLock()
defer rwMutex.RUnlock()
inquire1(context)
}
}
func inquire1(context *gin.Context) {
flag := 0
num := -1
name := context.PostForm("username")
password := context.PostForm("password")
for i := range Use {
if Use[i].Name == name && Use[i].Passward == password {
flag = 1
num = i //记录此用户的下标
break
}
}
if flag == 0 { //有找到和没找到两种情况
context.HTML(200, "inquire1.html", nil)
} else {
context.HTML(200, "inquire2.html", nil)
context.JSON(http.StatusOK, Use[num])
}
}
-
alter 修改密码
func alter(context *gin.Context) {
//同上面的代码
switch context.Request.Method {
case http.MethodGet:
context.HTML(200, "alter.html", nil)
case http.MethodPost:
rwMutex.Lock()
defer rwMutex.Unlock()
alter1(context)
}
}
func alter1(context *gin.Context) {
name := context.PostForm("username")
password := context.PostForm("password")
password_new := context.PostForm("password_new")
num := -1
for i := range Use {
if Use[i].Name == name && Use[i].Passward == password {
num = i
break
}
}
if num == -1 {
context.HTML(200, "alter1.html", nil)
} else {
Use[num].Passward = password_new
context.HTML(200, "alter2.html", nil)
}
}
-
logOut 登出
func logOut(context *gin.Context){
mutex.Lock()
defer mutex.Unlock()
//设置一个同名的cookie并将有效时间改为-1
context.SetCookie("user_name","",-1,"/","",false,true)
context.HTML(200,"login.html",nil) //返回到重新登录的页面
}
-
mimi 检测cookie的设置情况
func mimi (context *gin.Context){
user_name, err := context.Cookie("user_name")
if err != nil{
context.HTML(http.StatusUnauthorized,"error.html",gin.H{
"error": "Unauthorized: Please log in first!",
})
return
}
// 根据 cookie 获取用户信息
var foundUser *User
for _, user := range Use {
if user.Name == user_name {
foundUser = &user
break
}
}
if foundUser == nil {
// 如果找不到对应的用户
context.HTML(http.StatusUnauthorized, "error.html",gin.H{
"error": "User not found:Invaild session!",
})
return
}
context.HTML(http.StatusOK, "mimi.html", nil) // 渲染页面
}
基本的功能已经完善,让我们看看全部代码吧:
package main
import (
"net/http"
"sync"
"github.com/gin-gonic/gin"
)
//var flag int = 0
//var num = 0
var mutex sync.Mutex
var rwMutex sync.RWMutex
type User struct {
Name string `json:"name"`
Passward string `json:"password"`
Age int `json:"age,omitempty"`
Phone string `json:"phone,omitempty"`
QQ string `json:"qq,omitempty"`
Email string `json:"email,omitempty"`
}
var Use []User
func index(context *gin.Context) {
context.HTML(200, "index.html", nil)
}
func login(context *gin.Context) {
context.HTML(200, "login.html", nil)
}
func register(context *gin.Context) {
context.HTML(200, "register.html", nil)
}
func auth1(context *gin.Context) { //控制注册
rwMutex.Lock()
defer rwMutex.Unlock()
flag := 0
name := context.PostForm("username")
password := context.PostForm("password")
for i := range Use {
if Use[i].Name == name {
flag = 1
break
}
}
if flag == 0 {
Use = append(Use, User{Name: name, Passward: password})
context.HTML(200, "register1.html", nil)
} else {
context.HTML(200, "register2.html", nil)
}
}
func auth2(context *gin.Context) { //控制登录
rwMutex.RLock()
defer rwMutex.RUnlock()
name := context.PostForm("username")
password := context.PostForm("password")
flag := 0
for i := range Use {
if Use[i].Name == name && Use[i].Passward == password {
flag = 1
break
}
}
if flag == 1 {
//登录成功,设置cookie
context.SetCookie("user_name",name,3600,"/","",false,true) //使用localhost 或 127.0.0.1
//填写"localhost" 或 ""
//返回登录的页面
context.HTML(200, "login1.html", gin.H{
"username":name,
})
} else {
context.HTML(200, "login2.html", nil)
}
}
func alter(context *gin.Context) {
switch context.Request.Method {
case http.MethodGet:
context.HTML(200, "alter.html", nil)
case http.MethodPost:
rwMutex.Lock()
defer rwMutex.Unlock()
alter1(context)
}
}
func alter1(context *gin.Context) {
name := context.PostForm("username")
password := context.PostForm("password")
password_new := context.PostForm("password_new")
num := -1
for i := range Use {
if Use[i].Name == name && Use[i].Passward == password {
num = i
break
}
}
if num == -1 {
context.HTML(200, "alter1.html", nil)
} else {
Use[num].Passward = password_new
context.HTML(200, "alter2.html", nil)
}
}
func inquire(context *gin.Context) {
switch context.Request.Method {
case http.MethodGet:
context.HTML(200, "inquire.html", nil)
case http.MethodPost:
rwMutex.RLock()
defer rwMutex.RUnlock()
inquire1(context)
}
}
func inquire1(context *gin.Context) {
flag := 0
num := -1
name := context.PostForm("username")
password := context.PostForm("password")
for i := range Use {
if Use[i].Name == name && Use[i].Passward == password {
flag = 1
num = i
break
}
}
if flag == 0 {
context.HTML(200, "inquire1.html", nil)
} else {
context.HTML(200, "inquire2.html", nil)
context.JSON(http.StatusOK, Use[num])
}
}
func mimi (context *gin.Context){
user_name, err := context.Cookie("user_name")
if err != nil{
context.HTML(http.StatusUnauthorized,"error.html",gin.H{
"error": "Unauthorized: Please log in first!",
})
return
}
// 根据 cookie 获取用户信息
var foundUser *User
for _, user := range Use {
if user.Name == user_name {
foundUser = &user
break
}
}
if foundUser == nil {
// 如果找不到对应的用户
context.HTML(http.StatusUnauthorized, "error.html",gin.H{
"error": "User not found:Invaild session!",
})
return
}
context.HTML(http.StatusOK, "mimi.html", nil) // 渲染页面
}
func logOut(context *gin.Context){
mutex.Lock()
defer mutex.Unlock()
//设置一个同名的cookie并将有效时间改为-1
context.SetCookie("user_name","",-1,"/","",false,true)
context.HTML(200,"login.html",nil)
}
func main() {
//获取路由对象
router := gin.Default()
//加载响应的HTML文件
router.LoadHTMLGlob("./templates/*") // * 是加载文件夹里的所有文件
router.GET("/", index)
router.GET("login", login)
router.GET("register", register)
router.POST("auth1", auth1)
router.POST("auth2", auth2)
router.GET("inquire", inquire)
router.POST("inquire", inquire)
router.GET("alter", alter)
router.POST("alter", alter)
router.GET("logout",logOut)
router.GET("mimi",mimi)
router.Run(":8090")
}
以及以上的HTML文件
附件:HTML文件
在templates文件夹下:
-
alter.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>修改密码</title>
</head>
<body>
<h3>修改密码</h3>
<form action="/alter" method="post">
<fieldset>
<legend>请输入您的用户名和原密码:</legend>
<label for="username">用户名:</label>
<input type="text" id="username" name="username" placeholder="请输入用户名" required>
<br><br>
<label for="password">原密码:</label>
<input type="password" id="password" name="password" placeholder="请输入原密码" required>
<br><br>
<label for="password">新密码:</label>
<input type="password" id="password" name="password_new" placeholder="请输入新密码" required>
<br><br>
<input type="submit" value="确认修改">
</fieldset>
</form>
</body>
</html>
-
alter1.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>修改失败</title>
</head>
<body>
<h3>没有此用户或者用户名或密码输入错误!!!</h3>
<h3>请重新输入</h3>
<form action="/alter" method="GET">
<fieldset>
<input type="submit" value="重新输入">
</fieldset>
</form>
</body>
</html>
-
alter2.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>修改成功</title>
</head>
<body>
<h3>恭喜你,修改成功</h3>
<h3>现在可以重新登录了</h3>
<form action="/login" method="GET">
<fieldset>
<input type="submit" value="重新登录">
</fieldset>
</form>
</body>
</html>
-
error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Access Denied</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
margin: 0;
padding: 0;
background-color: #f2f2f2;
}
.container {
margin-top: 20%;
}
h1 {
color: #ff4c4c;
}
p {
font-size: 18px;
color: #333;
}
a {
display: inline-block;
margin-top: 20px;
padding: 10px 20px;
text-decoration: none;
color: white;
background-color: #007bff;
border-radius: 5px;
}
a:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<h1>Access Denied</h1>
<p>{{ .error }}</p>
<a href="/login">Go to Login Page</a>
</div>
</body>
</html>
-
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户管理系统</title>
</head>
<body>
<h3>欢迎来到我的用户管理系统</h3>
<form action="/login" method="GET">
<input type="submit" value="登录">
</form>
<form action="/register" method="GET">
<input type="submit" value="注册">
</form>
</form>
<h4>秘密通道</h4>
<form action="/mimi" method="GET">
<input type="submit" value="秘密通道">
</form>
</body>
</html>
-
inquire.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>查询用户</title>
</head>
<body>
<h3>查询用户</h3>
<form action="/inquire" method="post">
<fieldset>
<legend>请输入您的用户名和密码:</legend>
<label for="username">用户名:</label>
<input type="text" id="username" name="username" placeholder="请输入用户名" required>
<br><br>
<label for="password">密码:</label>
<input type="password" id="password" name="password" placeholder="请输入密码" required>
<br><br>
<input type="submit" value="确认查询">
</fieldset>
</form>
</body>
</html>
-
inquire1.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>查询失败</title>
</head>
<body>
<h3>没有此用户或者用户名或密码输入错误!!!</h3>
<h3>请重新输入</h3>
<form action="/inquire" method="GET">
<fieldset>
<input type="submit" value="重新输入">
</fieldset>
</form>
</body>
</html>
-
inquire2.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>修改成功</title>
</head>
<body>
<h3>恭喜你,查询成功</h3>
<h3>现在可以查看该用户的信息了</h3>
</body>
</html>
-
login.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录页面</title>
</head>
<body>
<h3>用户登录</h3>
<form action="/auth2" method="post">
<fieldset>
<legend>请输入您的登录信息:</legend>
<label for="username">用户名:</label>
<input type="text" id="username" name="username" placeholder="请输入用户名" required>
<br><br>
<label for="password">密码:</label>
<input type="password" id="password" name="password" placeholder="请输入密码" required>
<br><br>
<input type="submit" value="登录">
</fieldset>
</form>
<h4>修改密码</h4>
<form action="/alter" method="GET">
<input type="submit" value="修改密码">
</form>
</form>
<h4>查询用户</h4>
<form action="/inquire" method="GET">
<input type="submit" value="查询用户">
</form>
</form>
<h4>秘密通道</h4>
<form action="/mimi" method="GET">
<input type="submit" value="秘密通道">
</form>
</body>
</html>
-
login1.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录成功</title>
</head>
<body>
<h3>恭喜你,登录成功!</h3>
</form>
<h4>登出</h4>
<form action="/logout" method="GET">
<input type="submit" value="登出">
</form>
</form>
<h4>秘密通道</h4>
<form action="/mimi" method="GET">
<input type="submit" value="秘密通道">
</form>
</body>
</html>
-
login2.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录失败</title>
</head>
<body>
<h3>用户名或密码输入错误!!!</h3>
<h3>请重新登录</h3>
<form action="/login" method="GET">
<fieldset>
<input type="submit" value="重新登录">
</fieldset>
</form>
</body>
</html>
-
mimi.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>反应测试游戏</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
#game-container {
text-align: center;
border: 2px solid #333;
border-radius: 10px;
padding: 20px;
background-color: white;
width: 300px;
}
#box {
width: 100%;
height: 150px;
margin: 20px 0;
background-color: red;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 24px;
font-weight: bold;
cursor: pointer;
}
#message {
font-size: 18px;
margin-top: 10px;
}
</style>
</head>
<body>
<div id="game-container">
<h1>反应测试游戏</h1>
<div id="box">等待中...</div>
<div id="message">点击绿色框!</div>
<button id="restart" style="display:none;">重新开始</button>
</div>
<script>
let box = document.getElementById('box');
let message = document.getElementById('message');
let restartButton = document.getElementById('restart');
let startTime, endTime;
let isGreen = false;
// 随机延迟函数
function getRandomDelay() {
return Math.random() * 3000 + 1000; // 1到4秒
}
// 开始游戏
function startGame() {
isGreen = false;
box.style.backgroundColor = 'red';
box.textContent = '等待中...';
message.textContent = '点击绿色框!';
restartButton.style.display = 'none';
// 设置随机时间后变绿
setTimeout(() => {
isGreen = true;
box.style.backgroundColor = 'green';
box.textContent = '现在点击!';
startTime = Date.now();
}, getRandomDelay());
}
// 检测点击事件
box.addEventListener('click', () => {
if (isGreen) {
endTime = Date.now();
let reactionTime = endTime - startTime;
box.textContent = '点击完成!';
message.textContent = `你的反应时间是:${reactionTime} 毫秒!`;
restartButton.style.display = 'block';
} else {
box.textContent = '太早了!';
message.textContent = '你需要等到绿色框出现!';
restartButton.style.display = 'block';
}
});
// 重新开始按钮
restartButton.addEventListener('click', startGame);
// 初始化游戏
startGame();
</script>
</body>
</html>
-
register.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册页面</title>
</head>
<body>
<h3>用户注册</h3>
<form action="/auth1" method="post">
<fieldset>
<legend>请输入您的注册信息:</legend>
<label for="username">用户名:</label>
<input type="text" id="username" name="username" placeholder="请输入用户名" required>
<br><br>
<label for="password">密码:</label>
<input type="password" id="password" name="password" placeholder="请输入密码" required>
<br><br>
<input type="submit" value="注册">
</fieldset>
</form>
</body>
</html>
-
register1.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册成功</title>
</head>
<body>
<h3>恭喜你,注册成功!</h3>
<h3>现在可以登录了</h3>
<form action="/login" method="GET">
<fieldset>
<input type="submit" value="登录">
</fieldset>
</form>
</form>
<h4>秘密通道</h4>
<form action="/mimi" method="GET">
<input type="submit" value="秘密通道">
</form>
</body>
</html>
-
register2.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册失败</title>
</head>
<body>
<h3>用户名重复,注册失败</h3>
<h3>请重新注册</h3>
<form action="/register" method="GET">
<fieldset>
<input type="submit" value="重新注册">
</fieldset>
</form>
</body>
</html>
小结
这个简单的用户管理系统就完成了,有很多可以继续优化的点:
-
在注册或登录时,可以避免对整个
Use切片加锁,而是使用并发安全的数据结构,比如sync.Map或带锁的哈希表来存储用户数据。 -
使用更细粒度的锁,避免全局锁阻塞所有用户操作。
-
查询性能优化
- 当前的
for循环查找用户效率较低(O(n))。可以使用map结构替代[]User,实现 O(1) 的查询时间。
- 当前的
-
代码结构进一步优化。