β

session模块在go语言中的实现

春江花月夜 - Crane's blog 4642 阅读

正在为Words Review 实现云端同步的功能,需要在服务器端维护在线用户的信息,这就需要session的管理。 Web.go 没有实现session模块, go dashboard 上有一个session的实现,但build状态是不通过,而且看源码实现的太复杂。

我没有去仔细翻看session的相关标准,只是基于我之前对web应用中cookie session的理解, 一个基本的session模块只需要能够针对每个session identifier正确的存取数据,管理生命周期就可以了。

下面是在golang中的简单实现, 你可以在github上找到源代码。

首先定义session实例的数据结构。 一个session对象就是一个字符串,存储这个session的id, 由于golang不允许对系统内置数据类型添加方法,我们需要一个自定义类型:

type Session string
    

出于基本的安全考虑,session id需要唯一, 不可预测。 GUID是个不错的选择, 下面这个函数用来产生session id。

golang还没有官方的windows支持,目前的go语言版本调用windows api好像还有问题, 而且我的site也不打算跑在windows下面。 所以windows下guid暂时先用时间戳加随机数的方式, linux下直接读取/dev/urandom

func newGuid() (string, os.Error) {
        if syscall.OS == "windows" {
            //no /dev/urandom on windows
            //TODO: use some windows api instead ?
            guid := getMd5Hex(time.UTC().Format(time.ANSIC) + strconv.Itoa64(rand.Int63()))
            return guid, nil
        }
    
        f, err := os.Open("/dev/urandom")
        if err != nil {
            return "", err
        }
        defer f.Close()
    
        b := make([]byte, 16)
        _, err = f.Read(b)
        if err != nil {
            return "", err
        }
        uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
        return uuid, nil
    }
    

生命周期的管理使用 goroutines 和 channels, 在指定时间后删除session:

func New() Session {
        return NewTimeout(sessionTimeout)
    }
    
    func NewTimeout(timeout int64) Session {
        sid, err := newGuid()
        if err != nil {
            panic(err)
        }
        sessions[sid] = make(map[string]interface{})
        t := time.NewTimer(timeout * 1e9) //timer接受的是nanoseconds
        go startSession(t.C, sid) 
        return Session(sid)
    }
    
    func startSession(c <-chan int64, sid string) {
        <-c
        sessions[sid] = nil, false
    }
    

数据的存取就比较简单了,使用一个package level可见的map变量即可简单实现。

用法示例:

创建新的session:

s := session.New() //默认过期时间1小时
    
    s := session.NewTimeout(20 * 60)  // 20 分钟后过期
    

存取session数据:

err := s.Put("Username", "Bob")
    err  = s.Put("Logintime", time.UTC())
    
    v, err :=  s.Get("Logintime") //return interface{}, need type assertion 
    loginTime := v.(*time.Time)
    

结合web.go:

s := session.New()
    ctx.SetCookie("u", string(s), 3600)  //set session-cookie
    err := s.Put("user", u)
    

关于session肯定还有很多细节没有处理,但满足我现在的需求了。

作者:春江花月夜 - Crane's blog
Crane's Blog
原文地址:session模块在go语言中的实现, 感谢原作者分享。