sa_token_core/
http_basic.rs1use crate::context::SaTokenContext;
7use crate::error::{SaTokenError, SaTokenResult};
8use crate::util::StpUtil;
9
10pub const DEFAULT_REALM: &str = "sa-token";
13
14pub fn ct_eq(a: &[u8], b: &[u8]) -> bool {
17 if a.len() != b.len() {
18 return false;
19 }
20 let mut diff = 0u8;
21 for (x, y) in a.iter().zip(b.iter()) {
22 diff |= x ^ y;
23 }
24 diff == 0
25}
26
27pub fn decode_basic_authorization(header: &str) -> Option<String> {
30 let rest = header
31 .strip_prefix("Basic ")
32 .or_else(|| header.strip_prefix("basic "))?;
33 let rest = rest.trim();
34 if rest.is_empty() {
35 return None;
36 }
37 use base64::Engine;
38 let bytes = base64::engine::general_purpose::STANDARD
39 .decode(rest.as_bytes())
40 .ok()?;
41 String::from_utf8(bytes).ok()
42}
43
44pub fn check(realm: &str, account: &str) -> SaTokenResult<()> {
49 let expected = if account.is_empty() {
50 StpUtil::try_get_config()
51 .map(|c| c.http_basic.clone())
52 .unwrap_or_default()
53 } else {
54 account.to_string()
55 };
56 if expected.is_empty() {
57 return Err(SaTokenError::BasicAuthFailed {
58 realm: realm.to_string(),
59 });
60 }
61
62 let header = SaTokenContext::try_current()
63 .and_then(|ctx| ctx.auth_meta().authorization)
64 .ok_or_else(|| SaTokenError::BasicAuthFailed {
65 realm: realm.to_string(),
66 })?;
67
68 let decoded =
69 decode_basic_authorization(&header).ok_or_else(|| SaTokenError::BasicAuthFailed {
70 realm: realm.to_string(),
71 })?;
72
73 if !ct_eq(decoded.as_bytes(), expected.as_bytes()) {
74 return Err(SaTokenError::BasicAuthFailed {
75 realm: realm.to_string(),
76 });
77 }
78 Ok(())
79}
80
81pub fn check_account(account: &str) -> SaTokenResult<()> {
84 check(DEFAULT_REALM, account)
85}