mirror of
https://github.com/kataras/iris.git
synced 2025-12-21 11:57:02 +00:00
New JWT features and changes (examples updated). Improvements on the Context User and Private Error features
TODO: Write the new e-book JWT section and the HISTORY entry of the chnages and add a simple example on site docs
This commit is contained in:
131
middleware/jwt/blocklist.go
Normal file
131
middleware/jwt/blocklist.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
stdContext "context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Blocklist is an in-memory storage of tokens that should be
|
||||
// immediately invalidated by the server-side.
|
||||
// The most common way to invalidate a token, e.g. on user logout,
|
||||
// is to make the client-side remove the token itself.
|
||||
// However, if someone else has access to that token,
|
||||
// it could be still valid for new requests until its expiration.
|
||||
type Blocklist struct {
|
||||
entries map[string]time.Time // key = token | value = expiration time (to remove expired).
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewBlocklist returns a new up and running in-memory Token Blocklist.
|
||||
// The returned value can be set to the JWT instance's Blocklist field.
|
||||
func NewBlocklist(gcEvery time.Duration) *Blocklist {
|
||||
return NewBlocklistContext(stdContext.Background(), gcEvery)
|
||||
}
|
||||
|
||||
// NewBlocklistContext same as `NewBlocklist`
|
||||
// but it also accepts a standard Go Context for GC cancelation.
|
||||
func NewBlocklistContext(ctx stdContext.Context, gcEvery time.Duration) *Blocklist {
|
||||
b := &Blocklist{
|
||||
entries: make(map[string]time.Time),
|
||||
}
|
||||
|
||||
if gcEvery > 0 {
|
||||
go b.runGC(ctx, gcEvery)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Set upserts a given token, with its expiration time,
|
||||
// to the block list, so it's immediately invalidated by the server-side.
|
||||
func (b *Blocklist) Set(token string, expiresAt time.Time) {
|
||||
b.mu.Lock()
|
||||
b.entries[token] = expiresAt
|
||||
b.mu.Unlock()
|
||||
}
|
||||
|
||||
// Del removes a "token" from the block list.
|
||||
func (b *Blocklist) Del(token string) {
|
||||
b.mu.Lock()
|
||||
delete(b.entries, token)
|
||||
b.mu.Unlock()
|
||||
}
|
||||
|
||||
// Count returns the total amount of blocked tokens.
|
||||
func (b *Blocklist) Count() int {
|
||||
b.mu.RLock()
|
||||
n := len(b.entries)
|
||||
b.mu.RUnlock()
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// Has reports whether the given "token" is blocked by the server.
|
||||
// This method is called before the token verification,
|
||||
// so even if was expired it is removed from the block list.
|
||||
func (b *Blocklist) Has(token string) bool {
|
||||
if token == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
b.mu.RLock()
|
||||
_, ok := b.entries[token]
|
||||
b.mu.RUnlock()
|
||||
|
||||
/* No, the Blocklist will be used after the token is parsed,
|
||||
there we can call the Del method if err was ErrExpired.
|
||||
if ok {
|
||||
// As an extra step, to keep the list size as small as possible,
|
||||
// we delete it from list if it's going to be expired
|
||||
// ~in the next `blockedExpireLeeway` seconds.~
|
||||
// - Let's keep it easier for testing by not setting a leeway.
|
||||
// if time.Now().Add(blockedExpireLeeway).After(expiresAt) {
|
||||
if time.Now().After(expiresAt) {
|
||||
b.Del(token)
|
||||
}
|
||||
}*/
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// GC iterates over all entries and removes expired tokens.
|
||||
// This method is helpful to keep the list size small.
|
||||
// Depending on the application, the GC method can be scheduled
|
||||
// to called every half or a whole hour.
|
||||
// A good value for a GC cron task is the JWT's max age (default).
|
||||
func (b *Blocklist) GC() int {
|
||||
now := time.Now()
|
||||
var markedForDeletion []string
|
||||
|
||||
b.mu.RLock()
|
||||
for token, expiresAt := range b.entries {
|
||||
if now.After(expiresAt) {
|
||||
markedForDeletion = append(markedForDeletion, token)
|
||||
}
|
||||
}
|
||||
b.mu.RUnlock()
|
||||
|
||||
n := len(markedForDeletion)
|
||||
if n > 0 {
|
||||
for _, token := range markedForDeletion {
|
||||
b.Del(token)
|
||||
}
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (b *Blocklist) runGC(ctx stdContext.Context, every time.Duration) {
|
||||
t := time.NewTicker(every)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Stop()
|
||||
return
|
||||
case <-t.C:
|
||||
b.GC()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user