扫码订阅《 》或入驻星球,即可阅读文章!

GOLANG ROADMAP

阅读模式

  • 沉浸
  • 自动
  • 日常
首页
Go友会
  • 城市
  • 校园
Go学院
  • Go小课
  • Go小考
  • Go实战
  • 精品课
Go求职
  • 求职辅导🔥
  • Offer收割社群
  • 企业题库
  • 面试宝典
Go宝典
  • 在线宝典
  • B站精选
  • 推荐图书
  • 每日博文
Go仓库
实验区
  • Go周边
  • Go下载
  • Go月刊
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
author-avatar

GOLANG ROADMAP


首页
Go友会
  • 城市
  • 校园
Go学院
  • Go小课
  • Go小考
  • Go实战
  • 精品课
Go求职
  • 求职辅导🔥
  • Offer收割社群
  • 企业题库
  • 面试宝典
Go宝典
  • 在线宝典
  • B站精选
  • 推荐图书
  • 每日博文
Go仓库
实验区
  • Go周边
  • Go下载
  • Go月刊
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
  • Go语言Web编程

    • 课程介绍
  • Web基础

  • 表单

  • 访问数据库

  • session和数据存储

  • 文本处理

  • Web服务

  • 安全与加密

  • 国际化和本地化

  • 错误处理,调试和测试

  • 部署与维护

  • 如何设计一个Web框架

  • 扩展Web框架

    • 第1节:扩展 Web 框架
    • 第2节: 静态文件支持
    • 第3节:Session 支持
    • 第4节:表单及验证支持
    • 第5节:用户认证
    • 第6节:多语言支持
    • 第7节:pprof 支持
    • 第8节:小结

扫码订阅《 》或入驻星球,即可阅读文章!

第5节:用户认证


ASTA谢

在开发 Web 应用过程中,用户认证是开发者经常遇到的问题,用户登录、注册、登出等操作,而一般认证也分为三个方面的认证

  • HTTP Basic 和 HTTP Digest 认证
  • 第三方集成认证:QQ、微博、豆瓣、OPENID、google、github、facebook和twitter等
  • 自定义的用户登录、注册、登出,一般都是基于 session、cookie 认证

beego 目前没有针对这三种方式进行任何形式的集成,但是可以充分的利用第三方开源库来实现上面的三种方式的用户认证,不过后续beego 会对前面两种认证逐步集成。

# HTTP Basic 和 HTTP Digest 认证

这两个认证是一些应用采用的比较简单的认证,目前已经有开源的第三方库支持这两个认证:

github.com/abbot/go-http-auth 
1

下面代码演示了如何把这个库引入 beego 中从而实现认证:

package controllers

import (
	"github.com/abbot/go-http-auth"
	"github.com/astaxie/beego"
)

func Secret(user, realm string) string {
	if user == "john" {
		// password is "hello"
		return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1"
	}
	return ""
}

type MainController struct {
	beego.Controller
}

func (this *MainController) Prepare() {
	a := auth.NewBasicAuthenticator("example.com", Secret)
	if username := a.CheckAuth(this.Ctx.Request); username == "" {
		a.RequireAuth(this.Ctx.ResponseWriter, this.Ctx.Request)
	}
}

