前面我们已经搭建好后台的脚手架了,接着就是要开始大展身手的表演代码技能了。

在开始实际功能模块的编写之前,我们还需要实现重要的一步,就是后台管理员的登录和退出、管理员账号的修改等。

后台登录界面的设计

后台登录界面不需要搞得太复杂,layuiAdmin默认已经有了,我们直接拿来主义使用就可以了。 

admin-login

后台登录界面前端代码

我们在manage/src/views/目录下创建user文件夹,在里面增加login.html文件:

<script type="text/html" template>
  <link rel="stylesheet" href="{{ layui.setter.base }}style/login.css?v={{ layui.admin.v }}-1" media="all">
</script>


<div class="layadmin-user-login layadmin-user-display-show" id="LAY-user-login" style="display: none;">

  <div class="layadmin-user-login-main">
    <div class="layadmin-user-login-box layadmin-user-login-header">
      <h2>后台管理系统</h2>
    </div>
    <div class="layadmin-user-login-box layadmin-user-login-body layui-form">
      <div class="layui-form-item">
        <label class="layadmin-user-login-icon layui-icon layui-icon-username" for="LAY-user-login-username"></label>
        <input type="text" name="user_name" id="LAY-user-login-username" lay-verify="required" placeholder="用户名" class="layui-input">
      </div>
      <div class="layui-form-item">
        <label class="layadmin-user-login-icon layui-icon layui-icon-password" for="LAY-user-login-password"></label>
        <input type="password" name="password" id="LAY-user-login-password" lay-verify="required" placeholder="密码" class="layui-input">
      </div>
      <div class="layui-form-item">
        <button class="layui-btn layui-btn-fluid" lay-submit lay-filter="admin-login">登 入</button>
      </div>
    </div>
  </div>
  
  <div class="layui-trans layadmin-user-login-footer">
    
    <p>© 2020 <a href="https://www.kandaoni.com/" target="_blank">www.kandaoni.com</a></p>
  </div>
</div>

<script>
  layui.use('user', layui.factory('user'));
</script>

登录界面有两个重要的信息,一个是用户名,一个是密码。基本上和前端登录的时候一致,这里为了方便起见,也没有增加验证码环节,后面有需要的时候,我们再回来补充。

接着在manage/src/controller文件夹中,增加user.js:

/**

 @Name:layuiAdmin 用户登入和注册等
 @Author:贤心
 @Site:http://www.layui.com/admin/
 @License: LPPL
    
 */
 
layui.define('form', function(exports){
  var $ = layui.$
  ,layer = layui.layer
  ,laytpl = layui.laytpl
  ,setter = layui.setter
  ,view = layui.view
  ,admin = layui.admin
  ,form = layui.form
  ,router = layui.router()
  ,search = router.search;

  //提交
  form.on('submit(admin-login)', function(obj){
    //请求登入接口
    admin.req({
      url: '/user/login'
      ,type: 'post'
      ,data: obj.field
      ,done: function(res){

        //登入成功的提示与跳转
        layer.msg('登入成功', {
          offset: '15px'
          ,icon: 1
          ,time: 1000
        }, function(){
          location.hash = search.redirect ? decodeURIComponent(search.redirect) : '/';
        });
      }
    });
  });
  
  //对外暴露的接口
  exports('user', {});
});

js部分代码很简单,因为整个登录界面,我们只需要有账号密码,和登录按钮就可以,因此这里只有监听登录按钮的提交操作。在登录完成后,我们就跳转到首页,供后续继续操作。

后端登录功能golang逻辑

为了区分后台和前台的后端代码,我们在controller文件夹下,单独为后台增加一个文件夹manageController,用来统一存放后台的后端代码。

接着我们在controller/manageController/文件夹下创建一个user.go:

package manageController

import (
	"github.com/kataras/iris/v12"
	"irisweb/config"
	"irisweb/controller"
	"irisweb/middleware"
)

func UserLogin(ctx iris.Context) {
	//复用 AdminLoginForm
	controller.AdminLoginForm(ctx)
}

func UserLogout(ctx iris.Context) {
	session := middleware.Sess.Start(ctx)
	session.Delete("adminId")

	ctx.JSON(iris.Map{
		"code": config.StatusOK,
		"msg":  "已退出登录",
	})
}

这里面的golang代码比较简单,登录的时候,直接复用 controller.AdminLoginForm(ctx) 代码就可以了。退出登录则是在session中,三处adminId标记即可。

在接收数据的时候,我们需要定义一个structAdmin来完成接收。打开request/admin.go,添加上如下代码:

type Admin struct {
	UserName string `json:"user_name" validate:"required"`
	Password string `json:"password" validate:"required"`
}

接着我们增加登录退出的路由:

