読者です 読者をやめる 読者になる 読者になる

ikeikeikeike's unk blog.

http://github-awards.com/users/ikeikeikeike

べーごー signup,login,logout

Beegoで認証あたりを処理を書いた(メール飛ばしたりはしてない)。以下やっていること

  • プロジェクト生成
  • Model定義
  • Form処理
  • ユーザー, 認証処理
  • Flash messages, Session処理
  • login filter(require)なるもの

もろもろインストール

$ go get -u github.com/beego/bee
$ go get -u github.com/astaxie/beego

プロジェクト作成コマンド

$ bee new auth

こんなツリーが出来る

$ tree
.
├── conf
│   └── app.conf
├── controllers
│   └── default.go
├── main.go
├── models
├── routers
│   └── router.go
├── static
│   ├── css
│   ├── img
│   └── js
├── tests
│   └── default_test.go
└── views
    └── index.tpl

ユーザーを定義する

Email,Password,ログイン時間,作成時間,更新時間のよくある形

package models

import (
    "time"

    "github.com/astaxie/beego"
    "github.com/astaxie/beego/orm"
)

type User struct {
    Id            int64
    Email         string    `orm:"size(64);unique"`
    Password      string    `orm:"size(32)"`
    Lastlogintime time.Time `orm:"type(datetime);null"`
    Created       time.Time `orm:"auto_now_add;type(datetime)"`
    Updated       time.Time `orm:"auto_now;type(datetime)"`
}

func init() {
    orm.RegisterModelWithPrefix(
        beego.AppConfig.String("dbprefix"),
        new(User))
}

.. doc::

Fieldに複数の属性をつける場合ははセミコロン(;)でつなげる どんな属性があるかはドキュメントを: Model Definition - beego: simple & powerful Go app framework

Modelを定義したらDDLを走らせる。 これは以下のようにorm.RunSyncdbをinitあたりに定義しておけばBeegoがやってくれる (ノリ的にDjangoのsyncdbと同じもの)

func init() {
    dbname, force, verbose := "default", false, true
    err := orm.RunSyncdb(dbname, force, verbose)
    if err != nil {
        panic(err)
    }
}

私はこんなふうに書いてます: beego-samples/db.go at master · ikeikeikeike/beego-samples · GitHub

Formの定義

先ほど定義したuserテーブルを編集, パスワード確認のRepasswordと「form:」「valud:」を追加

type User struct {
    Id            int64
    Email         string    `orm:"size(64);unique" form:"Email"      valid:"Required;Email"`
    Password      string    `orm:"size(32)"        form:"Password"   valid:"Required;MinSize(6)"`
    Repassword    string    `orm:"-"               form:"Repassword" valid:"Required"`
    Lastlogintime time.Time `orm:"type(datetime);null" form:"-"`
    Created       time.Time `orm:"auto_now_add;type(datetime)"`
    Updated       time.Time `orm:"auto_now;type(datetime)"`
}

「form:」はhtml上のkinput[name="Email"]のところ。「valud:」はValidation定義 ドキュメントはここ: Form validation - beego: simple & powerful Go app framework

上述に対応するHTMLが下記のような形になる。

<form method="POST" action='{{urlfor "LoginController.Login"}}'>
  {{ .xsrfdata }}

  <input name="Email" type="email" value="{{index .Params "Email"}}" required />
  <input name="Password" type="password" value="" required pattern=".{6,}" 
         title="パスワードは6文字以上を入力してください" />
  <input name="Repassword" type="password" required pattern=".{6,}" 
         title="パスワードは6文字以上を入力してください" />

  <input type="submit" value="ログイン"> 
</form>

次は認証処理

  1. Routerの追加

Routerは以下の物があり,Automatching, Annotations, Namespaceがあったり色々できる

  • Basic router
  • RESTful router
  • Fixed router
  • Regex router

以下の例ははSinatra風に記述ができるRegex routerを使用している

package routers

import (
    ctl "bitbucket.org/ikeikeikeike/auth/controllers"
    "github.com/astaxie/beego"
)

func init() {
    beego.Router("/login", &ctl.LoginController{}, "get,post:Login")
    beego.Router("/logout", &ctl.LoginController{}, "get:Logout")
    beego.Router("/signup", &ctl.LoginController{}, "get,post:Signup")
}
  1. Controller Signup部分

xsrf, flash message, formのパース, Sessionとか色々やってる

func (c *LoginController) Signup() {
    c.TplNames = "login/signup.tpl"
    c.Data["xsrfdata"] = template.HTML(c.XsrfFormHtml())

    if !c.Ctx.Input.IsPost() {
        return
    }

    var err error
    flash := beego.NewFlash()

    u := &models.User{}
    if err = c.ParseForm(u); err != nil {
        flash.Error("Signup invalid!")
        flash.Store(&c.Controller)
        return
    }
    if err = models.IsValid(u); err != nil {
        flash.Error(err.Error())
        flash.Store(&c.Controller)
        return
    }

    id, err := lib.SignupUser(u)
    if err != nil || id < 1 {
        flash.Warning(err.Error())
        flash.Store(&c.Controller)
        return
    }

    flash.Success("Register user: %s", u.Email)
    flash.Store(&c.Controller)

    c.SetLogin(u)

    c.Redirect(c.UrlFor("UsersController.Index"), 303)
}
  1. Ctrl Login,Logout部分

とくに変わったことなし, URL参照: beego-samples/login.go at master · ikeikeikeike/beego-samples · GitHub

次,リクエスト時にログインしているか事前に確認するようにする

Filtersを使用: Filters - beego: simple & powerful Go app framework

   beego.InsertFilter("/", beego.BeforeRouter, func(ctx *context.Context) {
        _, ok := ctx.Input.Session("userinfo").(int64)
        if !ok {
            ctx.Redirect(302, "/login")
        }
    })

    beego.Router("/", &ctl.UsersController{}, "get:Index")
    beego.Router("/login", &ctl.LoginController{}, "get,post:Login")
    beego.Router("/logout", &ctl.LoginController{}, "get:Logout")
    beego.Router("/signup", &ctl.LoginController{}, "get,post:Signup")

Prepareをoverwriteすることでも実現できる: Controller funcs - beego: simple & powerful Go app framework

func (c *UsersController) Prepare() {
    if !c.IsLogin {
        c.Ctx.Redirect(302, c.LoginPath())
        return
    }
}

以上どす

上述の例を少しだけ拡張したサンプルソースコード