pic*_*ser 2 authentication session rust actix-web
我正在开发一个内部 API,经过批准的用户可以使用该 API 读取数据库并将其插入数据库。我的目的是让这个程序在我们的本地网络上运行,并且让多个用户或应用程序能够访问它。
\n在当前状态下,只要用户运行客户端的本地实例并在其下完成所有工作,它就会起作用localhost. 但是,当同一用户尝试从其 IP 地址登录时,会话不会被存储,并且无法访问任何内容。尝试从网络上的另一台计算机连接到运行客户端的计算机时,结果是相同的。
在发表评论或回答之前,请注意,这是我第一次实现身份验证。我的任何错误或严重错误都只是出于无知。
\n我的Cargo.toml文件包含以下依赖项:
actix-session = { version = "0.7.1", features = ["cookie-session"] }\nactix-web = "^4"\nargon2 = "0.4.1"\nrand_core = "0.6.3"\nreqwest = "0.11.11"\nserde = { version = "1.0.144", features = ["derive"] }\nserde_json = "1.0.85"\nsqlx = { version = "0.6.1", features = ["runtime-actix-rustls", "mysql", "macros"] }\nRun Code Online (Sandbox Code Playgroud)\n以下是 的内容main.rs:
use actix_session::storage::CookieSessionStore;\nuse actix_session::SessionMiddleware;\nuse actix_web::cookie::Key;\nuse actix_web::web::{get, post, Data, Path};\nuse actix_web::{HttpResponse, Responder};\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n let secret_key = Key::generate();\n\n // Load or create a new config file.\n // let settings = ...\n\n // Create a connection to the database.\n let pool = sqlx::mysql::MySqlPoolOptions::new()\n .connect(&format!(\n "mysql://{}:{}@{}:{}/mydb",\n env!("DB_USER"),\n env!("DB_PASS"),\n env!("DB_HOST"),\n env!("DB_PORT"),\n ))\n .await\n .unwrap();\n\n println!(\n "Application listening on {}:{}",\n settings.host,\n settings.port,\n );\n\n // Instantiate the application and add routes for each handler.\n actix_web::HttpServer::new(move || {\n let logger = actix_web::middleware::Logger::default();\n actix_web::App::new()\n .wrap(SessionMiddleware::new(\n CookieSessionStore::default(),\n secret_key.clone(),\n ))\n .wrap(logger)\n .app_data(Data::new(pool.clone()))\n /*\n Routes that return all rows from a database table.\n */\n /*\n Routes that return a webpage.\n */\n .route("/new", get().to(new))\n .route("/login", get().to(login))\n .route("/register", get().to(register))\n /*\n Routes that deal with authentication.\n */\n .route("/register", post().to(register_user))\n .route("/login", post().to(login_user))\n .route("/logout", get().to(logout_user))\n /*\n Routes that handle POST requests.\n */\n })\n .bind(format!("{}:{}", settings.host, settings.port))?\n .run()\n .await\n}\nRun Code Online (Sandbox Code Playgroud)\n涉及认证的代码如下:
\nuse crate::model::User;\nuse actix_session::Session;\nuse actix_web::web::{Data, Form};\nuse actix_web::{error::ErrorUnauthorized, HttpResponse};\nuse argon2::password_hash::{rand_core::OsRng, PasswordHasher, SaltString};\nuse argon2::{Argon2, PasswordHash, PasswordVerifier};\nuse sqlx::{MySql, Pool};\n\n#[derive(serde::Serialize)]\npub struct SessionDetails {\n user_id: u32,\n}\n\n#[derive(Debug, sqlx::FromRow)]\npub struct AuthorizedUser {\n pub id: u32,\n pub username: String,\n pub password_hash: String,\n pub approved: bool,\n}\n\npub fn check_auth(session: &Session) -> Result<u32, actix_web::Error> {\n match session.get::<u32>("user_id").unwrap() {\n Some(user_id) => Ok(user_id),\n None => Err(ErrorUnauthorized("User not logged in.")),\n }\n}\n\npub async fn register_user(\n data: Form<User>,\n pool: Data<Pool<MySql>>,\n) -> Result<String, Box<dyn std::error::Error>> {\n let data = data.into_inner();\n let salt = SaltString::generate(&mut OsRng);\n\n let argon2 = Argon2::default();\n\n let password_hash = argon2\n .hash_password(data.password.as_bytes(), &salt)\n .unwrap()\n .to_string();\n\n // Use to verify.\n // let parsed_hash = PasswordHash::new(&hash).unwrap();\n\n const INSERT_QUERY: &str =\n "INSERT INTO users (username, password_hash) VALUES (?, ?) RETURNING id;";\n\n let fetch_one: Result<(u32,), sqlx::Error> = sqlx::query_as(INSERT_QUERY)\n .bind(data.username)\n .bind(password_hash)\n .fetch_one(&mut pool.acquire().await.unwrap())\n .await;\n\n match fetch_one {\n Ok((user_id,)) => Ok(user_id.to_string()),\n Err(err) => Err(Box::new(err)),\n }\n}\n\npub async fn login_user(\n session: Session,\n data: Form<User>,\n pool: Data<Pool<MySql>>,\n) -> Result<HttpResponse, Box<dyn std::error::Error>> {\n let data = data.into_inner();\n let fetched_user: AuthorizedUser = match sqlx::query_as(\n "SELECT id, username, password_hash, approved FROM users WHERE username = ?;",\n )\n .bind(data.username)\n .fetch_one(&mut pool.acquire().await?)\n .await\n {\n Ok(fetched_user) => fetched_user,\n Err(e) => return Ok(HttpResponse::NotFound().body(format!("{e:?}"))),\n };\n\n let parsed_hash = PasswordHash::new(&fetched_user.password_hash).unwrap();\n\n match Argon2::default().verify_password(&data.password.as_bytes(), &parsed_hash) {\n Ok(_) => {\n if !fetched_user.approved {\n return Ok(\n HttpResponse::Unauthorized().body("This account has not yet been approved.")\n );\n }\n\n session.insert("user_id", &fetched_user.id)?;\n session.renew();\n\n Ok(HttpResponse::Ok().json(SessionDetails {\n user_id: fetched_user.id,\n }))\n }\n Err(_) => Ok(HttpResponse::Unauthorized().body("Incorrect password.")),\n }\n}\n\npub async fn logout_user(session: Session) -> HttpResponse {\n if check_auth(&session).is_err() {\n return HttpResponse::NotFound().body("No user logged in.");\n }\n\n session.purge();\n HttpResponse::SeeOther()\n .append_header(("Location", "/login"))\n .body(format!("User logged out successfully."))\n}\nRun Code Online (Sandbox Code Playgroud)\n我已经将我的客户端设置为0.0.0.0在 port 上与主机一起运行80,但是凭借我所拥有的一点网络知识,这是我能想到的最好的做法 \xe2\x80\x94 我在这里迷失了。任何帮助将不胜感激。
事实证明,cookie 没有被传输,因为我们的本地网络没有使用https.
改变这个...
.wrap(SessionMiddleware::new(
CookieSessionStore::default(),
secret_key.clone(),
))
Run Code Online (Sandbox Code Playgroud)
到以下...
.wrap(
SessionMiddleware::builder(CookieSessionStore::default(), secret_key.clone())
.cookie_secure(false)
.build(),
)
Run Code Online (Sandbox Code Playgroud)
解决了这个问题。
| 归档时间: |
|
| 查看次数: |
1850 次 |
| 最近记录: |