Skip to content

Event listeners

English | 中文

Implement SaTokenListener to hook login, logout, kick-out, and related lifecycle events for audit, metrics, or cache invalidation. All hooks have empty defaults — override only what you need.

When to use

  • Write login audit logs or refresh online counts.
  • Notify clients after kick-out / replace.
  • Clear local caches when grants change.

SaTokenListener hooks

rust
use async_trait::async_trait;
use sa_token_core::SaTokenListener;

struct AuditListener;

#[async_trait]
impl SaTokenListener for AuditListener {
    async fn on_login(&self, login_id: &str, token: &str, login_type: &str) {
        let _ = (login_id, token, login_type);
    }
    async fn on_logout(&self, login_id: &str, token: &str, login_type: &str) {
        let _ = (login_id, token, login_type);
    }
    async fn on_kick_out(&self, login_id: &str, token: &str, login_type: &str) {
        let _ = (login_id, token, login_type);
    }
    async fn on_replaced(&self, login_id: &str, token: &str, login_type: &str) {
        let _ = (login_id, token, login_type);
    }
    async fn on_renew_timeout(
        &self,
        login_id: &str,
        token: &str,
        login_type: &str,
        timeout_seconds: i64,
    ) {
        let _ = (login_id, token, login_type, timeout_seconds);
    }
    async fn on_banned(&self, login_id: &str, login_type: &str) {
        let _ = (login_id, login_type);
    }
    async fn on_unbanned(&self, login_id: &str, service: &str, login_type: &str) {
        let _ = (login_id, service, login_type);
    }
    async fn on_open_safe(&self, token: &str, service: &str) {
        let _ = (token, service);
    }
    async fn on_close_safe(&self, token: &str, service: &str) {
        let _ = (token, service);
    }
    async fn on_safe_verify(&self, token: &str, service: &str) {
        let _ = (token, service);
    }
    async fn on_grant_changed(&self, login_id: &str, login_type: &str) {
        let _ = (login_id, login_type);
    }
    async fn on_event(&self, event: &sa_token_core::SaTokenEvent) {
        let _ = event; // fired for every event in addition to typed hooks
    }
}

Built-in LoggingListener is useful for debug logging.

DispatchMode

Inject a custom SaTokenEventBus for dispatch policy:

rust
use std::time::Duration;
use sa_token_core::{
    event::{DispatchMode, EventBusConfig, SaTokenEventBus},
    SaTokenConfig,
};

let bus = SaTokenEventBus::with_config(EventBusConfig {
    dispatch_mode: DispatchMode::Concurrent, // Sequential | Concurrent | Detached
    listener_timeout: Some(Duration::from_secs(5)),
});

SaTokenConfig::builder()
    .storage(storage)
    .event_bus(bus)
    .register_listener(std::sync::Arc::new(AuditListener))
    .try_build()?;
ModeBehavior
Sequential (default)Await listeners in registration order
ConcurrentAwait all listeners in parallel
DetachedRun in the background; does not block publish

Default listener_timeout is 5 seconds; use EventBusConfig::no_timeout() to disable.

Registration

1. Builder (preferred — register at build time):

rust
use std::sync::Arc;
use sa_token_core::SaTokenConfig;
use sa_token_storage_memory::MemoryStorage;

SaTokenConfig::builder()
    .storage(Arc::new(MemoryStorage::new()))
    .register_listener(Arc::new(AuditListener))
    .register_listener(Arc::new(sa_token_core::LoggingListener))
    .try_build()?;

SaTokenState::builder().register_listener(...).build() works the same way.

2. Runtime via StpUtil:

rust
use sa_token_core::StpUtil;

StpUtil::register_listener(Arc::new(AuditListener)); // no-op before init

if let Some(bus) = StpUtil::event_bus() {
    bus.register(Arc::new(AuditListener));
}

event_bus() returns None before init — it does not panic.

MIT OR Apache-2.0