14.4 用户认证

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

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

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

HTTP Basic和 HTTP Digest认证

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

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

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

  1. package controllers
  2. import (
  3. "github.com/abbot/go-http-auth"
  4. "github.com/astaxie/beego"
  5. )
  6. func Secret(user, realm string) string {
  7. if user == "john" {
  8. // password is "hello"
  9. return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1"
  10. }
  11. return ""
  12. }
  13. type MainController struct {
  14. beego.Controller
  15. }
  16. func (this *MainController) Prepare() {
  17. a := auth.NewBasicAuthenticator("example.com", Secret)
  18. if username := a.CheckAuth(this.Ctx.Request); username == "" {
  19. a.RequireAuth(this.Ctx.ResponseWriter, this.Ctx.Request)
  20. }
  21. }
  22. func (this *MainController) Get() {
  23. this.Data["Username"] = "astaxie"
  24. this.Data["Email"] = "astaxie@gmail.com"
  25. this.TplNames = "index.tpl"
  26. }

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

oauth和oauth2的认证

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

  1. github.com/bradrydzewski/go.auth

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

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

    1. package controllers
    2. import (
    3. "github.com/astaxie/beego"
    4. "github.com/bradrydzewski/go.auth"
    5. )
    6. const (
    7. githubClientKey = "a0864ea791ce7e7bd0df"
    8. githubSecretKey = "a0ec09a647a688a64a28f6190b5a0d2705df56ca"
    9. )
    10. type GithubController struct {
    11. beego.Controller
    12. }
    13. func (this *GithubController) Get() {
    14. // set the auth parameters
    15. auth.Config.CookieSecret = []byte("7H9xiimk2QdTdYI7rDddfJeV")
    16. auth.Config.LoginSuccessRedirect = "/mainpage"
    17. auth.Config.CookieSecure = false
    18. githubHandler := auth.Github(githubClientKey, githubSecretKey)
    19. githubHandler.ServeHTTP(this.Ctx.ResponseWriter, this.Ctx.Request)
    20. }
  3. 处理登陆成功之后的页面

    1. package controllers
    2. import (
    3. "github.com/astaxie/beego"
    4. "github.com/bradrydzewski/go.auth"
    5. "net/http"
    6. "net/url"
    7. )
    8. type PageController struct {
    9. beego.Controller
    10. }
    11. func (this *PageController) Get() {
    12. // set the auth parameters
    13. auth.Config.CookieSecret = []byte("7H9xiimk2QdTdYI7rDddfJeV")
    14. auth.Config.LoginSuccessRedirect = "/mainpage"
    15. auth.Config.CookieSecure = false
    16. user, err := auth.GetUserCookie(this.Ctx.Request)
    17. //if no active user session then authorize user
    18. if err != nil || user.Id() == "" {
    19. http.Redirect(this.Ctx.ResponseWriter, this.Ctx.Request, auth.Config.LoginRedirect, http.StatusSeeOther)
    20. return
    21. }
    22. //else, add the user to the URL and continue
    23. this.Ctx.Request.URL.User = url.User(user.Id())
    24. this.Data["pic"] = user.Picture()
    25. this.Data["id"] = user.Id()
    26. this.Data["name"] = user.Name()
    27. this.TplNames = "home.tpl"
    28. }

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

用户认证 - 图1

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

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

用户认证 - 图2

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

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

用户认证 - 图3

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

自定义认证

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

  1. //登陆处理
  2. func (this *LoginController) Post() {
  3. this.TplNames = "login.tpl"
  4. this.Ctx.Request.ParseForm()
  5. username := this.Ctx.Request.Form.Get("username")
  6. password := this.Ctx.Request.Form.Get("password")
  7. md5Password := md5.New()
  8. io.WriteString(md5Password, password)
  9. buffer := bytes.NewBuffer(nil)
  10. fmt.Fprintf(buffer, "%x", md5Password.Sum(nil))
  11. newPass := buffer.String()
  12. now := time.Now().Format("2006-01-02 15:04:05")
  13. userInfo := models.GetUserInfo(username)
  14. if userInfo.Password == newPass {
  15. var users models.User
  16. users.Last_logintime = now
  17. models.UpdateUserInfo(users)
  18. //登录成功设置session
  19. sess := globalSessions.SessionStart(this.Ctx.ResponseWriter, this.Ctx.Request)
  20. sess.Set("uid", userInfo.Id)
  21. sess.Set("uname", userInfo.Username)
  22. this.Ctx.Redirect(302, "/")
  23. }
  24. }
  25. //注册处理
  26. func (this *RegController) Post() {
  27. this.TplNames = "reg.tpl"
  28. this.Ctx.Request.ParseForm()
  29. username := this.Ctx.Request.Form.Get("username")
  30. password := this.Ctx.Request.Form.Get("password")
  31. usererr := checkUsername(username)
  32. fmt.Println(usererr)
  33. if usererr == false {
  34. this.Data["UsernameErr"] = "Username error, Please to again"
  35. return
  36. }
  37. passerr := checkPassword(password)
  38. if passerr == false {
  39. this.Data["PasswordErr"] = "Password error, Please to again"
  40. return
  41. }
  42. md5Password := md5.New()
  43. io.WriteString(md5Password, password)
  44. buffer := bytes.NewBuffer(nil)
  45. fmt.Fprintf(buffer, "%x", md5Password.Sum(nil))
  46. newPass := buffer.String()
  47. now := time.Now().Format("2006-01-02 15:04:05")
  48. userInfo := models.GetUserInfo(username)
  49. if userInfo.Username == "" {
  50. var users models.User
  51. users.Username = username
  52. users.Password = newPass
  53. users.Created = now
  54. users.Last_logintime = now
  55. models.AddUser(users)
  56. //登录成功设置session
  57. sess := globalSessions.SessionStart(this.Ctx.ResponseWriter, this.Ctx.Request)
  58. sess.Set("uid", userInfo.Id)
  59. sess.Set("uname", userInfo.Username)
  60. this.Ctx.Redirect(302, "/")
  61. } else {
  62. this.Data["UsernameErr"] = "User already exists"
  63. }
  64. }
  65. func checkPassword(password string) (b bool) {
  66. if ok, _ := regexp.MatchString("^[a-zA-Z0-9]{4,16}$", password); !ok {
  67. return false
  68. }
  69. return true
  70. }
  71. func checkUsername(username string) (b bool) {
  72. if ok, _ := regexp.MatchString("^[a-zA-Z0-9]{4,16}$", username); !ok {
  73. return false
  74. }
  75. return true
  76. }

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

  1. func (this *AddBlogController) Prepare() {
  2. sess := globalSessions.SessionStart(this.Ctx.ResponseWriter, this.Ctx.Request)
  3. sess_uid := sess.Get("userid")
  4. sess_username := sess.Get("username")
  5. if sess_uid == nil {
  6. this.Ctx.Redirect(302, "/admin/login")
  7. return
  8. }
  9. this.Data["Username"] = sess_username
  10. }