增加用户登录验证

pull/21/merge
ouqiang 2017-04-23 17:00:47 +08:00
parent 6c99197078
commit ba5b0cd6ad
8 changed files with 226 additions and 71 deletions

View File

@ -32,7 +32,7 @@ var CmdWeb = cli.Command{
}, },
cli.StringFlag{ cli.StringFlag{
Name: "env,e", Name: "env,e",
Value: "dev", Value: "prod",
Usage: "runtime environment, dev|test|prod", Usage: "runtime environment, dev|test|prod",
}, },
cli.StringFlag{ cli.StringFlag{
@ -75,13 +75,18 @@ func parsePort(ctx *cli.Context) int {
} }
func setEnvironment(ctx *cli.Context) { func setEnvironment(ctx *cli.Context) {
var env string = "" var env string = "prod"
if ctx.IsSet("env") { if ctx.IsSet("env") {
env = ctx.String("env") env = ctx.String("env")
} }
if env == "prod" { switch env {
case "prod":
macaron.Env = macaron.PROD macaron.Env = macaron.PROD
case "test":
macaron.Env = macaron.TEST
case "dev":
macaron.Env = macaron.DEV
} }
} }

View File

@ -3,7 +3,6 @@ package models
import ( import (
"time" "time"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
"github.com/ouqiang/gocron/modules/logger"
) )
type TaskType int8 type TaskType int8

View File

@ -4,6 +4,5 @@ import "gopkg.in/macaron.v1"
// 首页 // 首页
func Home(ctx *macaron.Context) { func Home(ctx *macaron.Context) {
ctx.Data["Title"] = "首页" ctx.Redirect("/task")
ctx.HTML(200, "home/index")
} }

View File

@ -14,6 +14,8 @@ import (
"strings" "strings"
"github.com/ouqiang/gocron/modules/app" "github.com/ouqiang/gocron/modules/app"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/logger"
"github.com/ouqiang/gocron/routers/user"
"github.com/go-macaron/gzip"
) )
// 静态文件目录 // 静态文件目录
@ -23,28 +25,6 @@ const StaticDir = "public"
func Register(m *macaron.Macaron) { func Register(m *macaron.Macaron) {
// 所有GET方法自动注册HEAD方法 // 所有GET方法自动注册HEAD方法
m.SetAutoHead(true) m.SetAutoHead(true)
// 404错误
m.NotFound(func(ctx *macaron.Context) {
if isGetRequest(ctx) && !isAjaxRequest(ctx) {
ctx.Data["Title"] = "404 - NOT FOUND"
ctx.HTML(404, "error/404")
} else {
json := utils.JsonResponse{}
ctx.Resp.Write([]byte(json.Failure(utils.NotFound, "您访问的地址不存在")))
}
})
// 50x错误
m.InternalServerError(func(ctx *macaron.Context) {
logger.Debug("500错误")
if isGetRequest(ctx) && !isAjaxRequest(ctx) {
ctx.Data["Title"] = "500 - INTERNAL SERVER ERROR"
ctx.HTML(500, "error/500")
} else {
json := utils.JsonResponse{}
ctx.Resp.Write([]byte(json.Failure(utils.ServerError, "网站暂时无法访问,请稍后再试")))
}
})
// 首页 // 首页
m.Get("/", Home) m.Get("/", Home)
// 系统安装 // 系统安装
@ -55,7 +35,9 @@ func Register(m *macaron.Macaron) {
// 用户 // 用户
m.Group("/user", func() { m.Group("/user", func() {
m.Get("/login", user.Login)
m.Post("/login", user.ValidateLogin)
m.Get("/logout", user.Logout)
}) })
// 任务 // 任务
@ -81,12 +63,35 @@ func Register(m *macaron.Macaron) {
m.Get("", host.Index) m.Get("", host.Index)
m.Post("/remove/:id", host.Remove) m.Post("/remove/:id", host.Remove)
}) })
// 404错误
m.NotFound(func(ctx *macaron.Context) {
if isGetRequest(ctx) && !isAjaxRequest(ctx) {
ctx.Data["Title"] = "404 - NOT FOUND"
ctx.HTML(404, "error/404")
} else {
json := utils.JsonResponse{}
ctx.Resp.Write([]byte(json.Failure(utils.NotFound, "您访问的地址不存在")))
}
})
// 50x错误
m.InternalServerError(func(ctx *macaron.Context) {
logger.Debug("500错误")
if isGetRequest(ctx) && !isAjaxRequest(ctx) {
ctx.Data["Title"] = "500 - INTERNAL SERVER ERROR"
ctx.HTML(500, "error/500")
} else {
json := utils.JsonResponse{}
ctx.Resp.Write([]byte(json.Failure(utils.ServerError, "网站暂时无法访问,请稍后再试")))
}
})
} }
// 中间件注册 // 中间件注册
func RegisterMiddleware(m *macaron.Macaron) { func RegisterMiddleware(m *macaron.Macaron) {
m.Use(macaron.Logger()) m.Use(macaron.Logger())
m.Use(macaron.Recovery()) m.Use(macaron.Recovery())
m.Use(gzip.Gziper())
m.Use(macaron.Static(StaticDir)) m.Use(macaron.Static(StaticDir))
m.Use(macaron.Renderer(macaron.RenderOptions{ m.Use(macaron.Renderer(macaron.RenderOptions{
Directory: "templates", Directory: "templates",
@ -103,8 +108,14 @@ func RegisterMiddleware(m *macaron.Macaron) {
m.Use(session.Sessioner()) m.Use(session.Sessioner())
m.Use(csrf.Csrfer()) m.Use(csrf.Csrfer())
m.Use(toolbox.Toolboxer(m)) m.Use(toolbox.Toolboxer(m))
checkAppInstall(m)
userAuth(m)
setShareData(m)
}
// 系统未安装,重定向到安装页面 // 系统未安装,重定向到安装页面
func checkAppInstall(m *macaron.Macaron) {
m.Use(func(ctx *macaron.Context) { m.Use(func(ctx *macaron.Context) {
installUrl := "/install" installUrl := "/install"
if strings.HasPrefix(ctx.Req.URL.Path, installUrl) { if strings.HasPrefix(ctx.Req.URL.Path, installUrl) {
@ -114,8 +125,32 @@ func RegisterMiddleware(m *macaron.Macaron) {
ctx.Redirect(installUrl) ctx.Redirect(installUrl)
} }
}) })
// 设置模板共享变量 }
m.Use(func(ctx *macaron.Context) {
// 用户认证
func userAuth(m *macaron.Macaron) {
m.Use(func(ctx *macaron.Context, sess session.Store) {
if user.IsLogin(sess) {
return
}
uri := ctx.Req.URL.Path
found := false
excludePaths := []string{"/install", "/user/login"}
for _, path := range excludePaths {
if strings.HasPrefix(uri, path) {
found = true
break
}
}
if !found {
ctx.Redirect("/user/login")
}
})
}
// 设置共享数据
func setShareData(m *macaron.Macaron) {
m.Use(func(ctx *macaron.Context, sess session.Store) {
ctx.Data["URI"] = ctx.Req.URL.Path ctx.Data["URI"] = ctx.Req.URL.Path
urlPath := strings.TrimPrefix(ctx.Req.URL.Path, "/") urlPath := strings.TrimPrefix(ctx.Req.URL.Path, "/")
paths := strings.Split(urlPath, "/") paths := strings.Split(urlPath, "/")
@ -127,6 +162,7 @@ func RegisterMiddleware(m *macaron.Macaron) {
if len(paths) > 1 { if len(paths) > 1 {
ctx.Data["Action"] = paths[1] ctx.Data["Action"] = paths[1]
} }
ctx.Data["LoginUsername"] = user.Username(sess)
}) })
} }

66
routers/user/user.go Normal file
View File

@ -0,0 +1,66 @@
package user
import (
"gopkg.in/macaron.v1"
"github.com/ouqiang/gocron/modules/utils"
"github.com/ouqiang/gocron/models"
"github.com/go-macaron/session"
"github.com/ouqiang/gocron/modules/logger"
"time"
)
// @author qiang.ou<qingqianludao@gmail.com>
// @date 2017/4/23-14:16
func Login(ctx *macaron.Context) {
ctx.Data["Title"] = "用户登录"
ctx.HTML(200, "user/login")
}
func ValidateLogin(ctx *macaron.Context, sess session.Store) string {
username := ctx.QueryTrim("username")
password := ctx.QueryTrim("password")
json := utils.JsonResponse{}
if username == "" || password == "" {
return json.CommonFailure("用户名、密码不能为空")
}
userModel := new (models.User)
if !userModel.Match(username, password) {
return json.CommonFailure("用户名或密码错误")
}
sess.Set("username", username)
return json.Success("登录成功", nil)
}
func Logout(ctx *macaron.Context, sess session.Store) {
if IsLogin(sess) {
err := sess.Delete("username")
if err != nil {
logger.Error("用户退出登录失败", err)
}
}
ctx.SetSecureCookie("MacaronSession", "", 0, "/", "", nil, nil, time.Now().AddDate(-1, 0, 0))
Login(ctx)
}
func Username(sess session.Store) string {
username,ok := sess.Get("username").(string)
if ok {
return username
}
return ""
}
func IsLogin(sess session.Store) bool {
username,ok := sess.Get("username").(string)
if ok && username != "" {
return true
}
return false
}

View File

@ -36,16 +36,14 @@
</div> </div>
<div class="user"> <div class="user">
<div class="ui inline labeled icon top right pointing dropdown"> <div class="ui inline labeled icon top right pointing dropdown">
{{{if not .Logined}}} {{{if not .LoginUsername}}}
<img class="ui avatar image" src="/resource/images/gopher-avatar.png">
你好 你好
<i class="dropdown icon"></i> <i class="dropdown icon"></i>
<div class="menu"> <div class="menu">
<a class="item" href="/user/login"><i class="sign in icon"></i>登录</a> <a class="item" href="/user/login"><i class="sign in icon"></i>登录</a>
</div> </div>
{{{else}}} {{{else}}}
<img class="ui avatar image" src="/resource/images/gopher-avatar.png"> 你好,{{{.LoginUsername}}}
你好,{{{.Username}}}
<i class="dropdown icon"></i> <i class="dropdown icon"></i>
<div class="menu"> <div class="menu">
<a class="item" href="/user/logout"><i class="sign out icon"></i>退出</a> <a class="item" href="/user/logout"><i class="sign out icon"></i>退出</a>
@ -59,7 +57,6 @@
<div class="ui teal inverted menu"> <div class="ui teal inverted menu">
<div class="bigcontainer"> <div class="bigcontainer">
<div class="right menu"> <div class="right menu">
<a class="item {{{if eq .Controller ""}}}active{{{end}}}" href="/"><i class="home icon"></i>首页</a>
<a class="item {{{if eq .Controller "task"}}}active{{{end}}}" href="/task"><i class="tasks icon"></i>任务</a> <a class="item {{{if eq .Controller "task"}}}active{{{end}}}" href="/task"><i class="tasks icon"></i>任务</a>
<a class="item {{{if eq .Controller "host"}}}active{{{end}}}" href="/host"><i class="linux icon"></i>主机</a> <a class="item {{{if eq .Controller "host"}}}active{{{end}}}" href="/host"><i class="linux icon"></i>主机</a>
<a class="item {{{if eq .Controller "user"}}}active{{{end}}}" href="/user"><i class="user icon"></i>账户</a> <a class="item {{{if eq .Controller "user"}}}active{{{end}}}" href="/user"><i class="user icon"></i>账户</a>

View File

@ -50,8 +50,9 @@
</div> </div>
</div> </div>
</form> </form>
<div class="task-list">
{{{range $i, $v := .Tasks}}} {{{range $i, $v := .Tasks}}}
<div class="ui device two column middle aligned vertical grid list segment"> <div class="ui device two column middle aligned vertical grid segment">
<div class="column verborder"> <div class="column verborder">
<div class="ui info segment"> <div class="ui info segment">
<h5 class="ui header">{{{.Task.Name}}} {{{if eq .Status 1}}}<i class="large checkmark blue icon"></i> {{{else}}} <i class="large red minus icon"></i> {{{end}}} <h5 class="ui header">{{{.Task.Name}}} {{{if eq .Status 1}}}<i class="large checkmark blue icon"></i> {{{else}}} <i class="large red minus icon"></i> {{{end}}}
@ -69,7 +70,7 @@
</div> </div>
</div> </div>
<div class="center aligned column"> <div class="center aligned column">
<div class="ui buttons"> <div class="ui buttons operation">
<a class="ui purple button" href="/task/edit/{{{.Id}}}">编辑</a> <a class="ui purple button" href="/task/edit/{{{.Id}}}">编辑</a>
{{{if eq .Status 1}}} {{{if eq .Status 1}}}
<button class="ui primary button" @click="changeStatus({{{.Id}}},{{{.Status}}})">暂停</button> <button class="ui primary button" @click="changeStatus({{{.Id}}},{{{.Status}}})">暂停</button>
@ -83,6 +84,7 @@
</div> </div>
</div> </div>
{{{end}}} {{{end}}}
</div>
{{{ template "common/pagination" .}}} {{{ template "common/pagination" .}}}
</div> </div>
</div> </div>
@ -93,7 +95,7 @@
var vue = new Vue( var vue = new Vue(
{ {
el: '.ui.list', el: '.task-list',
methods: { methods: {
changeStatus: function (id ,status) { changeStatus: function (id ,status) {
var url = ''; var url = '';

View File

@ -1,4 +1,55 @@
{{{ template "common/header" . }}} {{{ template "common/header" . }}}
<div class="ui small modal">
<div class="header">用户登录</div>
<div class="content">
<form class="ui form">
<div class="two fields">
<div class="field">
<input class="small input" type="text" name="username" placeholder="用户名或邮箱">
</div>
</div>
<div class="two fields">
<div class="field">
<input type="password" name="password" placeholder="密码">
</div>
</div>
<button class="ui button primary button" >登录</button>
</form>
</div>
</div>
<script type="text/javascript">
$('.ui.modal').modal('setting', 'closable', false).modal('show');
$('.ui.form').form(
{
onSuccess: function(event, fields) {
util.post('/user/login', fields, function(code, message) {
location.href = "/"
});
return false;
},
fields: {
username: {
identifier : 'username',
rules: [
{
type : 'empty',
prompt : '请输入用户名'
}
]
},
Password: {
identifier : 'password',
rules: [
{
type : 'empty',
prompt : '请输入密码'
}
]
}
},
inline : true
});
</script>
{{{ template "common/footer" . }}} {{{ template "common/footer" . }}}