No mundo digital, onde a segurança é fundamental e o tempo é essencial, o gerenciamento eficaz de tokens de acesso torna-se tanto uma arte quanto uma ciência. Imagine um mundo onde cada aperto de mão digital seja governado por uma senha com limite de tempo, um token que contém a chave para uma comunicação segura e autorizada. Esta é uma história sobre esses tokens, sua intrincada dança com o tempo e as engrenagens através do Rust que mantêm essa dança em perfeita harmonia.
Nossa história se desenrola no cenário servidor-cliente, um lugar onde a confiança é fundamental e cada interação é examinada. Aqui, um servidor deve enviar uma resposta ao cliente com um charme especial – um token de autenticação. Mas esse token não é uma chave comum; é semelhante à carruagem da Cinderela, mágica, mas limitada pelo tempo. À medida que o relógio avança e atinge a marca de 20 minutos, o token, assim como a carruagem, se transforma, garantindo que nenhum token expirado ultrapasse o prazo de boas-vindas.
Nessa dança de tokens, recorremos ao Rust, a linguagem conhecida pela sua robustez e confiabilidade, para coreografar os nossos passos. Nossas ferramentas? Tokio para criar tarefas que vigiam o relógio, e Axum para construir um servidor, o palco para nossa atuação.
Imagine um novo projeto de carga tendo token_manager
como bastidor, onde a magia do código se transforma em performance. Aqui, dependências como Axum e Tokio são os ajudantes de palco, montando o cenário para a nossa peça.
Nosso roteiro está escrito em routes.rs
, onde AuthToken
e TokenManager
assumem os papéis principais. AuthToken
é um personagem meticuloso, sempre atento ao tique-taque do relógio, enquanto TokenManager
é o mentor, garantindo que os tokens estejam atualizados e que o desempenho seja perfeito.
À medida que nossa história avança, TokenManager
ganha vida, fazendo malabarismos com AuthTokens
com precisão. É um espetáculo de tarefas assíncronas, onde tokens são gerados, monitorados e atualizados – tudo em uma dança rítmica orquestrada pelos ticks do Tokio.
Nosso palco está montado com lazy_static!
, garantindo que nosso TokenManager
esteja sempre pronto, sempre alerta. O tempo de expiração padrão e a contagem de ticks são como o andamento da nossa música, orientando o ritmo da nossa apresentação.
No grande final, nosso main.rs
, a cortina sobe em um servidor Axum. As rotas são expostas como caminhos em nosso palco, cada uma levando a um ato diferente de nossa história simbólica. Como público, você pode testemunhar a geração do token, uma maravilha por si só, atualizada a cada 5 segundos, uma prova da precisão do nosso código.
Ao navegar para http://localhost:3000
, você não está apenas acessando uma rota; está entrando em um mundo onde os tokens dançam ao som do Rust, onde a segurança é uma performance e onde a expiração de cada token é uma deixa para o próximo entrar em cena.
Neste mundo, o another_route
é a sua janela para o espetáculo em andamento, uma espiada por trás das cortinas do nosso teatro de tokens. Aqui, você vê o token ativo, uma estrela por si só, desempenhando seu papel perfeitamente até a hora de passar o bastão.
E assim, nossa história de gerenciamento de tokens em Rust termina, mas o espetáculo nunca termina de verdade. É um ciclo, um loop contínuo de segurança e eficiência, um balé de bytes e tokens, todos desempenhando seu papel no vasto teatro digital. Bem-vindo ao mundo do gerenciamento de tokens em Rust – seguro, eficiente e sempre pontual.
Vamos criar um novo projeto de carga token_manager e atualizar o toml da seguinte forma:
[package]
name = "token_manager"
version = "0.1.0"
edition = "2021"
# Veja mais chaves e suas definições em https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = "0.6.20"
hyper = { version = "0.14.27", features = ["full"] }
chrono = {version = "0.4.31" }
tokio = { version = "1.34.0", features = ["full"] }
lazy_static = "1.4.0"
Crie um crate route.rs que terá uma struct de token real e uma struct token_manager para expor APIs de monitoramento de token da seguinte maneira:
//routes.rs
use chrono::{DateTime, Utc};
use std::sync::{Arc, RwLock};
use tokio::time::{interval, Duration};
lazy_static! {
pub static ref GLOBAL_TOKEN_MANAGER: TokenManager = TokenManager::new(5, 1);
}
struct AuthToken {
token: String,
exp_time: DateTime<Utc>,
expire_seconds: i64,
}
impl AuthToken {
pub fn new(expire_seconds: i64) -> Self {
AuthToken {
token: String::new(),
exp_time: Utc::now() + chrono::Duration::seconds(expire_seconds),
expire_seconds,
}
}
pub async fn update_token(&mut self) {
println!("Generating new token!");
self.token = AuthToken::generate_secure_token().await;
self.exp_time = Utc::now() + chrono::Duration::seconds(self.expire_seconds);
}
pub fn refresh_token(&mut self, token: &str) {
println!("Generating new token!");
self.token = token.to_owned();
self.exp_time = Utc::now() + chrono::Duration::seconds(self.expire_seconds);
}
pub fn is_expired(&self) -> bool {
Utc::now() > self.exp_time
}
pub fn get_token(&self) -> String {
self.token.clone()
}
pub async fn generate_secure_token() -> String {
let mut interval = interval(Duration::from_secs(1));
interval.tick().await;
Utc::now().to_string()
}
}
pub struct TokenManager {
auth_token: Arc<RwLock<AuthToken>>,
tick_interval_secs: u64,
}
impl TokenManager {
pub fn new(expire_seconds: i64, tick_interval_secs: u64) -> Self {
Self {
auth_token: Arc::new(RwLock::new(AuthToken::new(expire_seconds))),
tick_interval_secs,
}
}
pub fn fetch_token(&self) -> Option<String> {
let token = self.auth_token.clone();
let token_string = match token.read() {
Ok(t) => Some(t.get_token()),
Err(e) => {
eprintln!("Failed to acquire lock: {}", e);
None
}
};
token_string
}
pub async fn refresh_token(&self) {
let token_ref = self.auth_token.clone();
let new_token = AuthToken::generate_secure_token().await;
match token_ref.write() {
Ok(mut t) => t.refresh_token(&new_token),
Err(e) => {
eprintln!("Failed to acquire lock: {}", e);
}
};
}
pub async fn generate_tokens(&self) -> tokio::task::JoinHandle<()> {
let token_ref = self.auth_token.clone();
let interval_secs = self.tick_interval_secs;
tokio::spawn(async move {
let mut interval = interval(Duration::from_secs(interval_secs));
loop {
interval.tick().await;
// Verifica se o token precisa ser atualizado
let needs_refresh = {
let token = match token_ref.read() {
Ok(t) => t,
Err(e) => {
eprintln!("Failed to acquire lock: {}", e);
continue;
}
};
token.is_expired()
};
// Atualiza o token, se necessário
if needs_refresh {
let new_token = AuthToken::generate_secure_token().await;
let mut token = match token_ref.write() {
Ok(t) => t,
Err(e) => {
eprintln!("Failed to acquire lock: {}", e);
continue;
}
};
token.refresh_token(&new_token);
}
}
})
}
}
A struct AuthToken tem os seguintes atributos:
struct AuthToken {
token: String, // isto conterá o token real
exp_time: DateTime<Utc>, // isto manterá o tempo de expiração do token ativo atual
expire_seconds: i64,// isto manterá as informações do tempo de expiração de um token.
}
Agora crie um TokenManager, que conterá AuthToken RwLock e contagem de ticks para esperar antes de verificar o tempo de expiração do token para um token ativo:
pub struct TokenManager {
auth_token: Arc<RwLock<AuthToken>>, // objeto real da struct do token
tick_interval_secs: u64,// período de espera para verificar o tempo de expiração de um token ativo
}
TokenManager terá um método público auxiliar para gerar e buscar o token sempre que necessário. É importante entender como o método generate_token funciona com base em nossos requisitos.
generate_token gera uma tarefa assíncrona do Tokio e faz uma chamada de leitura ao objeto auth_token para verificar se o token expirou ou não. Se o token tiver expirado, ele retornará true para o sinalizador need_refresh e, uma vez que o sinalizador need_refresh for definido como true, um bloqueio de escrita no auth_token é obtido. Um novo token será gerado antes de chamar o método refresh_token.
pub async fn generate_tokens(&self) -> tokio::task::JoinHandle<()> {
let token_ref = self.auth_token.clone();
let interval_secs = self.tick_interval_secs;
tokio::spawn(async move {
let mut interval = interval(Duration::from_secs(interval_secs));
loop {
interval.tick().await;
// Verifica se o token precisa ser atualizado
let needs_refresh = {
let token = match token_ref.read() {
Ok(t) => t,
Err(e) => {
eprintln!("Failed to acquire lock: {}", e);
continue;
}
};
token.is_expired()
};
// Atualiza o token, se necessário
if needs_refresh {
let new_token = AuthToken::generate_secure_token().await;
let mut token = match token_ref.write() {
Ok(t) => t,
Err(e) => {
eprintln!("Failed to acquire lock: {}", e);
continue;
}
};
token.refresh_token(&new_token);
}
}
})
}
}
Vamos criar uma variável estática para inicializar e acessar o TokenManger globalmente:
lazy_static! {
pub static ref GLOBAL_TOKEN_MANAGER: TokenManager = TokenManager::new(5, 1);
}
Podemos definir expiration_time e tick_count padrão.
Agora, vamos criar uma função principal para demonstrar o requisito:
//main.rs
use axum::{routing::get, Router};
// usar axum_server::Server;
#[macro_use]
extern crate lazy_static;
mod routes;
use crate::routes::route;
async fn token_logic() -> tokio::task::JoinHandle<()> {
route::GLOBAL_TOKEN_MANAGER.refresh_token().await;
let handle = route::GLOBAL_TOKEN_MANAGER.generate_tokens().await;
handle
}
pub async fn another_route() -> String {
if let Some(token) = route::GLOBAL_TOKEN_MANAGER.fetch_token() {
String::from(token)
} else {
String::new()
}
// "Esta é outra rota."
}
#[tokio::main]
async fn main() {
// construir nossa aplicação com uma única rota
let app = Router::new()
.route("/", get(|| async { "Hello, World!" }))
.route("/another", axum::routing::get(another_route));
let handle = token_logic().await;
// execute-o com hyper em localhost:3000
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
// handle.abort();
// println!("token aborted");
}
Em main, estamos criando uma aplicação que irá gerar um token e iniciar o servidor Axum com rotas expostas. Se você executar http://localhost:3000 em seu navegador, verá que a geração de token foi iniciada e ele está atualizando o token a cada 5 segundos, depois de verificar o tempo de expiração do token gerado continuamente a cada 1 segundo.
Você vê o valor do token ativo acessando a rota “another_route” (outra rota) em http://localhost:3000 /another_route.
Você pode atualizar a lógica generate_secure_token para criar token com base em sua necessidade.
É assim que podemos criar uma aplicação de token de acesso em Rust!
Este artigo foi escrito por Shobhit Chaturvedi e traduzido por Isabela Curado Nehme. Seu original pode ser lido aqui.
Oldest comments (0)