[golang gin框架] 13.Gin 商城项目-配置公共基类实现公共的成功,失败提示页面 用户登录、退出登录、以及权限判断

该文介绍了如何在Gin框架中创建一个基础控制器,包括公共的成功和失败处理函数。同时,详细讲解了如何配置和使用session,包括基于cookie和redis两种存储引擎。此外,还涵盖了Golang的MD5加密方法,以及创建数据库、用户登录验证和权限判断的实现。最后,文中给出了登录页面的HTML表单和相关路由配置。
摘要由CSDN通过智能技术生成

项目配置基类

在面向对象设计中,被定义为包含所有实体共性的 class 类型,被称为“基类”
  1. 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

[下一节][golang gin框架] 14.Gin 商城项目-RBAC管理

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值