Skip to content

Commit 763a0a3

Browse files
committed
add mysql store
1 parent 4716f4e commit 763a0a3

File tree

4 files changed

+406
-3
lines changed

4 files changed

+406
-3
lines changed

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2018 Golang OAuth 2.0
3+
Copyright (c) 2018 Lyric
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

+46-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,46 @@
1-
# mysql
2-
MySQL storage for OAuth 2.0
1+
# MySQL Storage for [OAuth 2.0](https://github.com/go-oauth2/oauth2)
2+
3+
[![ReportCard][reportcard-image]][reportcard-url] [![GoDoc][godoc-image]][godoc-url] [![License][license-image]][license-url]
4+
5+
## Install
6+
7+
``` bash
8+
$ go get -u -v gopkg.in/go-oauth2/mysql.v3
9+
```
10+
11+
## Usage
12+
13+
``` go
14+
package main
15+
16+
import (
17+
"gopkg.in/go-oauth2/mysql.v3"
18+
"gopkg.in/oauth2.v3/manage"
19+
20+
_ "github.com/go-sql-driver/mysql"
21+
)
22+
23+
func main() {
24+
manager := manage.NewDefaultManager()
25+
26+
// use mysql token store
27+
manager.MapTokenStorage(
28+
mysql.NewStore(mysql.NewConfig("root:123456@tcp(127.0.0.1:3306)/myapp_test?charset=utf8") , "", 0),
29+
)
30+
// ...
31+
}
32+
```
33+
34+
## MIT License
35+
36+
```
37+
Copyright (c) 2018 Lyric
38+
```
39+
40+
[reportcard-url]: https://goreportcard.com/report/gopkg.in/go-oauth2/mysql.v3
41+
[reportcard-image]: https://goreportcard.com/badge/gopkg.in/go-oauth2/mysql.v3
42+
[godoc-url]: https://godoc.org/gopkg.in/go-oauth2/mysql.v3
43+
[godoc-image]: https://godoc.org/gopkg.in/go-oauth2/mysql.v3?status.svg
44+
[license-url]: http://opensource.org/licenses/MIT
45+
[license-image]: https://img.shields.io/npm/l/express.svg
46+

mysql.go

