[golang gin框架] 13.Gin 商城项目-配置公共基类实现公共的成功,失败提示页面 用户登录、退出登录、以及权限判断
一、项目配置基类
在面向对象设计中,被定义为包含所有实体共性的 class 类型,被称为“基类”
-
BaseController.go
基础控制器
package adminimport ("github.com/gin-gonic/gin""net/http"
)//基础控制器
type BaseController struct {}//公共成功函数
func (con BaseController) Success(c *gin.Context, message string, redirectUrl string) {c.HTML(http.StatusOK, "admin/public/success.html", gin.H{"message": message,"redirectUrl": redirectUrl,})
}//公共失败函数
func (con BaseController) Error(c *gin.Context, message string, redirectUrl string) {c.HTML(http.StatusOK, "admin/public/error.html", gin.H{"message": message,"redirectUrl": redirectUrl,})
}
2.success.html
公共成功跳转页面
{{ define "admin/public/success.html" }}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><meta http-equiv="refresh" content="1;url={{ .redirectUrl }}"><title>提示信息</title><link rel="stylesheet" href="/static/admin/bootstrap/css/bootstrap.css">
</head>
<body><div class="container-fluid"><div class="container" style="width: 480px;margin-top:100px;"><div class="alert alert-success"><h2>操作成功: {{ .message }}</h2><br /><p>如果您不做出选择,将在 1秒后跳转到上一个链接地址</p><br /></div></div></div>
</body>
</html>
{{ end }}
3.error.html
公共失败跳转页面
{{ define "admin/public/error.html" }}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><meta http-equiv="refresh" content="1;url={{ .redirectUrl }}"><title>提示信息</title><link rel="stylesheet" href="/static/admin/bootstrap/css/bootstrap.css">
</head>
<body><div class="container-fluid"><div class="container" style="width: 480px;margin-top:100px;"><div class="alert alert-dange"><h2>操作失败: {{ .message }}</h2><br /><p>如果您不做出选择,将在 1秒后跳转到上一个链接地址</p><br /></div></div></div>
</body>
</html>
{{ end }}
二、配置 session
1.安装 session 包
直接在项目main.go目录下运行命令:go get github.com/gin-contrib/sessions
2.基本的 session 用法
基于 cookie 的存储引擎
package main
import ( "github.com/gin-contrib/sessions""github.com/gin-contrib/sessions/cookie""github.com/gin-gonic/gin"
)func main() { 初始化路由,会设置默认中间件:engine.Use(Logger(), Recovery()),可以使用gin.New()来设置路由r := gin.Default()// 创建基于 cookie 的存储引擎,secret11111 参数是用于加密的密钥store := cookie.NewStore([]byte("secret11111"))// 设置 session 中间件,参数 mysession,指的是 session 的名字,也是 cookie 的名字// store 是前面创建的存储引擎,我们可以替换成其他存储引擎r.Use(sessions.Sessions("mysession", store))r.GET("/", func(c *gin.Context) {//初始化 session 对象session := sessions.Default(c)//设置过期时间session.Options(sessions.Options{MaxAge: 3600 * 6, // 6hrs})//设置 Sessionsession.Set("username", "张三") session.Save() c.JSON(200, gin.H{"msg": session.Get("username")})})r.GET("/user", func(c *gin.Context) {// 初始化 session 对象session := sessions.Default(c)// 通过 session.Get 读取 session 值username := session.Get("username") c.JSON(200, gin.H{"username": username})})r.Run(":8000")
}
基于redis的存储引擎
package mainimport ("github.com/gin-contrib/sessions""github.com/gin-contrib/sessions/redis""github.com/gin-gonic/gin"
)func main() {//初始化路由,会设置默认中间件:engine.Use(Logger(), Recovery()),可以使用gin.New()来设置路由r := gin.Default()//初始化基于redis的存储引擎: 需要启动redis服务,不然会报错//参数说明://自第1个参数-redis最大的空闲连接数//第2个参数-数通信协议tcp或者udp//第3个参数-redis地址,格式,host:port 第4个参数-redis密码//第5个参数-session加密密钥store,_:=redis.NewStore(10,"tcp","localhost:6379","",[]byte("secret"))r.Use(sessions.Sessions("mysession",store))r.GET("/", func(c *gin.Context) {//初始化 session 对象session := sessions.Default(c)//设置过期时间session.Options(sessions.Options{MaxAge: 3600 * 6, // 6hrs})//设置 Sessionsession.Set("username", "张三") session.Save() c.JSON(200, gin.H{"msg": session.Get("username")})})r.GET("/user", func(c *gin.Context) {// 初始化 session 对象session := sessions.Default(c)// 通过 session.Get 读取 session 值username := session.Get("username") c.JSON(200, gin.H{"username": username})})r.Run(":8000")
}
三、配置 Golang Md5 加密
在models/tools.go下写一个md5加密方法
package modelsimport ("crypto/md5""fmt""io"
)//md5加密
func Md5(str string) string {//data := []byte(str)//return fmt.Sprintf("%x\\n", md5.Sum(data))h := md5.New()io.WriteString(h, str)return fmt.Sprintf("%x", h.Sum(nil))
}
四、创建数据库、实现用户登录、以及用户权限判断
1.创建数据库
-- ----------------------------
-- Table structure for manager
-- ----------------------------
DROP TABLE IF EXISTS `manager`;
CREATE TABLE `manager` (`id` int(0) NOT NULL AUTO_INCREMENT,`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`password` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`mobile` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`status` tinyint(1) NULL DEFAULT NULL,`role_id` int(0) NULL DEFAULT NULL,`add_time` int(0) NULL DEFAULT NULL,`is_super` tinyint(1) NULL DEFAULT 0,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of manager
-- ----------------------------
INSERT INTO `manager` VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', '15201686411', '518864@qq.com', 1, 1, 0, 1);
INSERT INTO `manager` VALUES (2, 'zhagnsan', 'e10adc3949ba59abbe56e057f20f883e', '15201686412', '34233869@qq.com', 1, 2, 1581661532, 0);
2.创建模型
在目录models下创建manager.go
package models//管理员表type Manager struct { // 结构体首字母大写, 和数据库表名对应, 默认访问数据表users, 可以设置访问数据表的方法Id intUsername stringPassword stringMobile stringEmail stringStatus intRoleId intAddTime intIsSuper intRole Role `gorm:"foreignKey:RoleId;references:Id"` // 配置关联关系
}//配置数据库操作的表名称
func (Manager) TableName() string {return "manager"
}
3.创建控制器
在controllers/admin下创建LoginController.go,实现用户的登录登出
package adminimport ("encoding/json""fmt""github.com/gin-contrib/sessions""github.com/gin-gonic/gin""goshop/models""net/http"
)type LoginController struct {BaseController
}//进入登录页面
func (con LoginController) Index(c *gin.Context) {c.HTML(http.StatusOK, "admin/login/login.html", gin.H{})
}//执行登录操作
func (con LoginController) DoIndex(c *gin.Context) {//获取表单中的数据captchaId := c.PostForm("captchaId") // 验证码idverifyValue := c.PostForm("verifyValue") //验证码的值//获取用户名以及密码username := c.PostForm("username")password := c.PostForm("password")// 1.判断验证码是否验证成功if flag := models.VerifyCaptcha(captchaId, verifyValue); flag {//2.查询数据库,判断用户以及密码是否正确userinfo := []models.Manager{}password = models.Md5(password)models.DB.Where("username = ? and password = ? ", username, password).Find(&userinfo)if len(userinfo) > 0 {//3.执行登录,保存用户信息,执行跳转操作session := sessions.Default(c)//注意: session.Set没法保存结构体对应的切片,所以需要把结构体转换成json字符串userinfoSlice, _ := json.Marshal(userinfo)session.Set("userinfo", string(userinfoSlice))session.Save()con.Success(c, "登录成功", "/admin")} else {con.Error(c, "用户名或密码错误", "/admin/login")}} else {con.Error(c, "验证码验证失败", "/admin/login")}
}//获取验证码,验证验证码
func (con LoginController) Captcha(c *gin.Context) {id, b64s, err := models.MakeCaptcha()if err != nil {fmt.Println(err)}c.JSON(http.StatusOK, gin.H{"captchaId": id,"captchaImage": b64s,})
}func (con LoginController) LoginOut(c *gin.Context) {//1.销毁session中用户信息session := sessions.Default(c)session.Delete("userinfo")session.Save()con.Success(c, "退出登录成功", "/admin/login")
}
4.创建adminAuth.go中间件
在middlewares文件夹下创建adminAuth.go中间件,进行权限判断
package middlewares//中间件: 作用: 在执行路由之前或者之后进行相关逻辑判断import ("encoding/json""ginshop04/models""strings""github.com/gin-contrib/sessions""github.com/gin-gonic/gin"
)
func InitAdminAuthMiddleware(c *gin.Context) {/权限判断: 没有登录的用户不能进入后台管理中心//判断用户是否登录//1、获取Url访问的地址//当地址后面带参数时:,如: admin/captcha?t=0.8706946438889653,需要处理//strings.Split(c.Request.URL.String(), "?"): 把c.Request.URL.String()请求地址按照?分割成切片pathname := strings.Split(c.Request.URL.String(), "?")[0] //2、获取Session里面保存的用户信息session := sessions.Default(c)userinfo := session.Get("userinfo") //3、判断Session中的用户信息是否存在,如果不存在跳转到登录页面(注意需要判断) 如果存在继续向下执行//session.Get获取返回的结果是一个空接口类型,所以需要进行类型断言: 判断userinfo是不是一个stringuserinfoStr, ok := userinfo.(string) //类型断言if ok { // 说明是一个stringvar u []models.Manager//把获取到的用户信息转换结构体json.Unmarshal([]byte(userinfoStr), &u)if !(len(u) > 0 && u[0].Username != "") {if pathname != "/admin/login" && pathname != "/admin/doLogin" && pathname !="/admin/captcha" { //跳转到登录页面c.Redirect(302, "/admin/login")}}} else {if pathname != "/admin/login" && pathname != "/admin/doLogin" && pathname != "/admin/captcha" { c.Redirect(302, "/admin/login")}}
}
5.创建登录html表单页面
在templates/admin/login目录下,创建login.html页面
{{ define "admin/login/login.html" }}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>用户登录</title> <link rel="stylesheet" href="/static/admin/css/login.css"><script type="text/javascript" src="/static/admin/bootstrap/js/jquery-1.10.1.js"></script><script type="text/javascript" src="/static/admin/js/login.js"></script>
</head>
<body>
<div class="container"><div id="login"><form action="/admin/doLogin" method="post" id="myform"><input type="hidden" name="captchaId" id="captchaId"><div class="l_title">小米商城后台管理系统-IT营</div><dl><dd>管理员姓名:<input class="text" type="text" name="username" id="username"></dd><dd>管理员密码:<input class="text" type="password" name="password" id="password"></dd><dd>验 证 码:<input id="verifyValue" type="text" name="verifyValue"><img id="captchaImg" ></dd> <dd><input type="submit" class="submit" name="dosubmit" value=""></dd> </dl></form></div>
</div></body>
</html>
{{ end }}
6.增加登录相关路由
在routers/adminRouters.go文件下增加登录相关路由
package routersimport ("goshop/controllers/admin""goshop/middlewares""github.com/gin-gonic/gin"
)//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {//路由分组: 配置全局中间件:middlewares.InitMiddlewareadminRouters := r.Group("/admin", middlewares.InitAdminAuthMiddleware){//登录页面adminRouters.GET("/login", admin.LoginController{}.Index) // 实例化控制器,并访问其中方法adminRouters.POST("/doLogin", admin.LoginController{}.DoIndex)adminRouters.GET("/loginOut", admin.LoginController{}.LoginOut)//验证码adminRouters.GET("/captcha", admin.LoginController{}.Captcha)}
}
7.完善main.go文件
package mainimport ("fmt""github.com/gin-contrib/sessions""github.com/gin-contrib/sessions/redis""github.com/gin-gonic/gin""goshop/models""goshop/routers""html/template""os"
)func main() {//初始化路由,会设置默认中间件:engine.Use(Logger(), Recovery()),可以使用gin.New()来设置路由r := gin.Default()//初始化基于redis的存储引擎: 需要启动redis服务,不然会报错//参数说明://自第1个参数-redis最大的空闲连接数//第2个参数-数通信协议tcp或者udp//第3个参数-redis地址,格式,host:port 第4个参数-redis密码//第5个参数-session加密密钥store,_:=redis.NewStore(10,"tcp","localhost:6379","",[]byte("secret"))r.Use(sessions.Sessions("mysession",store))//加载templates中所有模板文件, 使用不同目录下名称相同的模板,注意:一定要放在配置路由之前才得行//如果模板在多级目录里面的话需要这样配置 r.LoadHTMLGlob("templates///*") / 表示目录//LoadHTMLGlob只能加载同一层级的文件//比如说使用router.LoadHTMLFile("/templates//*"),就只能加载/templates/admin/或者/templates/order/下面的文件//解决办法就是通过filepath.Walk来搜索/templates下的以.html结尾的文件,把这些html文件都加载一个数组中,然后用LoadHTMLFiles加载r.LoadHTMLGlob("templates///*")//var files []string//filepath.Walk("./templates", func(path string, info os.FileInfo, err error) error {// if strings.HasSuffix(path, ".html") {// files = append(files, path)// }// return nil//})//r.LoadHTMLFiles(files...)//配置静态web目录 第一个参数表示路由,第二个参数表示映射的目录r.Static("/static", "./static")//分组路由文件routers.AdminRoutersInit(r)routers.ApiRoutersInit(r)routers.DefaultRoutersInit(r)r.Run() // 启动一个web服务
}
[上一节][golang gin框架] 12.Gin 商城项目-base64Captcha生成图形验证码以及分布式架构中配置Captcha
[下一节][golang gin框架] 14.Gin 商城项目-RBAC管理