func manageRoute(app *iris.Application) {
	manage := app.Party("/manage", controller.Inspect)
	{
		manage.HandleDir("/", fmt.Sprintf("%smanage", config.ExecPath))
		manage.Post("/user/login", manageController.UserLogin)

		user := manage.Party("/user", middleware.ManageAuth)
		{
			user.Post("/logout", manageController.UserLogout)
    }
  }
}

使用manage.HandleDir("/", fmt.Sprintf("%smanage", config.ExecPath))来绑定我们建立的后台目录manage

用户退出需要先检查用户是否登录,因此,这里还需要增加后台授权中间件ManageAuth,编辑middleware/auth.go,增加ManageAuth函数:

func ManageAuth(ctx iris.Context) {
	//后台管理强制要求登录
	adminId := ctx.Values().GetIntDefault("adminId", 0)
	if adminId == 0 {
		ctx.JSON(iris.Map{
			"code": config.StatusNoLogin,
			"msg":  "请登录",
		})
		return
	}

	ctx.Next()
}

因为后台的交互是前后端分离的,因此都使用json来交互。这里判断如果没有登录,则返回一个提示登录的json。

管理员账面密码修改

上面已完成登录退出操作,我们接着实现管理员账号、密码的修改操作。

管理员账号的前端html

在manage/src/views/user文件夹下,新增detail.html:

<title>管理员</title>

<div class="layui-card layadmin-header">
  <div class="layui-breadcrumb" lay-filter="breadcrumb">
    <a lay-href="">后台首页</a>
    <a><cite>管理员</cite></a>
  </div>
</div>

<div class="layui-fluid">
  <div class="layui-row layui-col-space15">
    <div class="layui-col-md12">
      <div class="layui-card">
        <div class="layui-card-header">管理员修改</div>
        <div class="layui-card-body" pad15>

          <div class="layui-form" lay-filter="">
            <script type="text/html" template lay-url="/user/detail">
              <div class="layui-form-item">
                <label class="layui-form-label">用户名</label>
                <div class="layui-input-inline">
                  <input type="text" name="user_name" value="{{ d.data && d.data.user_name }}" lay-verify="username" lay-verType="tips" autocomplete="off" id="LAY_password" class="layui-input">
                </div>
              </div>
            </script>
            <div class="layui-form-item">
              <label class="layui-form-label">当前密码</label>
              <div class="layui-input-inline">
                <input type="password" name="old_password" placeholder="如不需修改密码,请留空" lay-verType="tips"
                  class="layui-input">
              </div>
            </div>
            <div class="layui-form-item">
              <label class="layui-form-label">新密码</label>
              <div class="layui-input-inline">
                <input type="password" name="password" placeholder="如不需修改密码,请留空" lay-verify="pass" lay-verType="tips"
                  autocomplete="off" id="LAY_password" class="layui-input">
              </div>
              <div class="layui-form-mid layui-word-aux">6到20个字符</div>
            </div>
            <div class="layui-form-item">
              <label class="layui-form-label">确认新密码</label>
              <div class="layui-input-inline">
                <input type="password" name="re_password" placeholder="如不需修改密码,请留空" lay-verify="repass"
                  lay-verType="tips" autocomplete="off" class="layui-input">
              </div>
            </div>
            <div class="layui-form-item">
              <div class="layui-input-block">
                <button class="layui-btn" lay-submit lay-filter="change-admin">确认修改</button>
              </div>
            </div>
          </div>

        </div>
      </div>
    </div>
  </div>
</div>

<script>
  layui.use('user', layui.factory('user'));
</script>

修改账号密码这里,我们需要提供用户名、当前密码、新的密码、重复新的密码四个表单,当然新密码不是必须提供的,如果不需要修改密码,则留空即可。

管理员账号的前端js

我们继续打开manage/src/controller/user.js,添加验证代码:

/**

 @Name:layuiAdmin 用户登入和注册等
 @Author:贤心
 @Site:http://www.layui.com/admin/
 @License: LPPL
    
 */
 
