Skip to main content

sa_token_core/
error.rs

1// Author: 金书记
2//
3//! Error type definitions | 错误类型定义
4
5use thiserror::Error;
6
7/// Result alias for core operations | 核心操作结果别名
8pub type SaTokenResult<T> = Result<T, SaTokenError>;
9
10/// Unified error type for sa-token-core | sa-token-core 统一错误类型
11#[derive(Debug, Error)]
12pub enum SaTokenError {
13    // ============ Basic Token Errors | 基础 Token 错误 ============
14    /// Token not found or expired | Token 不存在或已过期
15    #[error("Token not found or expired")]
16    TokenNotFound,
17
18    /// Token value is invalid | Token 值无效
19    #[error("Token is invalid: {0}")]
20    InvalidToken(String),
21
22    /// Token has expired | Token 已过期
23    #[error("Token has expired")]
24    TokenExpired,
25
26    // ============ Authentication Errors | 认证错误 ============
27    /// Caller is not logged in | 当前未登录
28    #[error("User not logged in")]
29    NotLogin,
30
31    /// Token exists but is inactive | Token 存在但未激活
32    #[error("Token is inactive")]
33    TokenInactive,
34
35    // ============ Authorization Errors | 授权错误 ============
36    /// Permission check failed | 权限校验失败
37    #[error("Permission denied")]
38    PermissionDenied,
39
40    /// Missing a specific permission | 缺少指定权限
41    #[error("Permission denied: missing permission '{0}'")]
42    PermissionDeniedDetail(String),
43
44    /// Missing a specific role | 缺少指定角色
45    #[error("Role denied: missing role '{0}'")]
46    RoleDenied(String),
47
48    // ============ Account Status Errors | 账户状态错误 ============
49    /// Account is banned until the given time | 账号被封禁至指定时间
50    #[error("Account is banned until {0}")]
51    AccountBanned(String),
52
53    /// Account was kicked out | 账号已被踢下线
54    #[error("Account is kicked out")]
55    AccountKickedOut,
56
57    /// Login was replaced on another device | 账号在其他设备顶替登录
58    #[error("Account login has been replaced on another device")]
59    AccountReplaced,
60
61    /// Secondary authentication required | 需要二次认证
62    #[error("Secondary authentication required for service '{0}'")]
63    NotSafe(String),
64
65    /// Account disabled for a service at a level | 账号在某服务下被禁用
66    #[error("Account is disabled for service '{service}' at level {level}")]
67    DisableService {
68        /// Service name | 服务名
69        service: String,
70        /// Disable level | 禁用等级
71        level: i32,
72    },
73
74    // ============ Session Errors | Session 错误 ============
75    /// Session not found | Session 不存在
76    #[error("Session not found")]
77    SessionNotFound,
78
79    // ============ Nonce Errors | Nonce 错误 ============
80    /// Nonce already consumed (possible replay) | Nonce 已使用(疑似重放)
81    #[error("Nonce has been used, possible replay attack detected")]
82    NonceAlreadyUsed,
83
84    /// Nonce format is invalid | Nonce 格式无效
85    #[error("Invalid nonce format")]
86    InvalidNonceFormat,
87
88    /// Nonce timestamp invalid or expired | Nonce 时间戳无效或过期
89    #[error("Nonce timestamp is invalid or expired")]
90    InvalidNonceTimestamp,
91
92    // ============ Refresh Token Errors | 刷新令牌错误 ============
93    /// Refresh token missing or expired | 刷新令牌不存在或过期
94    #[error("Refresh token not found or expired")]
95    RefreshTokenNotFound,
96
97    /// Refresh token payload invalid | 刷新令牌数据无效
98    #[error("Invalid refresh token data")]
99    RefreshTokenInvalidData,
100
101    /// Refresh token missing login_id | 刷新令牌缺少 login_id
102    #[error("Missing login_id in refresh token")]
103    RefreshTokenMissingLoginId,
104
105    /// Refresh token expire time format invalid | 刷新令牌过期时间格式无效
106    #[error("Invalid expire time format in refresh token")]
107    RefreshTokenInvalidExpireTime,
108
109    // ============ Token Validation Errors | Token 验证错误 ============
110    /// Token string is empty | Token 为空
111    #[error("Token is empty")]
112    TokenEmpty,
113
114    /// Token string is too short | Token 过短
115    #[error("Token is too short")]
116    TokenTooShort,
117
118    /// Login id is not a valid number | 登录 ID 不是合法数字
119    #[error("Login ID is not a valid number")]
120    LoginIdNotNumber,
121
122    // ============ OAuth2 Errors | OAuth2 错误 ============
123    /// OAuth2 client not found | OAuth2 客户端不存在
124    #[error("OAuth2 client not found")]
125    OAuth2ClientNotFound,
126
127    /// Invalid OAuth2 client credentials | OAuth2 客户端凭据无效
128    #[error("Invalid client credentials")]
129    OAuth2InvalidCredentials,
130
131    /// OAuth2 client id mismatch | OAuth2 客户端 ID 不匹配
132    #[error("Client ID mismatch")]
133    OAuth2ClientIdMismatch,
134
135    /// OAuth2 redirect URI mismatch | OAuth2 回调地址不匹配
136    #[error("Redirect URI mismatch")]
137    OAuth2RedirectUriMismatch,
138
139    /// Authorization code missing or expired | 授权码不存在或过期
140    #[error("Authorization code not found or expired")]
141    OAuth2CodeNotFound,
142
143    /// Access token missing or expired | 访问令牌不存在或过期
144    #[error("Access token not found or expired")]
145    OAuth2AccessTokenNotFound,
146
147    /// Refresh token missing or expired | 刷新令牌不存在或过期
148    #[error("Refresh token not found or expired")]
149    OAuth2RefreshTokenNotFound,
150
151    /// Invalid OAuth2 refresh token data | OAuth2 刷新令牌数据无效
152    #[error("Invalid refresh token data")]
153    OAuth2InvalidRefreshToken,
154
155    /// Invalid OAuth2 scope data | OAuth2 scope 数据无效
156    #[error("Invalid scope data")]
157    OAuth2InvalidScope,
158
159    /// PKCE code_verifier required | 需要 PKCE code_verifier
160    #[error("OAuth2 PKCE code_verifier required")]
161    OAuth2PkceRequired,
162
163    /// PKCE verification failed | PKCE 校验失败
164    #[error("OAuth2 PKCE verification failed")]
165    OAuth2PkceMismatch,
166
167    /// Token revoke failed | 令牌吊销失败
168    #[error("OAuth2 token revoke failed: {0}")]
169    OAuth2TokenRevokeFailed(String),
170
171    /// Unsupported OAuth2 grant type | 不支持的授权类型
172    #[error("OAuth2 unsupported grant type")]
173    OAuth2UnsupportedGrant,
174
175    /// Public client must use PKCE S256 | 公共客户端必须使用 PKCE S256
176    #[error("OAuth2 public client must use PKCE S256")]
177    OAuth2PkceRequiredForPublicClient,
178
179    // ============ SSO Errors | SSO 单点登录错误 ============
180    /// SSO ticket not found or invalid | SSO ticket 不存在或无效
181    #[error("SSO ticket not found or invalid")]
182    InvalidTicket,
183
184    /// SSO ticket expired | SSO ticket 已过期
185    #[error("SSO ticket has expired")]
186    TicketExpired,
187
188    /// Service URL mismatch | 服务地址不匹配
189    #[error("Service URL mismatch")]
190    ServiceMismatch,
191
192    /// SSO session not found | SSO 会话不存在
193    #[error("SSO session not found")]
194    SsoSessionNotFound,
195
196    /// SSO request signature invalid | SSO 请求签名无效
197    #[error("SSO request signature invalid")]
198    SsoSignInvalid,
199
200    /// Device / terminal type is not allowed.
201    /// 设备/终端类型不允许。
202    #[error("Terminal denied: expected '{expected}', actual '{actual}'")]
203    TerminalDenied {
204        /// Allowed terminal pattern | 允许的终端模式
205        expected: String,
206        /// Actual terminal value | 实际终端值
207        actual: String,
208    },
209
210    /// Same-Token header missing or not matching current/past token.
211    /// Same-Token 头缺失或与当前/宽限 token 不一致。
212    #[error("Invalid same-token")]
213    SameTokenInvalid,
214
215    /// HTTP Basic credentials missing or mismatch.
216    /// HTTP Basic 凭据缺失或不匹配。
217    #[error("HTTP Basic authentication failed")]
218    BasicAuthFailed {
219        /// Auth realm for WWW-Authenticate | WWW-Authenticate 的 realm
220        realm: String,
221    },
222
223    /// Request signature does not match.
224    /// 请求签名不匹配。
225    #[error("Invalid request signature")]
226    SignInvalid,
227
228    /// Request `timestamp` missing or outside the allowed window.
229    /// 请求 `timestamp` 缺失或超出允许窗口。
230    #[error("Request signature timestamp is invalid or expired")]
231    SignTimestampExpired,
232
233    /// Temp token missing or already deleted.
234    /// 临时令牌不存在或已删除。
235    #[error("Temp token not found")]
236    TempTokenNotFound,
237
238    /// Temp token past expire_at.
239    /// 临时令牌已过 expire_at。
240    #[error("Temp token has expired")]
241    TempTokenExpired,
242
243    // ============ Lifecycle Errors | 生命周期错误 ============
244    /// 全局 Manager 尚未初始化
245    /// Global manager has not been initialized
246    #[error("Sa-Token manager is not initialized; call StpUtil::try_init_manager() first")]
247    NotInitialized,
248
249    /// 全局 Manager 重复初始化
250    /// Global manager was already initialized
251    #[error("Sa-Token manager is already initialized")]
252    AlreadyInitialized,
253
254    // ============ System Errors | 系统错误 ============
255    /// Underlying storage failure | 底层存储失败
256    #[error("Storage error: {0}")]
257    StorageError(String),
258
259    /// Invalid configuration | 配置无效
260    #[error("Configuration error: {0}")]
261    ConfigError(String),
262
263    /// Serialization / deserialization failure | 序列化或反序列化失败
264    #[error("Serialization error: {0}")]
265    SerializationError(String),
266
267    /// Unexpected internal failure | 未预期的内部错误
268    #[error("Internal error: {0}")]
269    InternalError(String),
270}
271
272impl From<serde_json::Error> for SaTokenError {
273    fn from(value: serde_json::Error) -> Self {
274        Self::SerializationError(value.to_string())
275    }
276}
277
278impl From<sa_token_adapter::serializer::SerializerError> for SaTokenError {
279    fn from(value: sa_token_adapter::serializer::SerializerError) -> Self {
280        Self::SerializationError(value.to_string())
281    }
282}
283
284impl SaTokenError {
285    /// Get the error message as a string.
286    ///
287    /// Returns the English message from `#[error(...)]`.
288    /// 返回 `#[error(...)]` 定义的英文文案。
289    ///
290    /// # Examples
291    ///
292    /// ```rust,ignore
293    /// let err = SaTokenError::NotLogin;
294    /// assert_eq!(err.message(), "User not logged in");
295    /// ```
296    pub fn message(&self) -> String {
297        self.to_string()
298    }
299
300    /// Whether this is an authentication (login/token) error.
301    /// 是否为认证(登录/Token)类错误。
302    pub fn is_auth_error(&self) -> bool {
303        matches!(
304            self,
305            Self::NotLogin
306                | Self::TokenNotFound
307                | Self::TokenExpired
308                | Self::TokenInactive
309                | Self::InvalidToken(_)
310                | Self::AccountKickedOut
311                | Self::AccountReplaced
312        )
313    }
314
315    /// Whether this is an authorization (permission/role) error.
316    /// 是否为授权(权限/角色)类错误。
317    pub fn is_authz_error(&self) -> bool {
318        matches!(
319            self,
320            Self::PermissionDenied | Self::PermissionDeniedDetail(_) | Self::RoleDenied(_)
321        )
322    }
323}
324
325/// Application-level error messages | 应用层标准错误文案
326///
327/// Constants for app-specific errors that are not part of [`SaTokenError`].
328/// 供业务侧使用的标准短文案(非 [`SaTokenError`] 变体)。
329///
330/// # Examples
331///
332/// ```rust,ignore
333/// use sa_token_core::error::messages;
334///
335/// let err_msg = messages::INVALID_CREDENTIALS;
336/// return Err(ApiError::Unauthorized(err_msg.to_string()));
337/// ```
338pub mod messages {
339    /// Invalid username or password | 用户名或密码错误
340    pub const INVALID_CREDENTIALS: &str = "Invalid username or password";
341
342    /// Login failed | 登录失败
343    pub const LOGIN_FAILED: &str = "Login failed";
344
345    /// Authentication error | 认证错误
346    pub const AUTH_ERROR: &str = "Authentication error";
347
348    /// Permission required | 需要权限
349    pub const PERMISSION_REQUIRED: &str = "Permission required";
350
351    /// Role required | 需要角色
352    pub const ROLE_REQUIRED: &str = "Role required";
353
354    /// HTTP Basic authentication failed | HTTP Basic 认证失败
355    pub const BASIC_AUTH_FAILED: &str = "HTTP Basic authentication failed";
356}