一、项目配置基类
在面向对象设计中,被定义为包含所有实体共性的 class 类型,被称为“基类”
BaseController.go
基础控制器
package admin
import (
"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
})
//设置 Session
session.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 main
import (
"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
})
//设置 Session
session.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 models
import (
"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 int
Username string
Password string
Mobile string
Email string
Status int
RoleId int
AddTime int
IsSuper int
Role Role `gorm:"foreignKey:RoleId;references:Id"` // 配置关联关系
}
//配置数据库操作的表名称
func (Manager) TableName() string {
return "manager"
}
3.创建控制器
在controllers/admin下创建LoginController.go,实现用户的登录登出
package admin
import (
"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") // 验证码id
verifyValue := 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是不是一个string
userinfoStr, ok := userinfo.(string) //类型断言
if ok { // 说明是一个string
var 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 routers
import (
"goshop/controllers/admin"
"goshop/middlewares"
"github.com/gin-gonic/gin"
)
//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {
//路由分组: 配置全局中间件:middlewares.InitMiddleware
adminRouters := 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 main
import (
"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