layui.define('form', function(exports){
  var $ = layui.$
  ,layer = layui.layer
  ,laytpl = layui.laytpl
  ,setter = layui.setter
  ,view = layui.view
  ,admin = layui.admin
  ,form = layui.form
  ,router = layui.router()
  ,search = router.search;

  //自定义验证
  form.verify({
    username: function(value, item){ //value:表单的值、item:表单的DOM对象
      if(!new RegExp("^[a-zA-Z0-9_\u4e00-\u9fa5\\s·]+$").test(value)){
        return '用户名不能有特殊字符';
      }
      if(/(^\_)|(\__)|(\_+$)/.test(value)){
        return '用户名首尾不能出现下划线\'_\'';
      }
      if(/^\d+\d+\d$/.test(value)){
        return '用户名不能全为数字';
      }
    }
    
    //我们既支持上述函数式的方式,也支持下述数组的形式
    //数组的两个值分别代表:[正则匹配、匹配不符时的提示文字]
    ,pass: [
      /^[\S]{6,20}$/
      ,'密码必须6到20位,且不能出现空格'
    ] 
  });

  //提交
  form.on('submit(admin-login)', function(obj){
    //请求登入接口
    admin.req({
      url: '/user/login'
      ,type: 'post'
      ,data: obj.field
      ,done: function(res){

        //登入成功的提示与跳转
        layer.msg('登入成功', {
          offset: '15px'
          ,icon: 1
          ,time: 1000
        }, function(){
          location.hash = search.redirect ? decodeURIComponent(search.redirect) : '/';
        });
      }
    });
  });

  // 修改管理员
  form.on('submit(change-admin)', function(obj){
    if(obj.field.password != obj.field.re_password) {
      return layer.msg("两次输入的新密码不一致。请重新输入");
    }

    admin.req({
      url: '/user/detail'
      ,type: 'post'
      ,data: obj.field
      ,done: function(res){
        if(res.code === 0) {
          layer.msg(res.msg, {
            offset: '15px'
            ,icon: 1
            ,time: 1000
          });
        } else {
          layer.msg(res.msg);
        }
      }
    });
    
  });
  
  //对外暴露的接口
  exports('user', {});
});

修改管理员账号和密码的时候,我们需要做一些基本的验证操作,比如用户账号的规范、密码长度等信息的验证。最后就是表单的提交了。

管理员账号修改的golang后端逻辑代码

为了实现账号修改,我们还需要在后端接收提交过来的数据。

编辑controller/manageController/user.go:


func UserDetail(ctx iris.Context) {
	adminId := uint(ctx.Values().GetIntDefault("adminId", 0))

	admin, err := provider.GetAdminById(adminId)
	if err != nil {
		ctx.JSON(iris.Map{
			"code": config.StatusFailed,
			"msg":  "用户不存在",
		})
		return
	}

	ctx.JSON(iris.Map{
		"code": config.StatusOK,
		"msg":  "",
		"data": admin,
	})
}

func UserDetailForm(ctx iris.Context) {
	var req request.ChangeAdmin
	if err := ctx.ReadJSON(&req); err != nil {
		ctx.JSON(iris.Map{
			"code": config.StatusFailed,
			"msg":  err.Error(),
		})
		return
	}

	adminId := uint(ctx.Values().GetIntDefault("adminId", 0))

	admin, err := provider.GetAdminById(adminId)
	if err != nil {
		ctx.JSON(iris.Map{
			"code": config.StatusFailed,
			"msg":  "用户不存在",
		})
		return
	}

	if !admin.CheckPassword(req.OldPassword) {
		ctx.JSON(iris.Map{
			"code": config.StatusFailed,
			"msg":  "当前密码不正确",
		})
		return
	}

	admin.UserName = req.UserName
	admin.Password = admin.EncryptPassword(req.Password)
	err = admin.Save(config.DB)
	if err != nil {
		ctx.JSON(iris.Map{
			"code": config.StatusFailed,
			"msg":  "更新信息出错",
		})
		return
	}

	ctx.JSON(iris.Map{
		"code": config.StatusOK,
		"msg":  "管理员信息已更新",
	})
}

增加了userDetail()userDetailForm()函数,他们分别的作用是一个是给前端提供数据userDetail(),一个是接收并处理前端提交过来的数据userDetailForm()

在接收数据的时候,我们需要定义一个structChangeAdmin来完成接收。打开request/admin.go,添加上如下代码:

type ChangeAdmin struct {
	UserName    string `json:"user_name" validate:"required"`
	OldPassword string `json:"old_password"`
	Password    string `json:"password"`
	RePassword  string `json:"re_password"`
}

如果用户提交的表单中,包含密码的修改,则,我们需要先验证旧密码是否正确,如果正确了,再进行新密码的编码并更新数据库信息。

最后我们需要将他们添加到路由中,便可正常访问。编辑route/manage.go:

user := manage.Party("/user", middleware.ManageAuth)
{
  user.Get("/detail", manageController.UserDetail)
  user.Post("/detail", manageController.UserDetailForm)
  user.Post("/logout", manageController.UserLogout)
}

至此我们实现的后台的第一步。完成之后,我们启动golang项目来验证下结果,如果不出意外,就可以登录、退出、修改管理员账号密码信息了。 

admin-user

教程用例源码

完整的项目示例代码托管在GitHub上,访问github.com/fesiong/goblog 可以查看完整的教程项目源代码,建议在查看教程的同时,认真对照源码,可以有效提高码代码速度和加深对博客项目的认识。建议直接fork一份来在上面做修改。欢迎点Star。