users/login.html
html
复制代码
{{define "users/login.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<form method="post" action="/login">
username:<input style="align-content: center" type="text" name="username" onfocus="change()"><br/>
password:<input type="password" name="password" onfocus="change()"><br/>
<input type="submit" value="login">
</form>
<h1 id = "tag" style="color: red">{{.Message}}</h1>
</body>
<script>
function change() {
let tag = document.getElementById("tag");
tag.innerHTML="<h1></h1>";
}
</script>
</html>
{{end}}
default/index.html
html
复制代码
{{define "default/index.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index页面</title>
</head>
<body>
<h1 style="color: chocolate; align-content: center">{{.Content}}</h1>
</body>
</html>
{{end}}
controllers/users.go
go
复制代码
package controllers
import (
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"net/http"
)
package controllers
type Auth struct {
Username string
Password string
}
const DefaultAuthInfoKey = "AUTH"
func LoginGet() gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.HTML(http.StatusOK, "users/login.html", nil)
}
}
func LogoutGet() gin.HandlerFunc {
return func(ctx *gin.Context) {
session := sessions.Default(ctx)
session.Delete(DefaultAuthInfoKey)
session.Options(sessions.Options{
Path: "/",
Domain: "localhost",
MaxAge: 0,
Secure: true,
HttpOnly: false,
SameSite: 0,
})
session.Save()
ctx.Redirect(http.StatusFound, "/login")
ctx.Abort()
}
}
func LoginPost() gin.HandlerFunc {
return func(ctx *gin.Context) {
username := ctx.PostForm("username")
password := ctx.PostForm("password")
if username != "admin" || password != "admin" {
ctx.HTML(http.StatusOK, "users/login.html", gin.H{
"Message": "用户名或密码错误!",
})
ctx.Abort()
return
}
//登录成功
auth := Auth{Username: username, Password: password}
ctx.Set(DefaultAuthInfoKey, auth)
session := sessions.Default(ctx)
session.Set(DefaultAuthInfoKey, auth)
session.Save() //TODO 必须Save,否则不生效
ctx.Redirect(http.StatusFound, "/")
}
}
func Index() gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.HTML(http.StatusOK, "default/index.html", gin.H{
"Content": "欢迎回来:" + ctx.MustGet(DefaultAuthInfoKey).(Auth).Username,
})
}
}
routers/router.go
go
复制代码
package routers
import (
"encoding/gob"
"github.com/coolbit/gin_sample/controllers"
"github.com/coolbit/gin_sample/middleware"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"net/http"
)
var router *gin.Engine
func GetRouter() *gin.Engine {
return router
}
func AuthRequired() gin.HandlerFunc {
return func(ctx *gin.Context) {
session := sessions.Default(ctx)
auth := session.Get(controllers.DefaultAuthInfoKey)
au, ok := auth.(controllers.Auth)
if !ok || au.Username == "" {
ctx.Redirect(http.StatusFound, "/login")
ctx.Abort()
return
}
ctx.Set(controllers.DefaultAuthInfoKey, auth) // 设置权限信息,供给当前请求链路使用
session.Options(sessions.Options{ // 必须Save后才生效
Path: "/",
Domain: "localhost",
MaxAge: 7 * 24 * 60 * 60, // 7天
Secure: true,
HttpOnly: false,
SameSite: 0,
})
session.Set(controllers.DefaultAuthInfoKey, au)
session.Save() //"已刷新session"
}
}
func init() {
router = gin.Default()
// Set a lower memory limit for multipart forms (default is 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.Static("/static", "static")
router.LoadHTMLGlob("views/**/*")
gob.Register(controllers.Auth{}) // TODO 这个必须有,没有就写不了cookie
store := cookie.NewStore([]byte("我是密钥"))
router.Use(sessions.Sessions("SESSION_ID", store))
router.GET("/login", controllers.LoginGet())
router.POST("/login", controllers.LoginPost())
router.Use(AuthRequired()) // 鉴权插件设置在这里
router.GET("/", controllers.Index())
}
main.go
go
复制代码
package main
import (
"github.com/coolbit/gin_sample/routers"
)
func main() {
routers.GetRouter().Run(":80")
}
登录后才能使用的系统的登录逻辑,借助cookie,session
- 客户端发起
http://localhost/
请求。
- 请求须经过后端AuthRequired中间件鉴权。该中间件查看session中是否保存了请求携带的cookie对应的用户信息,若有。则登录成功;若没有,则重定向到
http://localhost/login
进行登录。
- GET方法请求
http://localhost/login
时只返回页面,不需鉴权逻辑。
- POST方法请求
http://localhost/login
时,不需鉴权逻辑。进行登录验证,并记录session,为当前context设置Key为"AUTH"的有效用户信息。方便该次请求链路使用。登录成功则重定向到http://localhost/
。