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}