Go实现Web后端基础功能(附源码)

前言:好一段时间没写过博客了,最近比较坎坷,感叹一下。好了,进入主题,源码在最后的链接
功能:
1、实现注册相关,包括用户名(支持中文),密码,验证码。数据库为mysql
2、登录,用session记录登录状态
3、管理,查询用户列表等,json
4、单元测试
实现:
讲一下session的实现原理,session可以通过url和cookie方式实现,推荐cookie方式,是由服务端产生并维护唯一的session id(简称sid),通过setcookie的方式设置在head头部,浏览器解析并存储,下次访问的时候浏览器会携带sid一起发起请求。服务端可以用map来存储,并用list来做垃圾回收。
mysession.go

package other

import (
    "container/list"
    "crypto/rand"
    "encoding/base64"
    "errors"
    "fmt"
    "io"
    _ "log"
    "net/http"
    "net/url"
    _ "reflect"
    "sync"
    "time"
)

type SessionManager struct {
    Lock       sync.Mutex
    Smap       map[string]*list.Element
    SL         *list.List //gc
    Cookiename string
    Expires    int
}
type Session struct {
    Key     string
    Sid     string
    Expires int
    Value   interface{}
}

/*
说明:只在start中用,并已经加锁,因此这里不需要,否则会引起死锁
*/
func (sm *SessionManager) get(sid string) (Session, error) {
    if element, ok := sm.Smap[sid]; ok {
        sm.updateList(sid)
        return *(element.Value.(*Session)), nil
    }
    fmt.Printf("can't find session by sid:%s\n", sid)
    return *sm.NewSession("", sid, ""), errors.New("can't find session by sid")
}

func (sm *SessionManager) NewSession(key string, sid string, value interface{}) *Session {
    return &Session{
        Key:     key,
        Sid:     sid,
        Expires: sm.Expires,
        Value:   value,
    }
}

func (sm *SessionManager) SessionStart(w http.ResponseWriter, r *http.Request) (session Session) {
    sm.Lock.Lock()
    defer sm.Lock.Unlock()
    cookie, err := r.Cookie(sm.Cookiename)
    if err != nil || cookie.Value == "" {
        sid := sm.sessionId()
        session = *sm.NewSession("", sid, "")
        cookie := http.Cookie{Name: sm.Cookiename, Value: url.QueryEscape(sid), Path: "/", HttpOnly: true, MaxAge: sm.Expires}
        http.SetCookie(w, &cookie)
        //fmt.Printf("\ncookie.value:%s,sid:%s\n", cookie.Value, sid)
        //log.Printf("cookiename:%s,err:%s", cookie, err.Error())
    } else {
        sid, _ := url.QueryUnescape(cookie.Value)
        //fmt.Printf("\nget(sid):cookie.value:%s,sid:%s\n", cookie.Value, sid)
        session, _ = sm.get(sid)
        //log.Printf("key:%s,cookie.value:%s,sid:%s\n", session.Key, cookie.Value, sid)
    }
    return
}

//垃圾回收
func (sm *SessionManager) Listen() {
    sm.Lock.Lock()
    defer sm.Lock.Unlock()
    var n *list.Element
    for e := sm.SL.Front(); e != nil; e = n {
        n = e.Next()
        if e.Value.(*Session).Expires == 0 {
            delete(sm.Smap, e.Value.(*Session).Sid)
            sm.SL.Remove(e)
        } else {
            e.Value.(*Session).Expires--
        }
    }

    time.AfterFunc(time.Duration(sm.Expires)*time.Second, func() { sm.Listen() })
}

func (sm *SessionManager) GetByKey(key string) (Session, error) {
    sm.Lock.Lock()
    defer sm.Lock.Unlock()
    for _, element := range sm.Smap {
        if element.Value.(*Session).Key == key {
            sm.updateList(element.Value.(*Session).Sid)
            return *(element.Value.(*Session)), nil
        }
    }
    fmt.Println("can't find session by key")
    return *sm.NewSession("", "", ""), errors.New("can't find session by key")
}

func (sm *SessionManager) Set(key string, sid string) (isExits bool) {
    isExits = false
    if _, ok := sm.Smap[sid]; ok {
        sm.SL.Remove(sm.Smap[sid])
        isExits = true
    }

    element := sm.SL.PushBack(sm.NewSession(key, sid, ""))
    sm.Smap[sid] = element
    return
}

func (sm *SessionManager) Del(key string) (bl bool) {
    sm.Lock.Lock()
    defer sm.Lock.Unlock()
    bl = false
    for _, element := range sm.Smap {
        if element.Value.(*Session).Key == key {
            delete(sm.Smap, element.Value.(*Session).Sid)
            sm.SL.Remove(element)
            bl = true
        }
    }
    return
}

//private函数,Lock之后使用,不用也不能上锁,否则死锁
func (sm *SessionManager) updateList(sid string) {

    if element, ok := sm.Smap[sid]; ok {
        element.Value.(*Session).Expires = sm.Expires
        sm.SL.MoveToBack(element)
    }
}

func (sm *SessionManager) sessionId() string {
    b := make([]byte, 32)
    if _, err := io.ReadFull(rand.Reader, b); err != nil {
        return ""
    }
    return base64.URLEncoding.EncodeToString(b)
}

运行结果:
Go实现Web后端基础功能(附源码)_第1张图片
Go实现Web后端基础功能(附源码)_第2张图片

源码链接:http://download.csdn.net/detail/luomoshusheng/9291683

你可能感兴趣的:(Go)