mirror of
https://github.com/danbulant/Portfolio
synced 2026-07-07 12:00:43 +00:00
feat: token vylepšení
This commit is contained in:
parent
e059719172
commit
44b4310ddf
8 changed files with 130 additions and 77 deletions
39
api/src/guard/candidate_jwt.rs
Normal file
39
api/src/guard/candidate_jwt.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
use config::DbConn;
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::outcome::Outcome;
|
||||||
|
use rocket::request::{self, FromRequest, Request};
|
||||||
|
use rocket::response::status;
|
||||||
|
use rocket_contrib::json::Json;
|
||||||
|
|
||||||
|
use portfolio_core::token::decode_candidate_token;
|
||||||
|
|
||||||
|
impl<'a, 'r> FromRequest<'a, 'r> for UserToken {
|
||||||
|
type Error = status::Custom<Json<Response>>;
|
||||||
|
fn from_request(
|
||||||
|
request: &'a Request<'r>,
|
||||||
|
) -> request::Outcome<Self, status::Custom<Json<Response>>> {
|
||||||
|
let conn = request.guard::<DbConn>().unwrap();
|
||||||
|
if let Some(authen_header) = request.headers().get_one("Authorization") {
|
||||||
|
let authen_str = authen_header.to_string();
|
||||||
|
if authen_str.starts_with("Bearer") {
|
||||||
|
let token = authen_str[6..authen_str.len()].trim();
|
||||||
|
if let Ok(token_data) = decode_candidate_token(token.to_string()) {
|
||||||
|
// if verify_token(&token_data, &conn) {
|
||||||
|
return Outcome::Success(token_data.claims);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Outcome::Failure((
|
||||||
|
Status::BadRequest,
|
||||||
|
status::Custom(
|
||||||
|
Status::Unauthorized,
|
||||||
|
Json(Response {
|
||||||
|
message: String::from("Invalid token"),
|
||||||
|
data: serde_json::to_value("").unwrap(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,11 +4,13 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
portfolio-entity = { path = "../entity" }
|
portfolio-entity = { path = "../entity" }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
argon2 = "0.4.1"
|
argon2 = "0.4.1"
|
||||||
chrono = "0.4.22"
|
chrono = "0.4.22"
|
||||||
jsonwebtoken = "8.1.1"
|
jsonwebtoken = "8.1.1"
|
||||||
|
dotenv = "0.15.0"
|
||||||
|
|
||||||
[dependencies.sea-orm]
|
[dependencies.sea-orm]
|
||||||
version = "^0.10.0"
|
version = "^0.10.0"
|
||||||
|
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
use chrono::Utc;
|
|
||||||
use config::DbConn;
|
|
||||||
use jsonwebtoken::errors::Result;
|
|
||||||
use jsonwebtoken::TokenData;
|
|
||||||
use jsonwebtoken::{Header, Validation};
|
|
||||||
use jsonwebtoken::{EncodingKey, DecodingKey};
|
|
||||||
use models::response::Response;
|
|
||||||
use models::user::{ User, LoginInfoDTO };
|
|
||||||
use rocket::http::Status;
|
|
||||||
use rocket::outcome::Outcome;
|
|
||||||
use rocket::request::{self, FromRequest, Request};
|
|
||||||
use rocket::response::status;
|
|
||||||
use rocket_contrib::json::Json;
|
|
||||||
|
|
||||||
static ONE_WEEK: i64 = 60 * 60 * 24 * 7; // in seconds
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct UserToken {
|
|
||||||
// issued at
|
|
||||||
pub iat: i64,
|
|
||||||
// expiration
|
|
||||||
pub exp: i64,
|
|
||||||
// data
|
|
||||||
pub user: String,
|
|
||||||
pub login_session: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'r> FromRequest<'a, 'r> for UserToken {
|
|
||||||
type Error = status::Custom<Json<Response>>;
|
|
||||||
fn from_request(
|
|
||||||
request: &'a Request<'r>,
|
|
||||||
) -> request::Outcome<Self, status::Custom<Json<Response>>> {
|
|
||||||
let conn = request.guard::<DbConn>().unwrap();
|
|
||||||
if let Some(authen_header) = request.headers().get_one("Authorization") {
|
|
||||||
let authen_str = authen_header.to_string();
|
|
||||||
if authen_str.starts_with("Bearer") {
|
|
||||||
let token = authen_str[6..authen_str.len()].trim();
|
|
||||||
if let Ok(token_data) = decode_token(token.to_string()) {
|
|
||||||
if verify_token(&token_data, &conn) {
|
|
||||||
return Outcome::Success(token_data.claims);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Outcome::Failure((
|
|
||||||
Status::BadRequest,
|
|
||||||
status::Custom(
|
|
||||||
Status::Unauthorized,
|
|
||||||
Json(Response {
|
|
||||||
message: String::from("Invalid token"),
|
|
||||||
data: serde_json::to_value("").unwrap(),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_token(login: LoginInfoDTO) -> String {
|
|
||||||
let now = Utc::now().timestamp_nanos() / 1_000_000_000; // nanosecond -> second
|
|
||||||
let payload = UserToken {
|
|
||||||
iat: now,
|
|
||||||
exp: now + ONE_WEEK,
|
|
||||||
user: login.username,
|
|
||||||
login_session: login.login_session,
|
|
||||||
};
|
|
||||||
|
|
||||||
jsonwebtoken::encode(&Header::default(), &payload, &EncodingKey::from_secret(include_bytes!("secret.key"))).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decode_token(token: String) -> Result<TokenData<UserToken>> {
|
|
||||||
jsonwebtoken::decode::<UserToken>(&token, &DecodingKey::from_secret(include_bytes!("secret.key")), &Validation::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify_token(token_data: &TokenData<UserToken>, conn: &DbConn) -> bool {
|
|
||||||
User::is_valid_login_session(&token_data.claims, conn)
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
mod mutation;
|
mod mutation;
|
||||||
mod query;
|
mod query;
|
||||||
pub mod crypto;
|
pub mod crypto;
|
||||||
|
pub mod token;
|
||||||
|
|
||||||
pub use mutation::*;
|
pub use mutation::*;
|
||||||
pub use query::*;
|
pub use query::*;
|
||||||
|
|
|
||||||
9
core/src/token/admin_token.rs
Normal file
9
core/src/token/admin_token.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct AdminToken {
|
||||||
|
// issued at
|
||||||
|
pub iat: i64,
|
||||||
|
// expiration
|
||||||
|
pub exp: i64,
|
||||||
|
}
|
||||||
11
core/src/token/candidate_token.rs
Normal file
11
core/src/token/candidate_token.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct CandidateToken {
|
||||||
|
// issued at
|
||||||
|
pub iat: i64,
|
||||||
|
// expiration
|
||||||
|
pub exp: i64,
|
||||||
|
pub name: String,
|
||||||
|
pub surname: String,
|
||||||
|
}
|
||||||
67
core/src/token/mod.rs
Normal file
67
core/src/token/mod.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
pub mod admin_token;
|
||||||
|
pub mod candidate_token;
|
||||||
|
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
|
use entity::{admin, candidate};
|
||||||
|
use jsonwebtoken::errors::Result;
|
||||||
|
use jsonwebtoken::TokenData;
|
||||||
|
use jsonwebtoken::{DecodingKey, EncodingKey};
|
||||||
|
use jsonwebtoken::{Header, Validation};
|
||||||
|
|
||||||
|
use admin_token::AdminToken;
|
||||||
|
use candidate_token::CandidateToken;
|
||||||
|
|
||||||
|
const ONE_WEEK: i64 = 60 * 60 * 24 * 7;
|
||||||
|
|
||||||
|
pub fn generate_candidate_token(candidate: candidate::Model) -> String {
|
||||||
|
let now = Utc::now().timestamp_nanos() / 1_000_000_000; // nanosecond -> second
|
||||||
|
let payload = CandidateToken {
|
||||||
|
iat: now,
|
||||||
|
exp: now + ONE_WEEK,
|
||||||
|
name: candidate.name.unwrap_or_else(|| "".into()),
|
||||||
|
surname: candidate.surname.unwrap_or_else(|| "".into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
jsonwebtoken::encode(
|
||||||
|
&Header::default(),
|
||||||
|
&payload,
|
||||||
|
&EncodingKey::from_secret(include_bytes!("secret.key")),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_admin_token(_admin: admin::Model) -> String {
|
||||||
|
let now = Utc::now().timestamp_nanos() / 1_000_000_000; // nanosecond -> second
|
||||||
|
let payload = AdminToken {
|
||||||
|
iat: now,
|
||||||
|
exp: now + ONE_WEEK,
|
||||||
|
};
|
||||||
|
|
||||||
|
jsonwebtoken::encode(
|
||||||
|
&Header::default(),
|
||||||
|
&payload,
|
||||||
|
&EncodingKey::from_secret(include_bytes!("secret.key")),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_candidate_token(token: String) -> Result<TokenData<CandidateToken>> {
|
||||||
|
jsonwebtoken::decode::<CandidateToken>(
|
||||||
|
&token,
|
||||||
|
&DecodingKey::from_secret(include_bytes!("secret.key")),
|
||||||
|
&Validation::default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_admin_token(token: String) -> Result<TokenData<AdminToken>> {
|
||||||
|
jsonwebtoken::decode::<AdminToken>(
|
||||||
|
&token,
|
||||||
|
&DecodingKey::from_secret(include_bytes!("secret.key")),
|
||||||
|
&Validation::default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*pub fn verify_token(token_data: &TokenData<UserToken>, conn: &DbConn) -> bool {
|
||||||
|
User::is_valid_login_session(&token_data.claims, conn)
|
||||||
|
}*/
|
||||||
1
core/src/token/secret.key
Normal file
1
core/src/token/secret.key
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
temp
|
||||||
Loading…
Reference in a new issue