2 添加用户名、邮箱等是否已经存在接口
This commit is contained in:
parent
25a63b1fb7
commit
61de18077e
35
Cargo.lock
generated
35
Cargo.lock
generated
@ -125,6 +125,18 @@ version = "1.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
"cpufeatures",
|
||||
"password-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arraydeque"
|
||||
version = "0.5.1"
|
||||
@ -602,6 +614,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@ -1907,6 +1928,17 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
@ -2417,7 +2449,10 @@ name = "rtsa_db"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
"lazy_static",
|
||||
"md-5",
|
||||
"rand_core",
|
||||
"regex",
|
||||
"rtsa_dto",
|
||||
"rtsa_log",
|
||||
|
@ -19,6 +19,9 @@ sqlx = { workspace = true, features = [
|
||||
thiserror = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
argon2 = "0.5.3"
|
||||
rand_core = { version = "0.6.4", features = ["std"] }
|
||||
md-5 = "0.10.6"
|
||||
|
||||
rtsa_dto = { path = "../rtsa_dto" }
|
||||
rtsa_log = { path = "../rtsa_log" }
|
||||
|
@ -3,7 +3,6 @@ use sqlx::Postgres;
|
||||
|
||||
use super::RtsaDbAccessor;
|
||||
use super::{OrgAccessor, RegisterUser, UserAccessor};
|
||||
use crate::password_util::verify_password;
|
||||
use crate::{
|
||||
common::{PageData, PageQuery, TableColumn},
|
||||
model::{OrganizationUserColumn, OrganizationUserModel, UserModel},
|
||||
@ -48,14 +47,6 @@ pub trait OrgUserAccessor {
|
||||
info: Value,
|
||||
updater_id: i32,
|
||||
) -> Result<OrganizationUserModel, DbAccessError>;
|
||||
/// 查询组织用户登陆
|
||||
/// username: 学工号/用户名/邮箱/手机号
|
||||
async fn query_org_user_login(
|
||||
&self,
|
||||
org_id: i32,
|
||||
username: &str,
|
||||
password: &str,
|
||||
) -> Result<(OrganizationUserModel, UserModel), DbAccessError>;
|
||||
/// 获取组织用户
|
||||
async fn query_org_user(
|
||||
&self,
|
||||
@ -383,35 +374,6 @@ impl OrgUserAccessor for RtsaDbAccessor {
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
async fn query_org_user_login(
|
||||
&self,
|
||||
org_id: i32,
|
||||
username: &str,
|
||||
password: &str,
|
||||
) -> Result<(OrganizationUserModel, UserModel), DbAccessError> {
|
||||
// 查询用户登陆
|
||||
let user = self.query_user_login(username, password).await;
|
||||
match user {
|
||||
Err(_) => {
|
||||
// 用户不存在,查询组织用户学工号+用户密码
|
||||
// 通过组织id和学工号查询组织用户
|
||||
let org_user = self.query_org_user_by_student_id(org_id, username).await?;
|
||||
let user = self.query_user(org_user.user_id).await?;
|
||||
// 检查用户密码
|
||||
if verify_password(password, &user.password) {
|
||||
Ok((org_user, user))
|
||||
} else {
|
||||
Err(DbAccessError::InvalidArgument("密码不匹配".to_string()))
|
||||
}
|
||||
}
|
||||
Ok(user) => {
|
||||
// 用户存在,查询组织用户
|
||||
let org_user = self.query_org_user(org_id, user.id).await?;
|
||||
Ok((org_user, user))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn query_org_user(
|
||||
&self,
|
||||
org_id: i32,
|
||||
@ -594,43 +556,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_query_org_user_login(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
let accessor = RtsaDbAccessor::new(pool);
|
||||
let (creator, org) = init_default_org_and_user(&accessor).await?;
|
||||
let create = CreateOrgUser::new(
|
||||
&org.code.clone().unwrap(),
|
||||
"test_student_id",
|
||||
"test_name",
|
||||
"test_password",
|
||||
creator.id,
|
||||
);
|
||||
accessor.create_org_user(create.clone()).await?;
|
||||
// Attempt to log in with student ID
|
||||
let (org_user, user) = accessor
|
||||
.query_org_user_login(org.id, "test_student_id", "test_password")
|
||||
.await?;
|
||||
assert_eq!(org_user.organization_id, org.id);
|
||||
assert_eq!(user.nickname, create.nickname());
|
||||
// Attempt to log in with username
|
||||
let (org_user, user) = accessor
|
||||
.query_org_user_login(org.id, &create.build_username(), "test_password")
|
||||
.await?;
|
||||
assert_eq!(org_user.organization_id, org.id);
|
||||
assert_eq!(user.nickname, create.nickname());
|
||||
// Attempt to log in with wrong password
|
||||
let result = accessor
|
||||
.query_org_user_login(org.id, "test_student_id", "wrong_password")
|
||||
.await;
|
||||
assert!(matches!(result, Err(DbAccessError::InvalidArgument(_))));
|
||||
// Attempt to log in with wrong username
|
||||
let result = accessor
|
||||
.query_org_user_login(org.id, "wrong_student_id", "test_password")
|
||||
.await;
|
||||
assert!(matches!(result, Err(DbAccessError::SqlxError(_))));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_query_org_user_by_student_id(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
let accessor = RtsaDbAccessor::new(pool);
|
||||
|
@ -4,7 +4,7 @@ use sqlx::Postgres;
|
||||
use crate::{
|
||||
common::{PageData, PageQuery, TableColumn},
|
||||
model::{UserColumn, UserModel},
|
||||
password_util::verify_password,
|
||||
password_util::{self, verify_password},
|
||||
username_util::{is_email, is_mobile},
|
||||
DbAccessError,
|
||||
};
|
||||
@ -48,6 +48,10 @@ pub trait UserAccessor {
|
||||
async fn query_user(&self, id: i32) -> Result<UserModel, DbAccessError>;
|
||||
/// 根据username查询用户数据
|
||||
async fn query_user_by_username(&self, username: &str) -> Result<UserModel, DbAccessError>;
|
||||
/// 根据email查询用户数据
|
||||
async fn query_user_by_email(&self, email: &str) -> Result<UserModel, DbAccessError>;
|
||||
/// 根据mobile查询用户数据
|
||||
async fn query_user_by_mobile(&self, mobile: &str) -> Result<UserModel, DbAccessError>;
|
||||
/// 是否用户名已经存在
|
||||
async fn is_user_name_exist(&self, name: &str) -> Result<bool, DbAccessError>;
|
||||
/// 是否用户email已经存在
|
||||
@ -296,7 +300,8 @@ impl UserAccessor for RtsaDbAccessor {
|
||||
Ok(PageData::new(total, rows))
|
||||
}
|
||||
|
||||
async fn register_user(&self, user: RegisterUser) -> Result<UserModel, DbAccessError> {
|
||||
async fn register_user(&self, mut user: RegisterUser) -> Result<UserModel, DbAccessError> {
|
||||
user.password = password_util::hash_password(&user.password)?;
|
||||
self.insert_user(user, &self.pool).await
|
||||
}
|
||||
|
||||
@ -305,88 +310,34 @@ impl UserAccessor for RtsaDbAccessor {
|
||||
username: &str,
|
||||
password: &str,
|
||||
) -> Result<UserModel, DbAccessError> {
|
||||
let table = UserColumn::Table.name();
|
||||
let username_column = UserColumn::Username.name();
|
||||
let email_column = UserColumn::Email.name();
|
||||
let mobile_column = UserColumn::Mobile.name();
|
||||
let user: UserModel;
|
||||
if is_email(username) {
|
||||
let query_clause = format!(
|
||||
"SELECT * FROM {table} WHERE {email_column} = $1 LIMIT 1",
|
||||
table = table,
|
||||
email_column = email_column,
|
||||
);
|
||||
let user: Result<UserModel, sqlx::Error> = sqlx::query_as(&query_clause)
|
||||
.bind(username)
|
||||
.fetch_one(&self.pool)
|
||||
.await;
|
||||
match user {
|
||||
Ok(user) => {
|
||||
if verify_password(password, &user.password) {
|
||||
Ok(user)
|
||||
} else {
|
||||
Err(DbAccessError::InvalidArgument("密码不匹配".to_string()))
|
||||
}
|
||||
}
|
||||
Err(sqlx::Error::RowNotFound) => Err(DbAccessError::InvalidArgument(
|
||||
"用户不存在(email)".to_string(),
|
||||
)),
|
||||
Err(e) => Err(DbAccessError::SqlxError(e)),
|
||||
let query_result = self.query_user_by_email(username).await;
|
||||
if query_result.is_ok() {
|
||||
user = query_result.unwrap();
|
||||
} else {
|
||||
return Err(DbAccessError::UserNotExist("email".to_string()));
|
||||
}
|
||||
} else if is_mobile(username) {
|
||||
let query_clause = format!(
|
||||
"SELECT * FROM {table} WHERE {mobile_column} = $1 LIMIT 1",
|
||||
table = table,
|
||||
mobile_column = mobile_column,
|
||||
);
|
||||
let user: Result<UserModel, sqlx::Error> = sqlx::query_as(&query_clause)
|
||||
.bind(username)
|
||||
.fetch_one(&self.pool)
|
||||
.await;
|
||||
match user {
|
||||
Ok(user) => {
|
||||
if verify_password(password, &user.password) {
|
||||
return Ok(user);
|
||||
} else {
|
||||
return Err(DbAccessError::InvalidArgument("密码不匹配".to_string()));
|
||||
}
|
||||
}
|
||||
Err(sqlx::Error::RowNotFound) => {
|
||||
return Err(DbAccessError::InvalidArgument(
|
||||
"用户不存在(mobile)".to_string(),
|
||||
));
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(DbAccessError::SqlxError(e));
|
||||
}
|
||||
let query_result = self.query_user_by_mobile(username).await;
|
||||
if query_result.is_ok() {
|
||||
user = query_result.unwrap();
|
||||
} else {
|
||||
return Err(DbAccessError::UserNotExist("mobile".to_string()));
|
||||
}
|
||||
} else {
|
||||
let query_clause = format!(
|
||||
"SELECT * FROM {table} WHERE {username_column} = $1 LIMIT 1",
|
||||
table = table,
|
||||
username_column = username_column,
|
||||
);
|
||||
let user: Result<UserModel, sqlx::Error> = sqlx::query_as(&query_clause)
|
||||
.bind(username)
|
||||
.fetch_one(&self.pool)
|
||||
.await;
|
||||
match user {
|
||||
Ok(user) => {
|
||||
if verify_password(password, &user.password) {
|
||||
return Ok(user);
|
||||
} else {
|
||||
return Err(DbAccessError::InvalidArgument("密码不匹配".to_string()));
|
||||
}
|
||||
}
|
||||
Err(sqlx::Error::RowNotFound) => {
|
||||
return Err(DbAccessError::InvalidArgument(
|
||||
"用户不存在(username)".to_string(),
|
||||
));
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(DbAccessError::SqlxError(e));
|
||||
}
|
||||
let query_result = self.query_user_by_username(username).await;
|
||||
if query_result.is_ok() {
|
||||
user = query_result.unwrap();
|
||||
} else {
|
||||
return Err(DbAccessError::UserNotExist("username".to_string()));
|
||||
}
|
||||
}
|
||||
if verify_password(password, &user.password).is_ok() {
|
||||
Ok(user)
|
||||
} else {
|
||||
Err(DbAccessError::PasswordNotMatch)
|
||||
}
|
||||
}
|
||||
|
||||
async fn query_user(&self, id: i32) -> Result<UserModel, DbAccessError> {
|
||||
@ -415,6 +366,36 @@ impl UserAccessor for RtsaDbAccessor {
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
async fn query_user_by_email(&self, email: &str) -> Result<UserModel, DbAccessError> {
|
||||
let table = UserColumn::Table.name();
|
||||
let email_column = UserColumn::Email.name();
|
||||
let query_clause = format!(
|
||||
"SELECT * FROM {table} WHERE {email_column} = $1 LIMIT 1",
|
||||
table = table,
|
||||
email_column = email_column,
|
||||
);
|
||||
let user = sqlx::query_as(&query_clause)
|
||||
.bind(email)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
async fn query_user_by_mobile(&self, mobile: &str) -> Result<UserModel, DbAccessError> {
|
||||
let table = UserColumn::Table.name();
|
||||
let mobile_column = UserColumn::Mobile.name();
|
||||
let query_clause = format!(
|
||||
"SELECT * FROM {table} WHERE {mobile_column} = $1 LIMIT 1",
|
||||
table = table,
|
||||
mobile_column = mobile_column,
|
||||
);
|
||||
let user = sqlx::query_as(&query_clause)
|
||||
.bind(mobile)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
async fn update_user_info(&self, id: i32, info: Value) -> Result<UserModel, DbAccessError> {
|
||||
let table = UserColumn::Table.name();
|
||||
let id_column = UserColumn::Id.name();
|
||||
@ -451,15 +432,16 @@ impl UserAccessor for RtsaDbAccessor {
|
||||
"new_password is too long".to_string(),
|
||||
));
|
||||
}
|
||||
let hashed = password_util::hash_password(new_password)?;
|
||||
let table = UserColumn::Table.name();
|
||||
let id_column = UserColumn::Id.name();
|
||||
let password_column = UserColumn::Password.name();
|
||||
let updated_at_column = UserColumn::UpdatedAt.name();
|
||||
let update_clause = format!(
|
||||
"UPDATE {table} SET {password_column} = '{new_password}', {updated_at_column} = 'now()' WHERE {id_column} = {id} RETURNING *",
|
||||
"UPDATE {table} SET {password_column} = '{hashed}', {updated_at_column} = 'now()' WHERE {id_column} = {id} RETURNING *",
|
||||
table = table,
|
||||
password_column = password_column,
|
||||
new_password = new_password,
|
||||
hashed = hashed,
|
||||
id_column = id_column,
|
||||
id = id
|
||||
);
|
||||
@ -609,12 +591,14 @@ impl UserAccessor for RtsaDbAccessor {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rtsa_dto::common::Role;
|
||||
use rtsa_log::tracing::info;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_register_user(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
rtsa_log::Logging::default().init();
|
||||
let accessor = RtsaDbAccessor { pool };
|
||||
|
||||
let new_user = RegisterUser::new("test_user", "test_password")
|
||||
@ -627,6 +611,7 @@ mod tests {
|
||||
let queried_user = accessor
|
||||
.query_user_login("test_user@example.com", "test_password")
|
||||
.await?;
|
||||
info!("queried_user: {:?}", queried_user);
|
||||
assert_eq!(queried_user.username, "test_user");
|
||||
assert_eq!(
|
||||
queried_user.email,
|
||||
@ -781,6 +766,42 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_query_user_by_email(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
let accessor = RtsaDbAccessor { pool };
|
||||
|
||||
// 准备测试数据
|
||||
let new_user = RegisterUser::new("test_user", "test_password").with_email("test@test.cn");
|
||||
let um = accessor.register_user(new_user).await?;
|
||||
|
||||
// Assume a user with email exists
|
||||
let user = accessor.query_user_by_email("test@test.cn").await?;
|
||||
assert_eq!(user.id, um.id);
|
||||
|
||||
// Assume a user with email does not exist
|
||||
let result = accessor.query_user_by_email("dev@test.cn").await;
|
||||
assert!(result.is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_query_user_by_mobile(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
let accessor = RtsaDbAccessor { pool };
|
||||
|
||||
// 准备测试数据
|
||||
let new_user = RegisterUser::new("test_user", "test_password").with_mobile("13345678901");
|
||||
let um = accessor.register_user(new_user).await?;
|
||||
|
||||
// Assume a user with mobile exists
|
||||
let user = accessor.query_user_by_mobile("13345678901").await?;
|
||||
assert_eq!(user.id, um.id);
|
||||
|
||||
// Assume a user with mobile does not exist
|
||||
let result = accessor.query_user_by_mobile("13345678902").await;
|
||||
assert!(result.is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_query_user_login(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
let accessor = RtsaDbAccessor { pool };
|
||||
@ -809,7 +830,7 @@ mod tests {
|
||||
|
||||
// Test with incorrect credentials
|
||||
let result = accessor
|
||||
.query_user_login("test_user@example.com", "wrongpassword")
|
||||
.query_user_login("test_user@example.com", "wrong_password")
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
Ok(())
|
||||
|
@ -14,4 +14,8 @@ pub enum DbAccessError {
|
||||
DataError(String),
|
||||
#[error("非法参数:{0}")]
|
||||
InvalidArgument(String),
|
||||
#[error("用户不存在:{0}")]
|
||||
UserNotExist(String),
|
||||
#[error("密码不正确")]
|
||||
PasswordNotMatch,
|
||||
}
|
||||
|
@ -1,4 +1,69 @@
|
||||
use argon2::{
|
||||
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
|
||||
Argon2,
|
||||
};
|
||||
use rand_core::OsRng;
|
||||
|
||||
use md5::{Digest, Md5};
|
||||
|
||||
use crate::DbAccessError;
|
||||
|
||||
/// 验证密码是否正确
|
||||
pub fn verify_password(password: &str, hash: &str) -> bool {
|
||||
password == hash
|
||||
pub fn verify_password(password: &str, hash: &str) -> Result<(), DbAccessError> {
|
||||
// Verify password against PHC string.
|
||||
// NOTE: hash params from `parsed_hash` are used instead of what is configured in the
|
||||
// `Argon2` instance.
|
||||
let parsed_hash = PasswordHash::new(hash);
|
||||
match parsed_hash {
|
||||
Ok(parsed_hash) => Argon2::default()
|
||||
.verify_password(password.as_bytes(), &parsed_hash)
|
||||
.map_err(|e| DbAccessError::InvalidArgument(format!("password verify error: {}", e))),
|
||||
Err(e) => Err(DbAccessError::InvalidArgument(format!(
|
||||
"password hash error: {}",
|
||||
e
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash_password(password: &str) -> Result<String, DbAccessError> {
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
|
||||
// Argon2 with default params (Argon2id v19)
|
||||
let argon2 = Argon2::default();
|
||||
|
||||
// Hash password to PHC string ($argon2id$v=19$...)
|
||||
let password_hash = argon2.hash_password(password.as_bytes(), &salt);
|
||||
match password_hash {
|
||||
Ok(ph) => Ok(ph.to_string()),
|
||||
Err(e) => Err(DbAccessError::InvalidArgument(format!(
|
||||
"password hash error: {}",
|
||||
e
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn md5(input: &str) -> String {
|
||||
let mut hasher = Md5::new();
|
||||
hasher.update(input);
|
||||
let result = hasher.finalize();
|
||||
format!("{:x}", result)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_hash_password() {
|
||||
let password = "password";
|
||||
let hash = hash_password(password).unwrap();
|
||||
assert!(verify_password(password, &hash).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_md5() {
|
||||
let input = "hello";
|
||||
let output = md5(input);
|
||||
assert_eq!(output, "5d41402abc4b2a76b9719d911017c592");
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,35 @@ impl UserQuery {
|
||||
Ok(user.into())
|
||||
}
|
||||
|
||||
/// 用户名是否存在
|
||||
async fn username_exists(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
username: String,
|
||||
) -> async_graphql::Result<bool> {
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let exist = db_accessor.is_user_name_exist(&username).await?;
|
||||
Ok(exist)
|
||||
}
|
||||
|
||||
/// 邮箱是否存在
|
||||
async fn email_exists(&self, ctx: &Context<'_>, email: String) -> async_graphql::Result<bool> {
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let exist = db_accessor.is_user_email_exist(&email).await?;
|
||||
Ok(exist)
|
||||
}
|
||||
|
||||
/// 手机号是否存在
|
||||
async fn mobile_exists(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
mobile: String,
|
||||
) -> async_graphql::Result<bool> {
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let exist = db_accessor.is_user_mobile_exist(&mobile).await?;
|
||||
Ok(exist)
|
||||
}
|
||||
|
||||
/// 分页查询用户(系统管理)
|
||||
#[graphql(guard = "RoleGuard::new(Role::Admin)")]
|
||||
async fn user_paging(
|
||||
|
@ -6,7 +6,7 @@ use rtsa_log::tracing::{debug, error, info};
|
||||
use serde_json::json;
|
||||
|
||||
pub const ADMIN_USER_NAME: &str = "_jl_admin_";
|
||||
pub const ADMIN_USER_PASSWORD: &str = "joylink0503";
|
||||
pub const ADMIN_USER_PASSWORD: &str = "4a6d74126bfd06d69406fcccb7e7d5d9";
|
||||
|
||||
pub const DEFAULT_ORG_CODE: &str = "default";
|
||||
const DEFAULT_ORG_NAME: &str = "默认机构";
|
||||
@ -64,7 +64,6 @@ mod tests {
|
||||
let db_accessor = rtsa_db::get_default_db_accessor();
|
||||
let user = db_accessor.query_user_by_username(ADMIN_USER_NAME).await?;
|
||||
assert_eq!(user.username, ADMIN_USER_NAME);
|
||||
assert_eq!(user.password, ADMIN_USER_PASSWORD);
|
||||
assert_eq!(user.nickname, ADMIN_USER_NAME);
|
||||
assert_eq!(user.roles, json!([Role::Admin]));
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use async_graphql::Guard;
|
||||
use rtsa_db::prelude::*;
|
||||
use rtsa_dto::common::Role;
|
||||
use rtsa_log::tracing::warn;
|
||||
use rtsa_log::tracing::{info, warn};
|
||||
|
||||
mod jwt_auth;
|
||||
use crate::{apis::UserLoginDto, error::BusinessError, sys_init::DEFAULT_ORG_CODE};
|
||||
@ -53,10 +53,10 @@ impl Guard for RoleGuard {
|
||||
/// 处理用户登录
|
||||
pub(crate) async fn handle_login(
|
||||
db_accessor: &RtsaDbAccessor,
|
||||
info: UserLoginDto,
|
||||
user_login_dto: UserLoginDto,
|
||||
) -> Result<Jwt, BusinessError> {
|
||||
let org_id;
|
||||
if let Some(oid) = info.org_id {
|
||||
if let Some(oid) = user_login_dto.org_id {
|
||||
org_id = oid;
|
||||
} else {
|
||||
let default_org = db_accessor.query_org_by_code(DEFAULT_ORG_CODE).await?;
|
||||
@ -64,35 +64,57 @@ pub(crate) async fn handle_login(
|
||||
}
|
||||
// 查询用户登陆
|
||||
let user = db_accessor
|
||||
.query_user_login(&info.username, &info.password)
|
||||
.query_user_login(&user_login_dto.username, &user_login_dto.password)
|
||||
.await;
|
||||
match user {
|
||||
Ok(user) => {
|
||||
// 用户存在
|
||||
Ok(Jwt::build_from(Claims::new(user.id, org_id))?)
|
||||
}
|
||||
Err(_) => {
|
||||
// 用户不存在,查询组织用户学工号+用户密码
|
||||
// 通过组织id和学工号查询组织用户
|
||||
let org_user = db_accessor
|
||||
.query_org_user_by_student_id(org_id, &info.username)
|
||||
.await;
|
||||
if org_user.is_err() {
|
||||
warn!("用户不存在: username={}, org_id={}", info.username, org_id);
|
||||
return Err(BusinessError::AuthError(
|
||||
Err(e) => {
|
||||
match e {
|
||||
DbAccessError::UserNotExist(e) => {
|
||||
info!(
|
||||
"用户不存在: {}, 尝试查询组织学工号用户: username={}, org_id={}",
|
||||
e, user_login_dto.username, org_id
|
||||
);
|
||||
// 用户不存在,查询组织用户学工号+用户密码
|
||||
// 通过组织id和学工号查询组织用户
|
||||
let org_user = db_accessor
|
||||
.query_org_user_by_student_id(org_id, &user_login_dto.username)
|
||||
.await;
|
||||
if org_user.is_err() {
|
||||
warn!(
|
||||
"用户不存在: username={}, org_id={}",
|
||||
user_login_dto.username, org_id
|
||||
);
|
||||
return Err(BusinessError::AuthError(
|
||||
"用户不存在或密码不正确".to_string(),
|
||||
));
|
||||
}
|
||||
let org_user = org_user.unwrap();
|
||||
let user = db_accessor.query_user(org_user.user_id).await?;
|
||||
// 检查用户密码
|
||||
if rtsa_db::password_util::verify_password(
|
||||
&user_login_dto.password,
|
||||
&user.password,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
Ok(Jwt::build_from(Claims::new(user.id, org_id))?)
|
||||
} else {
|
||||
warn!(
|
||||
"密码不匹配: username={}, org_id={}",
|
||||
user_login_dto.username, org_id
|
||||
);
|
||||
Err(BusinessError::AuthError(
|
||||
"用户不存在或密码不正确".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(BusinessError::AuthError(
|
||||
"用户不存在或密码不正确".to_string(),
|
||||
));
|
||||
}
|
||||
let org_user = org_user.unwrap();
|
||||
let user = db_accessor.query_user(org_user.user_id).await?;
|
||||
// 检查用户密码
|
||||
if rtsa_db::password_util::verify_password(&info.password, &user.password) {
|
||||
Ok(Jwt::build_from(Claims::new(user.id, org_id))?)
|
||||
} else {
|
||||
warn!("密码不匹配: username={}, org_id={}", info.username, org_id);
|
||||
Err(BusinessError::AuthError(
|
||||
"用户不存在或密码不正确".to_string(),
|
||||
))
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user