14.4 User validation

In the process of developing web applications, user authentication is a problem which developers frequently encounter. User login, registration and logout, among other operations, as well as general authentication can be divided into three parts:

  • HTTP Basic, and HTTP Digest Authentication
  • Third Party Authentication Integration: QQ, micro-blogging, watercress, OPENID, Google, GitHub, Facebook and twitter, etc.
  • Custom user login, registration, logout, are generally based on sessions and cookie authentication

Beego does not natively provide support for any of these three things, however you can easily make use of existing third party open source libraries to implement them. The first two authentication solutions are on Beego’s roadmap to eventually be integrated.

HTTP basic and digest authentication

Both HTTP basic and digest authentication are relatively simple techniques commonly used by web applications. There are already many open source third-party libraries which support these two authentication methods, such as:

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

The following code demonstrates how to use this library to implement authentication in a Beego application:

  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. }

The above code takes advantage of Beego’s prepare() function to perform authentication before allowing the normal flow of execution to proceed; as you can see, it’s very simple to implement HTTP authentication. Digest authentication can be implemented in much the same way.

OAuth and OAuth 2 authentication

OAuth and OAuth 2 are currently two of the most popular authentication methods. Fortunately, there are third-party libraries which implement this type of authentication such as the go.auth package available on github.

  1. github.com/bradrydzewski/go.auth

The code below demonstrates how to use this library to implement OAuth authentication in Beego using our GitHub credentials:

  1. Let’s add some routes

    1. beego.RegisterController("/auth/login", &controllers.GithubController{})
    2. beego.RegisterController("/mainpage", &controllers.PageController{})
  2. Then we deal with the GithubController landing page:

    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. Handling after a successful landing page:

    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. }

The whole process is as follows:

first open your browser and enter the address:

14.4. User validation - 图1

Figure 14.4 shows the home page with a login button

When clicking on the link, the following screen appears:

14.4. User validation - 图2

Figure 14.5 displayed after clicking the login button to authenticate with your GitHub credentials

After clicking “Authorize app”, the following screen appears:

14.4. User validation - 图3

Figure 14.6 authorized GitHub information gets displayed after the login page

Custom authentication

Custom authentication is generally combined with session authentication; the following code is a Beego based open source blog which demonstrates this:

  1. //Login process
  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. //Set the session successful login
  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. //Registration process
  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. //Set the session successful login
  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. }

Once you have implemented user login and registration, other modules can be added to determine whether the user has been logged in or not:

  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. }