-
Notifications
You must be signed in to change notification settings - Fork 62
/
manager.go
126 lines (100 loc) · 3.41 KB
/
manager.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
// SPDX-License-Identifier: AGPL-3.0-only
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>
package session
import (
"context"
"errors"
"time"
"go.uber.org/zap"
"github.com/bangumi/server/internal/cache"
"github.com/bangumi/server/internal/domain"
"github.com/bangumi/server/internal/model"
"github.com/bangumi/server/internal/pkg/errgo"
"github.com/bangumi/server/internal/pkg/generic"
"github.com/bangumi/server/internal/pkg/random"
"github.com/bangumi/server/internal/pkg/timex"
)
const defaultKeyLength = 32
const CookieKey = "sessionID"
const redisKeyPrefix = "chii:web:session:"
var ErrExpired = errors.New("your session has been expired")
type Manager interface {
Create(ctx context.Context, a domain.Auth) (string, Session, error)
Get(ctx context.Context, key string) (Session, error)
Revoke(ctx context.Context, key string) error
RevokeUser(ctx context.Context, id model.UserID) error
}
func New(c cache.Cache, repo Repo, log *zap.Logger) Manager {
return manager{cache: c, repo: repo, log: log.Named("web.Session.Manager")}
}
type manager struct {
repo Repo
cache cache.Cache
log *zap.Logger
}
func defaultKeyGenerator() string {
return random.Base62String(defaultKeyLength)
}
func (m manager) Create(ctx context.Context, a domain.Auth) (string, Session, error) {
key, s, err := m.repo.Create(ctx, a.ID, a.RegTime, defaultKeyGenerator)
if err != nil {
m.log.Error("un-expected error when creating session", zap.Error(err))
return "", Session{}, errgo.Wrap(err, "un-expected error when creating session")
}
if err := m.cache.Set(ctx, redisKeyPrefix+key, s, timex.OneWeek); err != nil {
return "", Session{}, errgo.Wrap(err, "redis.Set")
}
return key, s, nil
}
func (m manager) Get(ctx context.Context, key string) (Session, error) {
var s Session
ok, err := m.cache.Get(ctx, redisKeyPrefix+key, &s)
if err != nil {
return Session{}, errgo.Wrap(err, "redis.Get")
}
if ok {
return s, nil
}
s, err = m.repo.Get(ctx, key)
if err != nil {
return Session{}, errgo.Wrap(err, "mysqlRepo.Get")
}
if s.ExpiredAt <= time.Now().Unix() {
return Session{}, ErrExpired
}
// 缓存3天或缓存者到token失效
ttl := generic.Min(timex.OneDaySec*3, s.ExpiredAt)
if err := m.cache.Set(ctx, redisKeyPrefix+key, s, timex.Second(ttl)); err != nil {
m.log.Panic("failed to set cache")
}
return s, nil
}
func (m manager) Revoke(ctx context.Context, key string) error {
if err := m.repo.Revoke(ctx, key); err != nil {
return errgo.Wrap(err, "repo.Revoke")
}
err := m.cache.Del(ctx, redisKeyPrefix+key)
return errgo.Wrap(err, "redisCache.Del")
}
func (m manager) RevokeUser(ctx context.Context, id model.UserID) error {
keys, err := m.repo.RevokeUser(ctx, id)
if err != nil {
return errgo.Wrap(err, "repo.Revoke")
}
for i, key := range keys {
keys[i] = redisKeyPrefix + key
}
err = m.cache.Del(ctx, keys...)
return errgo.Wrap(err, "redisCache.Del")
}