func (this *MainController) Get() {
	this.Data["Username"] = "astaxie"
	this.Data["Email"] = "astaxie@gmail.com"
	this.TplNames = "index.tpl"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

上面代码利用了 beego 的 prepare 函数,在执行正常逻辑之前调用了认证函数,这样就非常简单的实现了 http auth,digest 的认证也是同样的原理。

# oauth 和 oauth2 的认证

oauth 和 oauth2 是目前比较流行的两种认证方式,还好第三方有一个库实现了这个认证,但是是国外实现的,并没有 QQ、微博之类的国内应用认证集成:

github.com/bradrydzewski/go.auth
1

下面代码演示了如何把该库引入 beego 中从而实现 oauth 的认证,这里以 github 为例演示:

  1. 添加两条路由
beego.RegisterController("/auth/login", &controllers.GithubController{})
beego.RegisterController("/mainpage", &controllers.PageController{})
1
2
  1. 然后我们处理 GithubController 登录的页面:
package controllers

import (
	"github.com/astaxie/beego"
	"github.com/bradrydzewski/go.auth"
)

const (
	githubClientKey = "a0864ea791ce7e7bd0df"
	githubSecretKey = "a0ec09a647a688a64a28f6190b5a0d2705df56ca"
)

type GithubController struct {
	beego.Controller
}

func (this *GithubController) Get() {
	// set the auth parameters
	auth.Config.CookieSecret = []byte("7H9xiimk2QdTdYI7rDddfJeV")
	auth.Config.LoginSuccessRedirect = "/mainpage"
	auth.Config.CookieSecure = false

	githubHandler := auth.Github(githubClientKey, githubSecretKey)

	githubHandler.ServeHTTP(this.Ctx.ResponseWriter, this.Ctx.Request)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  1. 处理登录成功之后的页面
package controllers

import (
	"github.com/astaxie/beego"
	"github.com/bradrydzewski/go.auth"
	"net/http"
	"net/url"
)

type PageController struct {
	beego.Controller
}

func (this *PageController) Get() {
	// set the auth parameters
	auth.Config.CookieSecret = []byte("7H9xiimk2QdTdYI7rDddfJeV")
	auth.Config.LoginSuccessRedirect = "/mainpage"
	auth.Config.CookieSecure = false

	user, err := auth.GetUserCookie(this.Ctx.Request)

	// if no active user session then authorize user
	if err != nil || user.Id() == "" {
		http.Redirect(this.Ctx.ResponseWriter, this.Ctx.Request, auth.Config.LoginRedirect, http.StatusSeeOther)
		return
	}

	// else, add the user to the URL and continue
	this.Ctx.Request.URL.User = url.User(user.Id())
	this.Data["pic"] = user.Picture()
	this.Data["id"] = user.Id()
	this.Data["name"] = user.Name()
	this.TplNames = "home.tpl"
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

整个的流程如下,首先打开浏览器输入地址:

图 14.4 显示带有登录按钮的首页

然后点击链接出现如下界面:

图 14.5 点击登录按钮后显示github的授权页

然后点击Authorize app就出现如下界面:

图 14.6 授权登录之后显示的获取到的github信息页

# 自定义认证

自定义的认证一般都是和 session 结合验证的,如下代码来源于一个基于 beego 的开源博客:

// 登录处理
func (this *LoginController) Post() {
	this.TplNames = "login.tpl"
	this.Ctx.Request.ParseForm()
	username := this.Ctx.Request.Form.Get("username")
	password := this.Ctx.Request.Form.Get("password")
	md5Password := md5.New()
	io.WriteString(md5Password, password)
	buffer := bytes.NewBuffer(nil)
	fmt.Fprintf(buffer, "%x", md5Password.Sum(nil))
	newPass := buffer.String()

	now := time.Now().Format("2006-01-02 15:04:05")

	userInfo := models.GetUserInfo(username)
	if userInfo.Password == newPass {
		var users models.User
		users.Last_logintime = now
		models.UpdateUserInfo(users)

		// 登录成功设置 session
		sess := globalSessions.SessionStart(this.Ctx.ResponseWriter, this.Ctx.Request)
		sess.Set("uid", userInfo.Id)
		sess.Set("uname", userInfo.Username)

		this.Ctx.Redirect(302, "/")
	}	
}

// 注册处理
func (this *RegController) Post() {
	this.TplNames = "reg.tpl"
	this.Ctx.Request.ParseForm()
	username := this.Ctx.Request.Form.Get("username")
	password := this.Ctx.Request.Form.Get("password")
	usererr := checkUsername(username)
	fmt.Println(usererr)
	if usererr == false {
		this.Data["UsernameErr"] = "Username error, Please to again"
		return
	}

	passerr := checkPassword(password)
	if passerr == false {
		this.Data["PasswordErr"] = "Password error, Please to again"
		return
	}

	md5Password := md5.New()
	io.WriteString(md5Password, password)
	buffer := bytes.NewBuffer(nil)
	fmt.Fprintf(buffer, "%x", md5Password.Sum(nil))
	newPass := buffer.String()

	now := time.Now().Format("2006-01-02 15:04:05")

	userInfo := models.GetUserInfo(username)

	if userInfo.Username == "" {
		var users models.User
		users.Username = username
		users.Password = newPass
		users.Created = now
		users.Last_logintime = now
		models.AddUser(users)

		// 登录成功设置 session
		sess := globalSessions.SessionStart(this.Ctx.ResponseWriter, this.Ctx.Request)
		sess.Set("uid", userInfo.Id)
		sess.Set("uname", userInfo.Username)
		this.Ctx.Redirect(302, "/")
	} else {
		this.Data["UsernameErr"] = "User already exists"
	}

}

func checkPassword(password string) (b bool) {
	if ok, _ := regexp.MatchString("^[a-zA-Z0-9]{4,16}$", password); !ok {
		return false
	}
	return true
}

func checkUsername(username string) (b bool) {
	if ok, _ := regexp.MatchString("^[a-zA-Z0-9]{4,16}$", username); !ok {
		return false
	}
	return true
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

有了用户登录和注册之后,其他模块的地方可以增加如下这样的用户是否登录的判断:

func (this *AddBlogController) Prepare() {
	sess := globalSessions.SessionStart(this.Ctx.ResponseWriter, this.Ctx.Request)
	sess_uid := sess.Get("userid")
	sess_username := sess.Get("username")
	if sess_uid == nil {
		this.Ctx.Redirect(302, "/admin/login")
		return
	}
	this.Data["Username"] = sess_username
}
1
2
3
4
5
6
7
8
9
10
  • HTTP Basic 和 HTTP Digest 认证
  • oauth 和 oauth2 的认证
  • 自定义认证