#![doc = include_str!("../README.md")]
#![doc(html_favicon_url = "https://summer-rs.github.io/favicon.ico")]
#![doc(html_logo_url = "https://summer-rs.github.io/logo.svg")]
mod config;
mod configurator;
mod custom_storage;
mod prelude;
#[cfg(feature = "with-summer-redis")]
pub mod storage;
use crate::config::SaTokenConfig as summerSaTokenConfig;
use sa_token_adapter::storage::SaStorage;
#[cfg(feature = "with-web")]
use sa_token_plugin_axum::SaTokenLayer;
use sa_token_plugin_axum::SaTokenState;
#[cfg(all(feature = "memory", not(feature = "with-summer-redis")))]
use sa_token_storage_memory::MemoryStorage;
use summer::app::AppBuilder;
use summer::async_trait;
use summer::config::ConfigRegistry;
use summer::plugin::ComponentRegistry;
use summer::plugin::{MutableComponentRegistry, Plugin};
#[cfg(feature = "with-web")]
use summer_web::LayerConfigurator;
pub use prelude::*;
#[cfg(feature = "with-web")]
use sa_token_core::PathAuthConfig as CorePathAuthConfig;
pub struct SaTokenPlugin;
#[async_trait]
impl Plugin for SaTokenPlugin {
async fn build(&self, app: &mut AppBuilder) {
let config = app
.get_config::<summerSaTokenConfig>()
.expect("sa-token plugin config load failed");
tracing::info!("Initializing Sa-Token plugin...");
let state = Self::create_state(app, config)
.await
.expect("sa-token state creation failed");
tracing::debug!(
"SaTokenState manager.config.token_name = {}",
state.manager.config.token_name
);
tracing::info!("Sa-Token plugin initialized successfully");
app.add_component(state.clone());
#[cfg(feature = "with-web")]
{
if let Some(config) = app.get_component::<CorePathAuthConfig>() {
tracing::info!("Registering SaTokenLayer with path-based authentication");
app.add_router_layer(move |router| {
router.layer(SaTokenLayer::with_path_auth(state.clone(), config.clone()))
});
} else {
tracing::info!("Registering SaTokenLayer as router middleware (no path config)");
app.add_router_layer(move |router| router.layer(SaTokenLayer::new(state.clone())));
}
}
}
fn name(&self) -> &str {
"summer_sa_token::SaTokenPlugin"
}
#[cfg(feature = "with-summer-redis")]
fn dependencies(&self) -> Vec<&str> {
vec!["summer_redis::RedisPlugin"]
}
}
impl SaTokenPlugin {
async fn create_state(
app: &AppBuilder,
config: summerSaTokenConfig,
) -> anyhow::Result<SaTokenState> {
let storage = Self::configure_storage(app, &config).await?;
tracing::debug!(
"Sa-Token config: token_name={}, timeout={}, auto_renew={}, is_concurrent={}, is_share={}",
config.token_name, config.timeout, config.auto_renew, config.is_concurrent, config.is_share
);
let mut builder = sa_token_core::SaTokenConfig::builder()
.storage(storage)
.token_name(config.token_name)
.timeout(config.timeout)
.active_timeout(config.active_timeout)
.auto_renew(config.auto_renew)
.is_concurrent(config.is_concurrent)
.is_share(config.is_share)
.token_style(config.token_style.into())
.enable_nonce(config.enable_nonce)
.nonce_timeout(config.nonce_timeout)
.enable_refresh_token(config.enable_refresh_token)
.refresh_token_timeout(config.refresh_token_timeout);
if let Some(prefix) = config.token_prefix {
builder = builder.token_prefix(prefix);
}
if let Some(key) = config.jwt_secret_key {
builder = builder.jwt_secret_key(key);
}
if let Some(algorithm) = config.jwt_algorithm {
builder = builder.jwt_algorithm(algorithm);
}
if let Some(issuer) = config.jwt_issuer {
builder = builder.jwt_issuer(issuer);
}
if let Some(audience) = config.jwt_audience {
builder = builder.jwt_audience(audience);
}
let manager = builder.build();
Ok(SaTokenState::from_manager(manager))
}
#[allow(unused_variables)]
async fn configure_storage(
app: &AppBuilder,
config: &summerSaTokenConfig,
) -> anyhow::Result<std::sync::Arc<dyn SaStorage>> {
if let Some(storage) = app.get_component::<custom_storage::SaTokenStorage>() {
tracing::info!("Using custom SaStorage component");
return Ok(storage.into());
}
#[cfg(feature = "with-summer-redis")]
{
if let Some(redis) = app.get_component::<summer_redis::Redis>() {
tracing::info!("Using SummerRedisStorage (reusing summer-redis connection)");
let storage = storage::SummerRedisStorage::new(
redis,
config.storage_prefix.clone(),
config.rewrite_storage_prefix,
);
Ok(std::sync::Arc::new(storage))
} else {
anyhow::bail!(
"Feature 'with-summer-redis' is enabled but RedisPlugin is not added. \
Please add RedisPlugin before SaTokenPlugin."
);
}
}
#[cfg(all(feature = "memory", not(feature = "with-summer-redis")))]
{
tracing::info!("Using Memory storage");
Ok(std::sync::Arc::new(MemoryStorage::new()))
}
#[cfg(not(any(feature = "memory", feature = "with-summer-redis")))]
{
anyhow::bail!(
"No storage backend available. Enable 'memory' or 'with-summer-redis' feature."
);
}
}
}