redistore

repository·master·Indexed 19 days ago

https://github.com/boj/redistore

A session store backend for the gorilla/sessions toolkit that uses Redis as the storage engine for Go applications. It supports multiple serialization formats (Gob, JSON, or custom), key rotation for encryption, and flexible connection options via WithPool, WithAddress, or WithURL. The current recommended version is v2, which utilizes the Option Pattern for configuration.

Tokens
4.9K
Snippets
13
Records
26
Agent score
64%

What's inside redistore

  1. Implement key rotation for encryption keys

    master

    To rotate keys without invalidating existing sessions, provide multiple key pairs to redistore.NewStore. The first pair is used for encoding new sessions, while all provided pairs are attempted for decoding existing sessions.

    Key Sizes:

    • Authentication key: 32 or 64 bytes (HMAC)
    • Encryption key: 16 (AES-128), 24 (AES-192), or 32 bytes (AES-256)

    Rotation Process:

    1. Add new key pair at the beginning of the slice.
    2. Keep old keys for a transition period.
    3. Remove old keys once all sessions have been renewed.

    Helper Functions:

    • KeysFromStrings(keys ...string): Provide keys from strings.
    • Keys(keys ...[]byte): Provide keys as byte slices.
    // Keys are provided in pairs: authentication key, encryption key
    store, err := redistore.NewStore(
        redistore.KeysFromStrings(
            "new-authentication-key", // Used for new sessions
            "new-encryption-key",     // Used for new sessions
            "old-authentication-key", // Tried for decoding
            "old-encryption-key",     // Tried for decoding
        ),
        redistore.WithAddress("tcp", ":6379"),
    )
  2. Use different session serializers

    master

    Redistore supports multiple serialization formats via the WithSerializer option.

    Gob Serializer (Default)

    Uses Go's encoding/gob. Best for complex Go types.

    JSON Serializer

    Uses encoding/json. Best for cross-language compatibility.

    // Using JSON Serializer
    store, err := redistore.NewStore(
        redistore.KeysFromStrings("secret-key"),
        redistore.WithAddress("tcp", ":6379"),
        redistore.WithSerializer(redistore.JSONSerializer{}),
    )

    Custom Serializer

    Implement the SessionSerializer interface to use your own logic:

    type SessionSerializer interface {
        Serialize(ss *sessions.Session) ([]byte, error)
        Deserialize(d []byte, ss *sessions.Session) error
    }
    
    type MySerializer struct{}
    
    func (s MySerializer) Serialize(ss *sessions.Session) ([]byte, error) {
        // implementation
    }
    
    func (s MySerializer) Deserialize(d []byte, ss *sessions.Session) error {
        // implementation
    }
    
    // Usage
    store, err := redistore.NewStore(
        redistore.KeysFromStrings("secret-key"),
        redistore.WithSerializer(MySerializer{}),
    )
  3. Manage sessions (Get, Set, Delete, Flash)

    master

    Redistore integrates with gorilla/sessions for standard session operations.

    Setting and Getting Values

    // Set
    session, _ := store.Get(r, "session-key")
    session.Values["username"] = "john"
    sessions.Save(r, w)
    
    // Get
    session, _ := store.Get(r, "session-key")
    if val, ok := session.Values["username"]; ok {
        username := val.(string)
    }

    Flash Messages

    // Add flash
    session.AddFlash("Welcome!")
    sessions.Save(r, w)
    
    // Retrieve and clear
    flashes := session.Flashes()
    for _, flash := range flashes {
        fmt.Println(flash)
    }
    sessions.Save(r, w) // Must save to clear

    Deleting a Session

    To delete a session, set its MaxAge to -1 and save.

    session, _ := store.Get(r, "session-key")
    session.Options.MaxAge = -1
    sessions.Save(r, w)
  4. Run redistore tests

    master

    To run the test suite, ensure a redis-server is running locally. You can run tests with verbosity or generate coverage reports using standard Go tooling.

    # Start Redis (required)
    redis-server
    
    # Run tests
    go test -v
    
    # With coverage
    go test -v -coverprofile=coverage.out
    go tool cover -html=coverage.out
  5. Quick Start with redistore

    master

    To get started, create a new store using redistore.NewStore. You must provide encryption/authentication keys and at least one connection option (Address, URL, or Pool).

    package main
    
    import (
        "log"
        "net/http"
    
        "github.com/boj/redistore/v2"
        "github.com/gorilla/sessions"
    )
    
    func main() {
        // Create a new store with options
        store, err := redistore.NewStore(
            redistore.KeysFromStrings("secret-key"),
            redistore.WithAddress("tcp", ":6379"),
        )
        if err != nil {
            panic(err)
        }
        defer store.Close()
    
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
            // Get a session
            session, err := store.Get(r, "session-key")
            if err != nil {
                log.Println(err.Error())
                return
            }
    
            // Set a value
            session.Values["foo"] = "bar"
    
            // Save session
            if err = sessions.Save(r, w); err != nil {
                log.Fatalf("Error saving session: %v", err)
            }
        })
    
        log.Fatal(http.ListenAndServe(":8080", nil))
    }
  6. Migrate from redistore v1 to v2

    master

    Version 2.0.0 is a breaking change that replaces multiple specific initialization functions (like NewRediStore, NewRediStoreWithDB, etc.) with a single, unified NewStore(keyPairs, opts...) function using the Option Pattern.

    Migration Steps:

    1. Update Import Path: Change your imports from github.com/boj/redistore to github.com/boj/redistore/v2.
    2. Update Dependencies: Run go get github.com/boj/redistore/v2.
    3. Replace Initialization: Replace old NewRediStore* calls with NewStore and use configuration options (e.g., WithAddress, WithAuth, WithDB) to specify settings.
    go get github.com/boj/redistore/v2
  7. Migrate from v1 to v2

    master

    The NewStore API in v2 is a breaking change from the v1 NewRediStore API. v2 uses the Option Pattern for configuration.

    Key differences:

    • v1 used a long list of positional arguments.
    • v2 uses a slice of byte keys and functional options like redistore.WithAddress.
    // v1 (Legacy)
    store, err := redistore.NewRediStore(10, "tcp", ":6379", "", "", []byte("key"))
    
    // v2 (Recommended)
    store, err := redistore.NewStore(
        []byte("key"),
        redistore.WithAddress("tcp", ":6379"),
    )
  8. Implement custom SessionSerializer

    master

    To use a custom serialization format, implement the SessionSerializer interface:

    type SessionSerializer interface {
    	Deserialize(d []byte, ss *sessions.Session) error
    	Serialize(ss *sessions.Session) ([]byte, error)
    }

    Redistore provides two built-in implementations:

    • JSONSerializer: Uses JSON encoding. Note that all session keys must be strings.
    • GobSerializer: Uses Go's binary gob encoding. This is the default and is efficient for complex Go data structures.
  9. Configure Redis connection options

    master

    When calling redistore.NewStore, you must provide exactly one of the following connection options:

    • WithPool(pool): Use a custom *redis.Pool from the github.com/gomodule/redigo/redis package.
    • WithAddress(network, address): Connect via network and address (e.g., "tcp", ":6379").
    • WithURL(url): Connect via a Redis URL (e.g., "redis://localhost:6379/0").
  10. Handle redistore configuration and connection errors

    master

    When calling redistore.NewStore, you should check the returned err for two main categories of issues:

    1. Configuration Errors: Occur when conflicting options are provided (e.g., providing both WithAddress and WithURL). The error message will indicate that "only one connection option can be specified".
    2. Connection Errors: Occur when the provided connection details are invalid or the Redis server is unreachable. The error message will follow the pattern "failed to connect to Redis: ...".
    // Example Configuration Error
    store, err := redistore.NewStore(
        [][]byte{[]byte("secret-key")},
        redistore.WithAddress("tcp", ":6379"),
        redistore.WithURL("redis://localhost"), // ❌ Error: multiple connection options
    )
    if err != nil {
        log.Fatal(err)
    }
    
    // Example Connection Error
    store, err := redistore.NewStore(
        [][]byte{[]byte("secret-key")},
        redistore.WithAddress("tcp", "invalid:9999"),
    )
    if err != nil {
        // Error: "failed to connect to Redis: ..."
        log.Fatal(err)
    }