use std::sync::Arc;
use std::fmt::Display;
use once_cell::sync::OnceCell;
use crate::{SaTokenManager, SaTokenResult, SaTokenError};
use crate::token::{TokenValue, TokenInfo};
use crate::session::SaSession;
use crate::context::SaTokenContext;
use crate::event::{SaTokenEventBus, SaTokenListener};
static GLOBAL_MANAGER: OnceCell<Arc<SaTokenManager>> = OnceCell::new();
pub trait LoginId {
fn to_login_id(&self) -> String;
}
impl<T: Display> LoginId for T {
fn to_login_id(&self) -> String {
self.to_string()
}
}
pub struct StpUtil;
impl StpUtil {
pub fn init_manager(manager: SaTokenManager) {
GLOBAL_MANAGER.set(Arc::new(manager))
.unwrap_or_else(|_| panic!("StpUtil manager already initialized"));
}
fn get_manager() -> &'static Arc<SaTokenManager> {
GLOBAL_MANAGER.get()
.expect("StpUtil not initialized. Call StpUtil::init_manager() first.")
}
pub fn event_bus() -> &'static SaTokenEventBus {
&Self::get_manager().event_bus
}
pub fn register_listener(listener: Arc<dyn SaTokenListener>) {
Self::event_bus().register(listener);
}
pub async fn login(login_id: impl LoginId) -> SaTokenResult<TokenValue> {
Self::get_manager().login(login_id.to_login_id()).await
}
pub async fn login_with_type(login_id: impl LoginId, _login_type: impl Into<String>) -> SaTokenResult<TokenValue> {
Self::get_manager().login(login_id.to_login_id()).await
}
pub async fn login_with_extra(
login_id: impl LoginId,
extra_data: serde_json::Value,
) -> SaTokenResult<TokenValue> {
Self::get_manager().login_with_options(
login_id.to_login_id(),
None, None, Some(extra_data),
None, None, ).await
}
pub async fn login_with_manager(
manager: &SaTokenManager,
login_id: impl Into<String>,
) -> SaTokenResult<TokenValue> {
manager.login(login_id).await
}
pub async fn logout(token: &TokenValue) -> SaTokenResult<()> {
tracing::debug!("开始执行 logout,token: {}", token);
let result = Self::get_manager().logout(token).await;
match &result {
Ok(_) => tracing::debug!("logout 执行成功,token: {}", token),
Err(e) => tracing::debug!("logout 执行失败,token: {}, 错误: {}", token, e),
}
result
}
pub async fn logout_with_manager(
manager: &SaTokenManager,
token: &TokenValue,
) -> SaTokenResult<()> {
manager.logout(token).await
}
pub async fn kick_out(login_id: impl LoginId) -> SaTokenResult<()> {
Self::get_manager().kick_out(&login_id.to_login_id()).await
}
pub async fn kick_out_with_manager(
manager: &SaTokenManager,
login_id: &str,
) -> SaTokenResult<()> {
manager.kick_out(login_id).await
}
pub async fn logout_by_login_id(login_id: impl LoginId) -> SaTokenResult<()> {
Self::get_manager().logout_by_login_id(&login_id.to_login_id()).await
}
pub async fn logout_by_token(token: &TokenValue) -> SaTokenResult<()> {
Self::logout(token).await
}
pub fn get_token_value() -> SaTokenResult<TokenValue> {
let ctx = SaTokenContext::try_current().ok_or(SaTokenError::NotLogin)?;
ctx.token.ok_or(SaTokenError::NotLogin)
}
pub async fn logout_current() -> SaTokenResult<()> {
let token = Self::get_token_value()?;
tracing::debug!("成功获取 token: {}", token);
let result = Self::logout(&token).await;
match &result {
Ok(_) => tracing::debug!("logout_current 执行成功,token: {}", token),
Err(e) => tracing::debug!("logout_current 执行失败,token: {}, 错误: {}", token, e),
}
result
}
pub fn is_login_current() -> bool {
if let Ok(_token) = Self::get_token_value() {
true
} else {
false
}
}
pub fn check_login_current() -> SaTokenResult<()> {
Self::get_token_value()?;
Ok(())
}
pub async fn get_login_id_as_string() -> SaTokenResult<String> {
let token = Self::get_token_value()?;
Self::get_login_id(&token).await
}
pub async fn get_login_id_as_long() -> SaTokenResult<i64> {
let login_id_str = Self::get_login_id_as_string().await?;
login_id_str.parse::<i64>()
.map_err(|_| SaTokenError::LoginIdNotNumber)
}
pub fn get_token_info_current() -> SaTokenResult<Arc<TokenInfo>> {
let ctx = SaTokenContext::try_current().ok_or(SaTokenError::NotLogin)?;
ctx.token_info.ok_or(SaTokenError::NotLogin)
}
pub async fn is_login(token: &TokenValue) -> bool {
Self::get_manager().is_valid(token).await
}
pub async fn is_login_by_login_id(login_id: impl LoginId) -> bool {
match Self::get_token_by_login_id(login_id).await {
Ok(token) => Self::is_login(&token).await,
Err(_) => false,
}
}
pub async fn is_login_with_manager(
manager: &SaTokenManager,
token: &TokenValue,
) -> bool {
manager.is_valid(token).await
}
pub async fn check_login(token: &TokenValue) -> SaTokenResult<()> {
if !Self::is_login(token).await {
return Err(SaTokenError::NotLogin);
}
Ok(())
}
pub async fn get_token_info(token: &TokenValue) -> SaTokenResult<TokenInfo> {
Self::get_manager().get_token_info(token).await
}
pub async fn get_login_id(token: &TokenValue) -> SaTokenResult<String> {
let token_info = Self::get_manager().get_token_info(token).await?;
Ok(token_info.login_id)
}
pub async fn get_login_id_or_default(
token: &TokenValue,
default: impl Into<String>,
) -> String {
Self::get_login_id(token)
.await
.unwrap_or_else(|_| default.into())
}
pub async fn get_token_by_login_id(login_id: impl LoginId) -> SaTokenResult<TokenValue> {
let manager = Self::get_manager();
let login_id_str = login_id.to_login_id();
let key = format!("sa:login:token:{}", login_id_str);
match manager.storage.get(&key).await {
Ok(Some(token_str)) => Ok(TokenValue::new(token_str)),
Ok(None) => Err(SaTokenError::NotLogin),
Err(e) => Err(SaTokenError::StorageError(e.to_string())),
}
}
pub async fn get_all_tokens_by_login_id(login_id: impl LoginId) -> SaTokenResult<Vec<TokenValue>> {
let manager = Self::get_manager();
let login_id_str = login_id.to_login_id();
let key = format!("sa:login:tokens:{}", login_id_str);
match manager.storage.get(&key).await {
Ok(Some(tokens_str)) => {
let token_strings: Vec<String> = serde_json::from_str(&tokens_str)
.map_err(SaTokenError::SerializationError)?;
Ok(token_strings.into_iter().map(TokenValue::new).collect())
}
Ok(None) => Ok(Vec::new()),
Err(e) => Err(SaTokenError::StorageError(e.to_string())),
}
}
pub async fn get_session(login_id: impl LoginId) -> SaTokenResult<SaSession> {
Self::get_manager().get_session(&login_id.to_login_id()).await
}
pub async fn save_session(session: &SaSession) -> SaTokenResult<()> {
Self::get_manager().save_session(session).await
}
pub async fn delete_session(login_id: impl LoginId) -> SaTokenResult<()> {
Self::get_manager().delete_session(&login_id.to_login_id()).await
}
pub async fn set_session_value<T: serde::Serialize>(
login_id: impl LoginId,
key: &str,
value: T,
) -> SaTokenResult<()> {
let manager = Self::get_manager();
let login_id_str = login_id.to_login_id();
let mut session = manager.get_session(&login_id_str).await?;
session.set(key, value)?;
manager.save_session(&session).await
}
pub async fn get_session_value<T: serde::de::DeserializeOwned>(
login_id: impl LoginId,
key: &str,
) -> SaTokenResult<Option<T>> {
let session = Self::get_manager().get_session(&login_id.to_login_id()).await?;
Ok(session.get::<T>(key))
}
pub fn create_token(token_value: impl Into<String>) -> TokenValue {
TokenValue::new(token_value.into())
}
pub fn is_valid_token_format(token: &str) -> bool {
!token.is_empty() && token.len() >= 16
}
}
impl StpUtil {
pub async fn set_permissions(
login_id: impl LoginId,
permissions: Vec<String>,
) -> SaTokenResult<()> {
let manager = Self::get_manager();
let mut map = manager.user_permissions.write().await;
map.insert(login_id.to_login_id(), permissions);
Ok(())
}
pub async fn add_permission(
login_id: impl LoginId,
permission: impl Into<String>,
) -> SaTokenResult<()> {
let manager = Self::get_manager();
let mut map = manager.user_permissions.write().await;
let login_id_str = login_id.to_login_id();
let permissions = map.entry(login_id_str).or_insert_with(Vec::new);
let perm = permission.into();
if !permissions.contains(&perm) {
permissions.push(perm);
}
Ok(())
}
pub async fn remove_permission(
login_id: impl LoginId,
permission: &str,
) -> SaTokenResult<()> {
let manager = Self::get_manager();
let mut map = manager.user_permissions.write().await;
if let Some(permissions) = map.get_mut(&login_id.to_login_id()) {
permissions.retain(|p| p != permission);
}
Ok(())
}
pub async fn clear_permissions(login_id: impl LoginId) -> SaTokenResult<()> {
let manager = Self::get_manager();
let mut map = manager.user_permissions.write().await;
map.remove(&login_id.to_login_id());
Ok(())
}
pub async fn get_permissions(login_id: impl LoginId) -> Vec<String> {
let manager = Self::get_manager();
let map = manager.user_permissions.read().await;
map.get(&login_id.to_login_id()).cloned().unwrap_or_default()
}
pub async fn has_permission(
login_id: impl LoginId,
permission: &str,
) -> bool {
let manager = Self::get_manager();
let map = manager.user_permissions.read().await;
if let Some(permissions) = map.get(&login_id.to_login_id()) {
if permissions.contains(&permission.to_string()) {
return true;
}
for perm in permissions {
if perm.ends_with(":*") {
let prefix = &perm[..perm.len() - 2];
if permission.starts_with(prefix) {
return true;
}
}
}
}
false
}
pub async fn has_all_permissions(
login_id: impl LoginId,
permissions: &[&str],
) -> bool {
let login_id_str = login_id.to_login_id();
for permission in permissions {
if !Self::has_permission(&login_id_str, permission).await {
return false;
}
}
true
}
pub async fn has_permissions_and(
login_id: impl LoginId,
permissions: &[&str],
) -> bool {
Self::has_all_permissions(login_id, permissions).await
}
pub async fn has_any_permission(
login_id: impl LoginId,
permissions: &[&str],
) -> bool {
let login_id_str = login_id.to_login_id();
for permission in permissions {
if Self::has_permission(&login_id_str, permission).await {
return true;
}
}
false
}
pub async fn has_permissions_or(
login_id: impl LoginId,
permissions: &[&str],
) -> bool {
Self::has_any_permission(login_id, permissions).await
}
pub async fn check_permission(
login_id: impl LoginId,
permission: &str,
) -> SaTokenResult<()> {
if !Self::has_permission(login_id, permission).await {
return Err(SaTokenError::PermissionDeniedDetail(permission.to_string()));
}
Ok(())
}
}
impl StpUtil {
pub async fn set_roles(
login_id: impl LoginId,
roles: Vec<String>,
) -> SaTokenResult<()> {
let manager = Self::get_manager();
let mut map = manager.user_roles.write().await;
map.insert(login_id.to_login_id(), roles);
Ok(())
}
pub async fn add_role(
login_id: impl LoginId,
role: impl Into<String>,
) -> SaTokenResult<()> {
let manager = Self::get_manager();
let mut map = manager.user_roles.write().await;
let login_id_str = login_id.to_login_id();
let roles = map.entry(login_id_str).or_insert_with(Vec::new);
let r = role.into();
if !roles.contains(&r) {
roles.push(r);
}
Ok(())
}
pub async fn remove_role(
login_id: impl LoginId,
role: &str,
) -> SaTokenResult<()> {
let manager = Self::get_manager();
let mut map = manager.user_roles.write().await;
if let Some(roles) = map.get_mut(&login_id.to_login_id()) {
roles.retain(|r| r != role);
}
Ok(())
}
pub async fn clear_roles(login_id: impl LoginId) -> SaTokenResult<()> {
let manager = Self::get_manager();
let mut map = manager.user_roles.write().await;
map.remove(&login_id.to_login_id());
Ok(())
}
pub async fn get_roles(login_id: impl LoginId) -> Vec<String> {
let manager = Self::get_manager();
let map = manager.user_roles.read().await;
map.get(&login_id.to_login_id()).cloned().unwrap_or_default()
}
pub async fn has_role(
login_id: impl LoginId,
role: &str,
) -> bool {
let manager = Self::get_manager();
let map = manager.user_roles.read().await;
if let Some(roles) = map.get(&login_id.to_login_id()) {
roles.contains(&role.to_string())
} else {
false
}
}
pub async fn has_all_roles(
login_id: impl LoginId,
roles: &[&str],
) -> bool {
let login_id_str = login_id.to_login_id();
for role in roles {
if !Self::has_role(&login_id_str, role).await {
return false;
}
}
true
}
pub async fn has_roles_and(
login_id: impl LoginId,
roles: &[&str],
) -> bool {
Self::has_all_roles(login_id, roles).await
}
pub async fn has_any_role(
login_id: impl LoginId,
roles: &[&str],
) -> bool {
let login_id_str = login_id.to_login_id();
for role in roles {
if Self::has_role(&login_id_str, role).await {
return true;
}
}
false
}
pub async fn has_roles_or(
login_id: impl LoginId,
roles: &[&str],
) -> bool {
Self::has_any_role(login_id, roles).await
}
pub async fn check_role(
login_id: impl LoginId,
role: &str,
) -> SaTokenResult<()> {
if !Self::has_role(login_id, role).await {
return Err(SaTokenError::RoleDenied(role.to_string()));
}
Ok(())
}
}
impl StpUtil {
pub async fn kick_out_batch<T: LoginId>(
login_ids: &[T],
) -> SaTokenResult<Vec<Result<(), SaTokenError>>> {
let manager = Self::get_manager();
let mut results = Vec::new();
for login_id in login_ids {
results.push(manager.kick_out(&login_id.to_login_id()).await);
}
Ok(results)
}
pub async fn get_token_timeout(token: &TokenValue) -> SaTokenResult<Option<i64>> {
let manager = Self::get_manager();
let token_info = manager.get_token_info(token).await?;
if let Some(expire_time) = token_info.expire_time {
let now = chrono::Utc::now();
let duration = expire_time.signed_duration_since(now);
Ok(Some(duration.num_seconds()))
} else {
Ok(None) }
}
pub async fn renew_timeout(
token: &TokenValue,
timeout_seconds: i64,
) -> SaTokenResult<()> {
let manager = Self::get_manager();
let mut token_info = manager.get_token_info(token).await?;
let new_expire_time = chrono::Utc::now() + chrono::Duration::seconds(timeout_seconds);
token_info.expire_time = Some(new_expire_time);
let key = format!("sa:token:{}", token.as_str());
let value = serde_json::to_string(&token_info)
.map_err(SaTokenError::SerializationError)?;
let timeout = std::time::Duration::from_secs(timeout_seconds as u64);
manager.storage.set(&key, &value, Some(timeout)).await
.map_err(|e| SaTokenError::StorageError(e.to_string()))?;
Ok(())
}
pub async fn set_extra_data(
token: &TokenValue,
extra_data: serde_json::Value,
) -> SaTokenResult<()> {
let manager = Self::get_manager();
let mut token_info = manager.get_token_info(token).await?;
token_info.extra_data = Some(extra_data);
let key = format!("sa:token:{}", token.as_str());
let value = serde_json::to_string(&token_info)
.map_err(SaTokenError::SerializationError)?;
manager.storage.set(&key, &value, manager.config.timeout_duration()).await
.map_err(|e| SaTokenError::StorageError(e.to_string()))?;
Ok(())
}
pub async fn get_extra_data(token: &TokenValue) -> SaTokenResult<Option<serde_json::Value>> {
let manager = Self::get_manager();
let token_info = manager.get_token_info(token).await?;
Ok(token_info.extra_data)
}
pub fn builder(login_id: impl LoginId) -> TokenBuilder {
TokenBuilder::new(login_id.to_login_id())
}
}
pub struct TokenBuilder {
login_id: String,
extra_data: Option<serde_json::Value>,
device: Option<String>,
login_type: Option<String>,
}
impl TokenBuilder {
pub fn new(login_id: String) -> Self {
Self {
login_id,
extra_data: None,
device: None,
login_type: None,
}
}
pub fn extra_data(mut self, data: serde_json::Value) -> Self {
self.extra_data = Some(data);
self
}
pub fn device(mut self, device: impl Into<String>) -> Self {
self.device = Some(device.into());
self
}
pub fn login_type(mut self, login_type: impl Into<String>) -> Self {
self.login_type = Some(login_type.into());
self
}
pub async fn login<T: LoginId>(self, login_id: Option<T>) -> SaTokenResult<TokenValue> {
let manager = StpUtil::get_manager();
let final_login_id = match login_id {
Some(id) => id.to_login_id(),
None => self.login_id,
};
let token = manager.login(final_login_id).await?;
let mut token_info = manager.get_token_info(&token).await?;
if let Some(data) = self.extra_data {
token_info.extra_data = Some(data);
}
if let Some(device) = self.device {
token_info.device = Some(device);
}
if let Some(login_type) = self.login_type {
token_info.login_type = login_type;
}
let key = format!("sa:token:{}", token.as_str());
let value = serde_json::to_string(&token_info)
.map_err(SaTokenError::SerializationError)?;
manager.storage.set(&key, &value, manager.config.timeout_duration()).await
.map_err(|e| SaTokenError::StorageError(e.to_string()))?;
Ok(token)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token_format_validation() {
assert!(StpUtil::is_valid_token_format("1234567890abcdef"));
assert!(!StpUtil::is_valid_token_format(""));
assert!(!StpUtil::is_valid_token_format("short"));
}
#[test]
fn test_create_token() {
let token = StpUtil::create_token("test-token-123");
assert_eq!(token.as_str(), "test-token-123");
}
}