-
Notifications
You must be signed in to change notification settings - Fork 0
/
Sessions.go
265 lines (242 loc) · 6.69 KB
/
Sessions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
package vweb
import (
"fmt"
"time"
"net/http"
"github.com/456vv/vmap/v2"
"github.com/456vv/verror"
)
type manageSession struct{
s Sessioner
recent time.Time
}
// Sessions集
type Sessions struct{
Expired time.Duration // 保存session时间长
Name string // 标识名称。用于Cookie
Size int // 会话ID长度。用于Cookie
Salt string // 加盐,由于计算机随机数是伪随机数。(可默认为空)。用于Cookie
ActivationID bool // 为true,保持会话ID。意思就是会话ID过期了,可以激活再次使用。用于Cookie
ss vmap.Map // 集,map[id]*Session
}
//Len 当前Session数量
// int 数量
func (T *Sessions) Len() int {
return T.ss.Len()
}
//ProcessDeadAll 定时来处理过期的Session
// []string 过期的ID名称
func (T *Sessions) ProcessDeadAll() []interface{} {
var expId []interface{}
if T.Expired != 0 {
currTime := time.Now()
T.ss.Range(func(id, mse interface{}) bool{
ms := mse.(*manageSession)
recentTime := ms.recent.Add(T.Expired)
if currTime.After(recentTime) {
//追加了expId一次性删除
expId = append(expId, id)
//执行Defer
go ms.s.Free()
}
return true
})
T.ss.Dels(expId)
}
return expId
}
//triggerDeadSession 由用户来触发,并删除已挂载入的Defer
func (T *Sessions) triggerDeadSession(ms *manageSession) (ok bool) {
if T.Expired != 0 {
currTime := time.Now()
recentTime := ms.recent.Add(T.Expired)
if currTime.After(recentTime) {
go ms.s.Free()
return true
}
}
return
}
//generateSessionIdSalt 生成Session标识符,并加盐
// string 标识符
func (T *Sessions) generateSessionIdSalt() string {
rnd := make([]byte, T.Size)
err := GenerateRandomId(rnd)
if err != nil {
panic(err)
}
if T.Salt == "" {
id := fmt.Sprintf("%x", rnd)
return id[:T.Size]
}
return AddSalt(rnd, T.Salt)
}
//generateSessionId 生成Session标识符
// string 标识符
func (T *Sessions) generateSessionId() string {
rnd := make([]byte, T.Size)
err := GenerateRandomId(rnd)
if err != nil {
panic(err)
}
id := fmt.Sprintf("%x", rnd)
return id[:T.Size]
}
//SessionId 从请求中读取会话标识
// req *http.Request 请求
// id string id标识符
// err error 错误
func (T *Sessions) SessionId(req *http.Request) (id string, err error) {
c, err := req.Cookie(T.Name)
if err != nil || c.Value == "" {
return "", verror.TrackErrorf("vweb: 该用会话属性(%s)名称,从客户端请求中没有找可用ID值。", T.Name)
}
return c.Value, nil
}
//NewSession 新建会话
// id string id标识符
// Sessioner 会话
func (T *Sessions) NewSession(id string) Sessioner {
if id == "" {
id = T.generateRandSessionId()
}
if s, ok := T.GetSession(id); ok {
return s
}
return T.SetSession(id, &Session{})
}
//GetSession 使用id读取会话
// id string id标识符
// Sessioner 会话
// bool 是否存在
func (T *Sessions) GetSession(id string) (Sessioner, bool) {
mse, ok := T.ss.GetHas(id)
if !ok {
return nil, false
}
ms := mse.(*manageSession)
if T.triggerDeadSession(ms) {
T.ss.Del(id)
return nil, false
}
ms.recent = time.Now()
return ms.s, true
}
//SetSession 使用id写入新的会话
// id string id标识符
// s Sessioner 新的会话
// Sessioner 会话
func (T *Sessions) SetSession(id string, s Sessioner) Sessioner {
return T.setSession(id, s, true)
}
func (T *Sessions) setSession(id string, s Sessioner, free bool) Sessioner {
if inf, ok := T.ss.GetHas(id); ok {
ms := inf.(*manageSession)
if ms.s.Token() == s.Token() {
//已经存在,无法再设置
return s
}
if free {
//替换原有Session,需要清理原有的defer
go ms.s.Free()
}
}
if t, can := s.(*Session); can {
//对应这个id,并保存
t.id = id
}
ms := &manageSession{
s:s,
recent:time.Now(),
}
T.ss.Set(id, ms)
return s
}
//DelSession 使用id删除的会话
// id string id标识符
func (T *Sessions) DelSession(id string) {
if mse, ok := T.ss.GetHas(id); ok {
ms := mse.(*manageSession)
go ms.s.Free()
T.ss.Del(id)
}
}
//writeToClient 写入到客户端
// rw http.ResponseWriter 响应
// id string id标识符
// Sessioner 会话
func (T *Sessions) writeToClient(rw http.ResponseWriter, id string) Sessioner {
wh := rw.Header()
//防止重复写入
for _, c := range readSetCookies(wh) {
if c.Name == T.Name {
if ss, ok := T.GetSession(c.Value); ok {
return ss
}
}
}
cookie := &http.Cookie{
Name: T.Name,
Value: id,
Path: "/",
HttpOnly: true,
}
wh.Add("Set-Cookie", cookie.String())
return T.SetSession(id, &Session{})
}
func (T *Sessions) generateRandSessionId() string {
var (
id string
maxWait = time.Second
wait time.Duration
printErr = "vweb: 警告>>会话ID即将耗尽,请尽快加大调整ID长度。本次已为用户分配临时ID(%s)\n"
)
if T.Salt != "" {
for id = T.generateSessionIdSalt(); T.ss.Has(id);{
wait=delay(wait, maxWait)
id = T.generateSessionIdSalt()
if wait >= maxWait {
id+="-temp"
//ID即将耗尽
fmt.Printf(printErr, id)
}
}
return id
}
for id = T.generateSessionId(); T.ss.Has(id);{
wait=delay(wait, maxWait)
id = T.generateSessionId()
if wait >= maxWait {
id+="-temp"
//ID即将耗尽
fmt.Printf(printErr, id)
}
}
return id
}
//Session 会话
// rw http.ResponseWriter 响应
// req *http.Request 请求
// Sessioner 会话接口
func (T *Sessions) Session(rw http.ResponseWriter, req *http.Request) Sessioner {
//判断标识名是否存在
id, err := T.SessionId(req)
if err != nil {
//客户是第一次请求,没有会话ID
//现在生成一个ID给客户端
id = T.generateRandSessionId()
return T.writeToClient(rw, id)
}
//判断Id是否有效
s, ok := T.GetSession(id)
if !ok {
//会话ID过期或不存在
//判断是否重新使用旧ID
if T.ActivationID && len(id) == T.Size {
return T.writeToClient(rw, id)
}
id = T.generateRandSessionId()
return T.writeToClient(rw, id)
}
return s
}