+245
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
package mysql
2+
3+
import (
4+
"database/sql"
5+
"fmt"
6+
"io"
7+
"os"
8+
"time"
9+
10+
"github.com/json-iterator/go"
11+
"gopkg.in/gorp.v2"
12+
"gopkg.in/oauth2.v3"
13+
"gopkg.in/oauth2.v3/models"
14+
)
15+
16+
// StoreItem data item
17+
type StoreItem struct {
18+
ID int64 `db:"id,primarykey,autoincrement"`
19+
ExpiredAt int64 `db:"expired_at"`
20+
Code string `db:"code,size:512"`
21+
Access string `db:"access,size:512"`
22+
Refresh string `db:"refresh,size:512"`
23+
Data string `db:"data,size:2048"`
24+
}
25+
26+
// NewConfig create mysql configuration instance
27+
func NewConfig(dsn string) *Config {
28+
return &Config{
29+
DSN: dsn,
30+
MaxLifetime: time.Hour * 2,
31+
MaxOpenConns: 50,
32+
MaxIdleConns: 25,
33+
}
34+
}
35+
36+
// Config mysql configuration
37+
type Config struct {
38+
DSN string
39+
MaxLifetime time.Duration
40+
MaxOpenConns int
41+
MaxIdleConns int
42+
}
43+
44+
// NewStore create mysql store instance,
45+
func NewStore(config *Config, tableName string, gcInterval int) *Store {
46+
db, err := sql.Open("mysql", config.DSN)
47+
if err != nil {
48+
panic(err)
49+
}
50+
51+
db.SetMaxOpenConns(config.MaxOpenConns)
52+
db.SetMaxIdleConns(config.MaxIdleConns)
53+
db.SetConnMaxLifetime(config.MaxLifetime)
54+
55+
return NewStoreWithDB(db, tableName, gcInterval)
56+
}
57+
58+
// NewStoreWithDB create mysql store instance
59+
func NewStoreWithDB(db *sql.DB, tableName string, gcInterval int) *Store {
60+
store := &Store{
61+
db: &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{Encoding: "UTF8", Engine: "MyISAM"}},
62+
tableName: "oauth2_token",
63+
stdout: os.Stderr,
64+
interval: 600,
65+
}
66+
if tableName != "" {
67+
store.tableName = tableName
68+
}
69+
70+
if gcInterval > 0 {
71+
store.interval = gcInterval
72+
}
73+
74+
table := store.db.AddTableWithName(StoreItem{}, store.tableName)
75+
table.AddIndex("idx_code", "Btree", []string{"code"})
76+
table.AddIndex("idx_access", "Btree", []string{"access"})
77+
table.AddIndex("idx_refresh", "Btree", []string{"refresh"})
78+
table.AddIndex("idx_expired_at", "Btree", []string{"expired_at"})
79+
80+
err := store.db.CreateTablesIfNotExists()
81+
if err != nil {
82+
panic(err)
83+
}
84+
store.db.CreateIndex()
85+
86+
go store.gc()
87+
return store
88+
}
89+
90+
// Store mysql token store
91+
type Store struct {
92+
interval int
93+
tableName string
94+
db *gorp.DbMap
95+
stdout io.Writer
96+
}
97+
98+
// SetStdout set error output
99+
func (s *Store) SetStdout(stdout io.Writer) *Store {
100+
s.stdout = stdout
101+
return s
102+
}
103+
104+
// Close close the store
105+
func (s *Store) Close() {
106+
s.db.Db.Close()
107+
}
108+
109+
func (s *Store) errorf(format string, args ...interface{}) {
110+
if s.stdout != nil {
111+
buf := fmt.Sprintf(format, args...)
112+
s.stdout.Write([]byte(buf))
113+
}
114+
}
115+
116+
func (s *Store) gc() {
117+
ticker := time.NewTicker(time.Second * time.Duration(s.interval))
118+
for range ticker.C {
119+
now := time.Now().Unix()
120+
query := fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE expired_at<=?", s.tableName)
121+
n, err := s.db.SelectInt(query, now)
122+
if err != nil {
123+
s.errorf("[ERROR]:%s", err.Error())
124+
return
125+
} else if n > 0 {
126+
_, err = s.db.Exec(fmt.Sprintf("DELETE FROM %s WHERE expired_at<=?", s.tableName), now)
127+
if err != nil {
128+
s.errorf("[ERROR]:%s", err.Error())
129+
}
130+
}
131+
}
132+
}
133+
134+
// Create create and store the new token information
135+
func (s *Store) Create(info oauth2.TokenInfo) error {
136+
buf, _ := jsoniter.Marshal(info)
137+
item := &StoreItem{
138+
Data: string(buf),
139+
}
140+
141+
if code := info.GetCode(); code != "" {
142+
item.Code = code
143+
item.ExpiredAt = info.GetCodeCreateAt().Add(info.GetCodeExpiresIn()).Unix()
144+
} else {
145+
item.Access = info.GetAccess()
146+
item.ExpiredAt = info.GetAccessCreateAt().Add(info.GetAccessExpiresIn()).Unix()
147+
148+
if refresh := info.GetRefresh(); refresh != "" {
149+
item.Refresh = info.GetRefresh()
150+
item.ExpiredAt = info.GetRefreshCreateAt().Add(info.GetRefreshExpiresIn()).Unix()
151+
}
152+
}
153+
154+
return s.db.Insert(item)
155+
}
156+
157+
// RemoveByCode delete the authorization code
158+
func (s *Store) RemoveByCode(code string) error {
159+
query := fmt.Sprintf("UPDATE %s SET code='' WHERE code=? LIMIT 1", s.tableName)
160+
_, err := s.db.Exec(query, code)
161+
if err != nil && err == sql.ErrNoRows {
162+
return nil
163+
}
164+
return err
165+
}
166+
167+
// RemoveByAccess use the access token to delete the token information
168+
func (s *Store) RemoveByAccess(access string) error {
169+
query := fmt.Sprintf("UPDATE %s SET access='' WHERE access=? LIMIT 1", s.tableName)
170+
_, err := s.db.Exec(query, access)
171+
if err != nil && err == sql.ErrNoRows {
172+
return nil
173+
}
174+
return err
175+
}
176+
177+
// RemoveByRefresh use the refresh token to delete the token information
178+
func (s *Store) RemoveByRefresh(refresh string) error {
179+
query := fmt.Sprintf("UPDATE %s SET refresh='' WHERE refresh=? LIMIT 1", s.tableName)
180+
_, err := s.db.Exec(query, refresh)
181+
if err != nil && err == sql.ErrNoRows {
182+
return nil
183+
}
184+
return err
185+
}
186+
187+
func (s *Store) toTokenInfo(data string) oauth2.TokenInfo {
188+
var tm models.Token
189+
jsoniter.Unmarshal([]byte(data), &tm)
190+
return &tm
191+
}
192+
193+
// GetByCode use the authorization code for token information data
194+
func (s *Store) GetByCode(code string) (oauth2.TokenInfo, error) {
195+
if code == "" {
196+
return nil, nil
197+
}
198+
199+
query := fmt.Sprintf("SELECT * FROM %s WHERE code=? LIMIT 1", s.tableName)
200+
var item StoreItem
201+
err := s.db.SelectOne(&item, query, code)
202+
if err != nil {
203+
if err == sql.ErrNoRows {
204+
return nil, nil
205+
}
206+
return nil, err
207+
}
208+
return s.toTokenInfo(item.Data), nil
209+
}
210+
211+
// GetByAccess use the access token for token information data
212+
func (s *Store) GetByAccess(access string) (oauth2.TokenInfo, error) {
213+
if access == "" {
214+
return nil, nil
215+
}
216+
217+
query := fmt.Sprintf("SELECT * FROM %s WHERE access=? LIMIT 1", s.tableName)
218+
var item StoreItem
219+
err := s.db.SelectOne(&item, query, access)
220+
if err != nil {
221+
if err == sql.ErrNoRows {
222+
return nil, nil
223+
}
224+
return nil, err
225+
}
226+
return s.toTokenInfo(item.Data), nil
227+
}
228+
229+
// GetByRefresh use the refresh token for token information data
230+
func (s *Store) GetByRefresh(refresh string) (oauth2.TokenInfo, error) {
231+
if refresh == "" {
232+
return nil, nil
233+
}
234+
235+
query := fmt.Sprintf("SELECT * FROM %s WHERE refresh=? LIMIT 1", s.tableName)
236+
var item StoreItem
237+
err := s.db.SelectOne(&item, query, refresh)
238+
if err != nil {
239+
if err == sql.ErrNoRows {
240+
return nil, nil
241+
}
242+
return nil, err
243+
}
244+
return s.toTokenInfo(item.Data), nil
245+
}

0 commit comments

Comments
 (0)