123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- package tango
- import (
- "net/http"
- "time"
- "fmt"
- "strings"
- "crypto/sha1"
- "crypto/hmac"
- "strconv"
- "bytes"
- "encoding/base64"
- "io/ioutil"
- )
- func isValidCookieValue(p []byte) bool {
- for _, b := range p {
- if b <= ' ' ||
- b >= 127 ||
- b == '"' ||
- b == ',' ||
- b == ';' ||
- b == '\\' {
- return false
- }
- }
- return true
- }
- func isValidCookieName(s string) bool {
- for _, r := range s {
- if r <= ' ' ||
- r >= 127 ||
- strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", r) {
- return false
- }
- }
- return true
- }
- type Cookies interface {
- Get(string) *http.Cookie
- Set(*http.Cookie)
- Expire(string, time.Time)
- Del(string)
- }
- type cookies struct {
- req *http.Request
- resp http.ResponseWriter
- }
- func NewCookie(name string, value string, age ...int64) *http.Cookie {
- if !isValidCookieName(name) || !isValidCookieValue([]byte(value)) {
- return nil
- }
- var utctime time.Time
- if len(age) == 0 {
- // 2^31 - 1 seconds (roughly 2038)
- utctime = time.Unix(2147483647, 0)
- } else {
- utctime = time.Unix(time.Now().Unix()+age[0], 0)
- }
- return &http.Cookie{Name: name, Value: value, Expires: utctime}
- }
- func (c *cookies) Get(key string) *http.Cookie {
- ck, err := c.req.Cookie(key)
- if err != nil {
- return nil
- }
- return ck
- }
- func (c *cookies) Set(ck *http.Cookie) {
- http.SetCookie(c.resp, ck)
- }
- func (c *cookies) Expire(key string, expire time.Time) {
- ck := c.Get(key)
- if ck != nil {
- ck.Expires = expire
- c.Set(ck)
- }
- }
- func (c *cookies) Del(key string) {
- c.Expire(key, time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local))
- }
- func getCookieSig(key string, val []byte, timestamp string) string {
- hm := hmac.New(sha1.New, []byte(key))
- hm.Write(val)
- hm.Write([]byte(timestamp))
- hex := fmt.Sprintf("%02x", hm.Sum(nil))
- return hex
- }
- // secure cookies
- type secureCookies struct {
- *cookies
- secret string
- }
- func parseSecureCookie(secret string, value string) string {
- parts := strings.SplitN(value, "|", 3)
- val, timestamp, sig := parts[0], parts[1], parts[2]
- if getCookieSig(secret, []byte(val), timestamp) != sig {
- return ""
- }
- ts, _ := strconv.ParseInt(timestamp, 0, 64)
- if time.Now().Unix()-31*86400 > ts {
- return ""
- }
- buf := bytes.NewBufferString(val)
- encoder := base64.NewDecoder(base64.StdEncoding, buf)
- res, _ := ioutil.ReadAll(encoder)
- return string(res)
- }
- func (c *secureCookies) Get(key string) *http.Cookie {
- ck := c.cookies.Get(key)
- if ck == nil {
- return nil
- }
- v := parseSecureCookie(c.secret, ck.Value)
- if v == "" {
- return nil
- }
- ck.Value = v
- return ck
- }
- func NewSecureCookie(secret, name string, val string, age ...int64) *http.Cookie {
- var buf bytes.Buffer
- encoder := base64.NewEncoder(base64.StdEncoding, &buf)
- encoder.Write([]byte(val))
- encoder.Close()
- vs := buf.String()
- vb := buf.Bytes()
- timestamp := strconv.FormatInt(time.Now().Unix(), 10)
- sig := getCookieSig(secret, vb, timestamp)
- cookie := strings.Join([]string{vs, timestamp, sig}, "|")
- return NewCookie(name, cookie, age...)
- }
- func (c *secureCookies) Expire(key string, expire time.Time) {
- ck := c.Get(key)
- if ck != nil {
- ck.Expires = expire
- c.Set(ck)
- }
- }
- func (c *secureCookies) Del(key string) {
- c.Expire(key, time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local))
- }
|