f419c124 by slene

add captcha util

1 parent fee3c2b8
...@@ -3,7 +3,6 @@ package beego ...@@ -3,7 +3,6 @@ package beego
3 import ( 3 import (
4 "bytes" 4 "bytes"
5 "crypto/hmac" 5 "crypto/hmac"
6 "crypto/rand"
7 "crypto/sha1" 6 "crypto/sha1"
8 "encoding/base64" 7 "encoding/base64"
9 "errors" 8 "errors"
...@@ -22,6 +21,7 @@ import ( ...@@ -22,6 +21,7 @@ import (
22 21
23 "github.com/astaxie/beego/context" 22 "github.com/astaxie/beego/context"
24 "github.com/astaxie/beego/session" 23 "github.com/astaxie/beego/session"
24 "github.com/astaxie/beego/utils"
25 ) 25 )
26 26
27 var ( 27 var (
...@@ -455,7 +455,7 @@ func (c *Controller) XsrfToken() string { ...@@ -455,7 +455,7 @@ func (c *Controller) XsrfToken() string {
455 } else { 455 } else {
456 expire = int64(XSRFExpire) 456 expire = int64(XSRFExpire)
457 } 457 }
458 token = getRandomString(15) 458 token = string(utils.RandomCreateBytes(15))
459 c.SetSecureCookie(XSRFKEY, "_xsrf", token, expire) 459 c.SetSecureCookie(XSRFKEY, "_xsrf", token, expire)
460 } 460 }
461 c._xsrf_token = token 461 c._xsrf_token = token
...@@ -492,14 +492,3 @@ func (c *Controller) XsrfFormHtml() string { ...@@ -492,14 +492,3 @@ func (c *Controller) XsrfFormHtml() string {
492 func (c *Controller) GetControllerAndAction() (controllerName, actionName string) { 492 func (c *Controller) GetControllerAndAction() (controllerName, actionName string) {
493 return c.controllerName, c.actionName 493 return c.controllerName, c.actionName
494 } 494 }
495
496 // getRandomString returns random string.
497 func getRandomString(n int) string {
498 const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
499 var bytes = make([]byte, n)
500 rand.Read(bytes)
501 for i, b := range bytes {
502 bytes[i] = alphanum[b%byte(len(alphanum))]
503 }
504 return string(bytes)
505 }
......
1 package captcha
2
3 // modifiy and integrated to Beego in one file from https://github.com/dchest/captcha
4
5 import (
6 "fmt"
7 "html/template"
8 "net/http"
9 "path"
10 "strings"
11
12 "github.com/astaxie/beego"
13 "github.com/astaxie/beego/cache"
14 "github.com/astaxie/beego/context"
15 "github.com/astaxie/beego/utils"
16 )
17
18 var (
19 defaultChars = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
20 )
21
22 const (
23 challengeNums = 6
24 expiration = 600
25 fieldIdName = "captcha_id"
26 fieldCaptchaName = "captcha"
27 cachePrefix = "captcha_"
28 urlPrefix = "/captcha/"
29 )
30
31 type Captcha struct {
32 store cache.Cache
33 urlPrefix string
34 FieldIdName string
35 FieldCaptchaName string
36 StdWidth int
37 StdHeight int
38 ChallengeNums int
39 Expiration int64
40 CachePrefix string
41 }
42
43 func (c *Captcha) key(id string) string {
44 return c.CachePrefix + id
45 }
46
47 func (c *Captcha) genRandChars() []byte {
48 return utils.RandomCreateBytes(c.ChallengeNums, defaultChars...)
49 }
50
51 func (c *Captcha) Handler(ctx *context.Context) {
52 var chars []byte
53
54 id := path.Base(ctx.Request.RequestURI)
55 if i := strings.Index(id, "."); i != -1 {
56 id = id[:i]
57 }
58
59 key := c.key(id)
60
61 if v, ok := c.store.Get(key).([]byte); ok {
62 chars = v
63 } else {
64 ctx.Output.SetStatus(404)
65 ctx.WriteString("captcha not found")
66 return
67 }
68
69 // reload captcha
70 if len(ctx.Input.Query("reload")) > 0 {
71 chars = c.genRandChars()
72 if err := c.store.Put(key, chars, c.Expiration); err != nil {
73 ctx.Output.SetStatus(500)
74 ctx.WriteString("captcha reload error")
75 beego.Error("Reload Create Captcha Error:", err)
76 return
77 }
78 }
79
80 img := NewImage(chars, c.StdWidth, c.StdHeight)
81 if _, err := img.WriteTo(ctx.ResponseWriter); err != nil {
82 beego.Error("Write Captcha Image Error:", err)
83 }
84 }
85
86 func (c *Captcha) CreateCaptchaHtml() template.HTML {
87 value, err := c.CreateCaptcha()
88 if err != nil {
89 beego.Error("Create Captcha Error:", err)
90 return ""
91 }
92
93 // create html
94 return template.HTML(fmt.Sprintf(`<input type="hidden" name="%s" value="%s">`+
95 `<a class="captcha" href="javascript:">`+
96 `<img onclick="this.src=('%s%s.png?reload='+(new Date()).getTime())" class="captcha-img" src="%s%s.png">`+
97 `</a>`, c.FieldIdName, value, c.urlPrefix, value, c.urlPrefix, value))
98 }
99
100 func (c *Captcha) CreateCaptcha() (string, error) {
101 // generate captcha id
102 id := string(utils.RandomCreateBytes(15))
103
104 // get the captcha chars
105 chars := c.genRandChars()
106
107 // save to store
108 if err := c.store.Put(c.key(id), chars, c.Expiration); err != nil {
109 return "", err
110 }
111
112 return id, nil
113 }
114
115 func (c *Captcha) VerifyReq(req *http.Request) bool {
116 req.ParseForm()
117 return c.Verify(req.Form.Get(c.FieldIdName), req.Form.Get(c.FieldCaptchaName))
118 }
119
120 func (c *Captcha) Verify(id string, challenge string) (success bool) {
121 if len(challenge) == 0 || len(id) == 0 {
122 return
123 }
124
125 var chars []byte
126
127 key := c.key(id)
128
129 if v, ok := c.store.Get(key).([]byte); ok && len(v) == len(challenge) {
130 chars = v
131 } else {
132 return
133 }
134
135 defer func() {
136 // finally remove it
137 c.store.Delete(key)
138 }()
139
140 // verify challenge
141 for i, c := range chars {
142 if c != challenge[i]-48 {
143 return
144 }
145 }
146
147 return true
148 }
149
150 func NewCaptcha(urlPrefix string, store cache.Cache) *Captcha {
151 cpt := &Captcha{}
152 cpt.store = store
153 cpt.FieldIdName = fieldIdName
154 cpt.FieldCaptchaName = fieldCaptchaName
155 cpt.ChallengeNums = challengeNums
156 cpt.Expiration = expiration
157 cpt.CachePrefix = cachePrefix
158 cpt.StdWidth = stdWidth
159 cpt.StdHeight = stdHeight
160
161 if len(urlPrefix) == 0 {
162 urlPrefix = urlPrefix
163 }
164
165 if urlPrefix[len(urlPrefix)-1] != '/' {
166 urlPrefix += "/"
167 }
168
169 cpt.urlPrefix = urlPrefix
170
171 return cpt
172 }
173
174 func NewWithFilter(urlPrefix string, store cache.Cache) *Captcha {
175 cpt := NewCaptcha(urlPrefix, store)
176
177 // create filter for serve captcha image
178 beego.AddFilter(urlPrefix+":", "BeforeRouter", cpt.Handler)
179
180 // add to template func map
181 beego.AddFuncMap("create_captcha", cpt.CreateCaptchaHtml)
182
183 return cpt
184 }
1 package captcha
2
3 import (
4 "testing"
5
6 "github.com/astaxie/beego/utils"
7 )
8
9 type byteCounter struct {
10 n int64
11 }
12
13 func (bc *byteCounter) Write(b []byte) (int, error) {
14 bc.n += int64(len(b))
15 return len(b), nil
16 }
17
18 func BenchmarkNewImage(b *testing.B) {
19 b.StopTimer()
20 d := utils.RandomCreateBytes(challengeNums, defaultChars...)
21 b.StartTimer()
22 for i := 0; i < b.N; i++ {
23 NewImage(d, stdWidth, stdHeight)
24 }
25 }
26
27 func BenchmarkImageWriteTo(b *testing.B) {
28 b.StopTimer()
29 d := utils.RandomCreateBytes(challengeNums, defaultChars...)
30 b.StartTimer()
31 counter := &byteCounter{}
32 for i := 0; i < b.N; i++ {
33 img := NewImage(d, stdWidth, stdHeight)
34 img.WriteTo(counter)
35 b.SetBytes(counter.n)
36 counter.n = 0
37 }
38 }
1 package utils
2
3 import (
4 "crypto/rand"
5 )
6
7 // RandomCreateBytes generate random []byte by specify chars.
8 func RandomCreateBytes(n int, alphabets ...byte) []byte {
9 const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
10 var bytes = make([]byte, n)
11 rand.Read(bytes)
12 for i, b := range bytes {
13 if len(alphabets) == 0 {
14 bytes[i] = alphanum[b%byte(len(alphanum))]
15 } else {
16 bytes[i] = alphabets[b%byte(len(alphabets))]
17 }
18 }
19 return bytes
20 }
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!