DONE,处理重定向
This commit is contained in:
parent
2b7932dd49
commit
d119f958f6
@ -11,7 +11,6 @@ server {
|
|||||||
proxy_set_header X-Original-URI $scheme://$host$request_uri;
|
proxy_set_header X-Original-URI $scheme://$host$request_uri;
|
||||||
proxy_pass http://protected/;
|
proxy_pass http://protected/;
|
||||||
}
|
}
|
||||||
location
|
|
||||||
location = /aaron/auth {
|
location = /aaron/auth {
|
||||||
internal;
|
internal;
|
||||||
proxy_pass http://localhost:3000/auth;
|
proxy_pass http://localhost:3000/auth;
|
||||||
|
|||||||
@ -63,7 +63,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Login</h1>
|
<h1>Login</h1>
|
||||||
<form action="{{ home_url|safe }}/login{{ url }}" method="POST">
|
<form action="{{ home_url|safe }}/login" method="POST">
|
||||||
<label for="username">Username:</label>
|
<label for="username">Username:</label>
|
||||||
<input type="text" id="username" name="username" required>
|
<input type="text" id="username" name="username" required>
|
||||||
<label for="password">Password:</label>
|
<label for="password">Password:</label>
|
||||||
|
|||||||
@ -1,26 +1,26 @@
|
|||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::env;
|
use std::env;
|
||||||
pub const COOKIE_NAME: Lazy<String> =
|
pub static COOKIE_NAME: Lazy<String> =
|
||||||
Lazy::new(|| env::var("COOKIE_NAME").unwrap_or("aaron_auth".to_string()));
|
Lazy::new(|| env::var("COOKIE_NAME").unwrap_or("aaron_auth".to_string()));
|
||||||
pub const SESSION_ACTIVE_TIME: Lazy<u64> = Lazy::new(|| {
|
pub static SESSION_ACTIVE_TIME: Lazy<u64> = Lazy::new(|| {
|
||||||
env::var("SESSION_ACTIVE_TIME")
|
env::var("SESSION_ACTIVE_TIME")
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|value| value.parse().ok())
|
.and_then(|value| value.parse().ok())
|
||||||
.unwrap_or(600)
|
.unwrap_or(600)
|
||||||
});
|
});
|
||||||
pub const COOKIE_DOMAIN: Lazy<String> =
|
pub static COOKIE_DOMAIN: Lazy<String> =
|
||||||
Lazy::new(|| env::var("DOMAIN").ok().unwrap_or(".aaronhu.cn".to_owned()));
|
Lazy::new(|| env::var("DOMAIN").ok().unwrap_or(".aaronhu.cn".to_owned()));
|
||||||
pub const LOGIN_PAGE_HTML: &str = include_str!("../loginpage.html");
|
pub static LOGIN_PAGE_HTML: &str = include_str!("../loginpage.html");
|
||||||
pub const REGISTER_PAGE_HTML: &str = include_str!("../register.html");
|
pub static REGISTER_PAGE_HTML: &str = include_str!("../register.html");
|
||||||
pub const PORT: Lazy<String> = Lazy::new(|| env::var("PORT").unwrap_or("3000".to_string()));
|
pub static PORT: Lazy<String> = Lazy::new(|| env::var("PORT").unwrap_or("3000".to_string()));
|
||||||
// 部署此应用的URL,默认为/aaron
|
// 部署此应用的URL,默认为/aaron
|
||||||
pub const HOME_URL: Lazy<String> =
|
pub static HOME_URL: Lazy<String> =
|
||||||
Lazy::new(|| env::var("HOME_URL").unwrap_or("/aaron".to_owned()));
|
Lazy::new(|| env::var("HOME_URL").unwrap_or("/aaron".to_owned()));
|
||||||
pub const DATABASE_URL: Lazy<String> =
|
pub static DATABASE_URL: Lazy<String> =
|
||||||
Lazy::new(|| env::var("DATABASE_URL").unwrap_or("".to_owned()));
|
Lazy::new(|| env::var("DATABASE_URL").unwrap_or("".to_owned()));
|
||||||
pub const RP_ID: Lazy<String> =
|
pub static RP_ID: Lazy<String> =
|
||||||
Lazy::new(|| env::var("RP_ID").unwrap_or("localhost".to_owned()));
|
Lazy::new(|| env::var("RP_ID").unwrap_or("localhost".to_owned()));
|
||||||
pub const RP_ORIGIN: Lazy<String> =
|
pub static RP_ORIGIN: Lazy<String> =
|
||||||
Lazy::new(|| env::var("RP_ORIGIN").unwrap_or("http://localhost:8080".to_owned()));
|
Lazy::new(|| env::var("RP_ORIGIN").unwrap_or("http://localhost:8080".to_owned()));
|
||||||
pub const RP_NAME: Lazy<String> =
|
pub static RP_NAME: Lazy<String> =
|
||||||
Lazy::new(|| env::var("RP_NAME").unwrap_or("Localhost".to_owned()));
|
Lazy::new(|| env::var("RP_NAME").unwrap_or("Localhost".to_owned()));
|
||||||
|
|||||||
@ -20,11 +20,11 @@ pub struct ServerState {
|
|||||||
impl ServerState {
|
impl ServerState {
|
||||||
pub async fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
// Effective domain name.
|
// Effective domain name.
|
||||||
let rp_id = RP_ID;
|
let rp_id = &RP_ID;
|
||||||
// Url containing the effective domain name
|
// Url containing the effective domain name
|
||||||
// MUST include the port number!
|
// MUST include the port number!
|
||||||
let rp_origin = Url::parse(&RP_ORIGIN).expect("Invalid URL");
|
let rp_origin = Url::parse(&RP_ORIGIN).expect("Invalid URL");
|
||||||
let builder = WebauthnBuilder::new(&rp_id, &rp_origin).expect("Invalid configuration");
|
let builder = WebauthnBuilder::new(rp_id, &rp_origin).expect("Invalid configuration");
|
||||||
|
|
||||||
// Now, with the builder you can define other options.
|
// Now, with the builder you can define other options.
|
||||||
// Set a "nice" relying party name. Has no security properties and
|
// Set a "nice" relying party name. Has no security properties and
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
use axum::{routing::{get, get_service, post, Route}, Router};
|
use axum::{routing::{get, post}, Router};
|
||||||
use entities::*;
|
use entities::*;
|
||||||
|
|
||||||
use services::{auth::{self, finish_authentication, start_authentication}, gc_task, login, login_page, register_page};
|
use services::{auth::{self, finish_authentication, start_authentication}, gc_task, login, login_page, register_page};
|
||||||
@ -10,7 +10,6 @@ use std::sync::Arc;
|
|||||||
use tower_cookies::CookieManagerLayer;
|
use tower_cookies::CookieManagerLayer;
|
||||||
use tower_http::trace::{self, TraceLayer};
|
use tower_http::trace::{self, TraceLayer};
|
||||||
use tower_sessions::{Expiry, MemoryStore, SessionManagerLayer};
|
use tower_sessions::{Expiry, MemoryStore, SessionManagerLayer};
|
||||||
use tower_http::services::{ServeDir, ServeFile};
|
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|||||||
@ -3,7 +3,6 @@ use std::collections::HashMap;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use axum::debug_handler;
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Json, Path},
|
extract::{Json, Path},
|
||||||
@ -63,12 +62,15 @@ use crate::ServerState;
|
|||||||
// TODO - Improve error handling and messages
|
// TODO - Improve error handling and messages
|
||||||
fn auth_user(cookie_content: String, session_table: &mut HashMap<Uuid, Instant>) -> bool {
|
fn auth_user(cookie_content: String, session_table: &mut HashMap<Uuid, Instant>) -> bool {
|
||||||
let Ok(uuid) = Uuid::parse_str(&cookie_content) else {
|
let Ok(uuid) = Uuid::parse_str(&cookie_content) else {
|
||||||
|
info!("此用户Session不在表中");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let Some(expire) = session_table.get(&uuid) else {
|
let Some(expire) = session_table.get(&uuid) else {
|
||||||
|
info!("此用户Session已过期");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if *expire <= Instant::now() {
|
if *expire <= Instant::now() {
|
||||||
|
info!("此用户Session已过期");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
session_table.insert(
|
session_table.insert(
|
||||||
@ -76,7 +78,7 @@ fn auth_user(cookie_content: String, session_table: &mut HashMap<Uuid, Instant>)
|
|||||||
Instant::now() + Duration::from_secs(*SESSION_ACTIVE_TIME),
|
Instant::now() + Duration::from_secs(*SESSION_ACTIVE_TIME),
|
||||||
);
|
);
|
||||||
tracing::info!("valid cookie {}", uuid);
|
tracing::info!("valid cookie {}", uuid);
|
||||||
return true;
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start_register(
|
pub async fn start_register(
|
||||||
@ -188,6 +190,7 @@ pub async fn finish_register(
|
|||||||
info!("Passkey 正常");
|
info!("Passkey 正常");
|
||||||
let uid = &user_id.to_string();
|
let uid = &user_id.to_string();
|
||||||
let username = &user_name.to_string();
|
let username = &user_name.to_string();
|
||||||
|
info!("检查用户是否存在");
|
||||||
// Check if the user_id already exists
|
// Check if the user_id already exists
|
||||||
let record = sqlx::query!("SELECT COUNT(KEY) AS count FROM users WHERE KEY = $1;", uid)
|
let record = sqlx::query!("SELECT COUNT(KEY) AS count FROM users WHERE KEY = $1;", uid)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
@ -226,13 +229,14 @@ pub async fn finish_register(
|
|||||||
.rows_affected()
|
.rows_affected()
|
||||||
!= 1
|
!= 1
|
||||||
{
|
{
|
||||||
|
error!("将用户凭据持久化时失败");
|
||||||
return Err(WebauthnError::AuthenticationFailure.to_string());
|
return Err(WebauthnError::AuthenticationFailure.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusCode::OK
|
StatusCode::OK
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("challenge_register -> {:?}", e);
|
error!("challenge_register -> {:?}", e);
|
||||||
StatusCode::BAD_REQUEST
|
StatusCode::BAD_REQUEST
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -277,7 +281,7 @@ pub async fn start_authentication(
|
|||||||
info!("Start Authentication");
|
info!("Start Authentication");
|
||||||
let pool = &state.db;
|
let pool = &state.db;
|
||||||
// Remove any previous authentication that may have occurred from the session.
|
// Remove any previous authentication that may have occurred from the session.
|
||||||
let _ = session.remove::<(Uuid, PasskeyAuthentication)>("auth_state");
|
let _ = session.remove::<(Uuid, PasskeyAuthentication)>("auth_state").await;
|
||||||
let user_id = sqlx::query!("SELECT KEY FROM users WHERE NAME = $1;", user_name)
|
let user_id = sqlx::query!("SELECT KEY FROM users WHERE NAME = $1;", user_name)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
@ -342,7 +346,7 @@ pub async fn finish_authentication(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.ok_or(WebauthnError::AuthenticationFailure.to_string())?;
|
.ok_or(WebauthnError::AuthenticationFailure.to_string())?;
|
||||||
|
|
||||||
let _ = session.remove::<(Uuid, PasskeyAuthentication)>("auth_state");
|
let _ = session.remove::<(Uuid, PasskeyAuthentication)>("auth_state").await;
|
||||||
|
|
||||||
let res = match state
|
let res = match state
|
||||||
.webauthn
|
.webauthn
|
||||||
|
|||||||
@ -62,7 +62,6 @@ pub async fn auth_otp(
|
|||||||
pub async fn login(
|
pub async fn login(
|
||||||
State(state): State<Arc<ServerState>>,
|
State(state): State<Arc<ServerState>>,
|
||||||
cookies: Cookies,
|
cookies: Cookies,
|
||||||
Query(params): Query<HashMap<String, String>>,
|
|
||||||
Form(frm): Form<UserLoginForm>,
|
Form(frm): Form<UserLoginForm>,
|
||||||
) -> Result<Redirect, (StatusCode, &'static str)> {
|
) -> Result<Redirect, (StatusCode, &'static str)> {
|
||||||
let conn = state.db.acquire().await;
|
let conn = state.db.acquire().await;
|
||||||
@ -88,14 +87,18 @@ pub async fn login(
|
|||||||
s,
|
s,
|
||||||
Instant::now() + Duration::from_secs(*SESSION_ACTIVE_TIME),
|
Instant::now() + Duration::from_secs(*SESSION_ACTIVE_TIME),
|
||||||
);
|
);
|
||||||
|
let original_uri = cookies.get("OriginalURL");
|
||||||
let mut new_cookie = Cookie::new(COOKIE_NAME.to_string(), s.to_string());
|
let mut new_cookie = Cookie::new(COOKIE_NAME.to_string(), s.to_string());
|
||||||
new_cookie.set_domain(COOKIE_DOMAIN.to_string());
|
new_cookie.set_domain(COOKIE_DOMAIN.to_string());
|
||||||
cookies.add(new_cookie);
|
cookies.add(new_cookie);
|
||||||
if let Some(original_uri) = params.get("original_url") {
|
// 从Cookie中恢复重定向信息
|
||||||
return Ok(Redirect::to(original_uri));
|
let res = match original_uri{
|
||||||
}
|
Some(redirect) => Ok(Redirect::to(redirect.value())),
|
||||||
|
None => Err((StatusCode::ACCEPTED, "ok")),
|
||||||
return Err((StatusCode::ACCEPTED, "ok"));
|
};
|
||||||
|
// 处理完成重定向后,清除Cookie
|
||||||
|
cookies.remove(Cookie::new("OriginalURL", ""));
|
||||||
|
return res;
|
||||||
} else {
|
} else {
|
||||||
return Err((StatusCode::UNAUTHORIZED, "wrong password"));
|
return Err((StatusCode::UNAUTHORIZED, "wrong password"));
|
||||||
}
|
}
|
||||||
@ -108,22 +111,9 @@ pub async fn login_page(headers: HeaderMap<HeaderValue>) -> impl IntoResponse {
|
|||||||
let mut env = Environment::new();
|
let mut env = Environment::new();
|
||||||
env.add_template("login.html", LOGIN_PAGE_HTML).unwrap();
|
env.add_template("login.html", LOGIN_PAGE_HTML).unwrap();
|
||||||
let template = env.get_template("login.html").unwrap();
|
let template = env.get_template("login.html").unwrap();
|
||||||
if let Some(original_uri) = headers.get("X-Original-URI") {
|
|
||||||
if let Ok(uri) = original_uri.to_str() {
|
|
||||||
tracing::info!("redirect to {}", uri);
|
|
||||||
if !uri.is_empty() {
|
|
||||||
let uri = "?original_url=".to_owned() + uri;
|
|
||||||
return Html(
|
|
||||||
template
|
|
||||||
.render(context! { url => uri, home_url => HOME_URL.to_string() })
|
|
||||||
.unwrap_or("Error".to_string()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Html(
|
Html(
|
||||||
template
|
template
|
||||||
.render(context! { url => String::new(), home_url => HOME_URL.to_string() })
|
.render(context! { home_url => HOME_URL.to_string() })
|
||||||
.unwrap_or("Error".to_string()),
|
.unwrap_or("Error".to_string()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user