9 个不稳定版本 (3 个破坏性版本)
使用旧的 Rust 2015
0.4.3 | 2018年7月30日 |
---|---|
0.4.2 | 2018年7月22日 |
0.3.1 | 2018年7月12日 |
0.2.1 | 2018年7月1日 |
0.1.2 | 2018年6月30日 |
#2579 in 数据库接口
27 每月下载量
39KB
627 代码行
Actix 数据库身份提供者
Actix 框架的 SQL 数据库(diesel)集成
描述
实现了 Actix-Web 身份中间件的 SQL 后端。不执行任何身份验证,仅在需要时“记住”用户。可以通过 SqlIdentityBuilder::response_header()
方法配置包含授权令牌的返回头。
正常服务器应用程序流程
- 应用程序验证用户身份
- 应用程序使用用户可识别的字符串调用
remember()
- SQL 身份中间件生成新令牌并将其嵌入响应头中
- 应用程序返回响应(包含头信息)
从这里,可以按照身份中间件指南继续正常流程
正常客户端应用程序流程
- 客户端向登录端点发送 POST 请求,并由服务器授权
- 客户端接收响应,从指定的头信息中提取授权令牌
- 在未来的请求(包括注销)中,客户端使用返回的令牌构建带有身份验证头的载体认证
支持的 SQL 变体
- SQLite
- MySQL
- Postgres
特性
默认: SQLite, MySQL, Postgres
sqlite: 包含 SQLite 支持
mysql: 包含 MySQL 支持
postgres: 包含 PostgreSQL 支持
数据库要求
此crate需要一个名为 identities 的表,包含以下字段
字段 | 类型 | 约束 | 描述 |
---|---|---|---|
id | BIGINT | PRIMARY KEY, AUTO INCREMENT | A unique id that is not the token, for revoking sessions |
token | CHAR(32) | NOT NULL, UNIQUE | The auto-generated token field, will be used to lookup user |
userid | TEXT | NOT NULL | The user id to remember, probably a key in another table |
ip | TEXT | The IP the user most recently connected from | |
useragent | TEXT | The user-agent of the most recent connection | |
created | TIMESTAMP | NOT NULL | Timestamp (w/out timezone) this token was created |
modified | TIMESTAMP | NOT NULL | Timestamp (w/out timezone) this token was last used |
SQLite、MySQL 和 PostgreSQL 的示例 SQL 文件可在存储库的 sql/ 文件夹中找到
服务器示例
extern crate actix_web;
extern crate actix_web_sql_identity;
use actix_web::{http, server, App, HttpRequest, Responder};
use actix_web::middleware::identity::{IdentityService, RequestIdentity};
use actix_web_sql_identity::SqlIdentityBuilder;
const POOL_SIZE: usize = 3; // Number of connections per pool
fn login(mut req: HttpRequest) -> impl Responder {
// Should pull username/id from request
req.remember("username_or_id".to_string());
"Logged in!".to_string()
}
fn profile(req: HttpRequest) -> impl Responder {
if let Some(user) = req.identity() {
format!("Hello, {}!", user)
} else {
"Hello, anonymous user!".to_string()
}
}
fn logout(mut req: HttpRequest) -> impl Responder {
req.forget();
"Logged out!".to_string()
}
fn main() {
server::new(|| {
// Construct our policy, passing the address and any options
let policy = SqlIdentityBuilder::new("sqlite://my.db")
.pool_size(POOL_SIZE);
App::new()
.route("/login", http::Method::POST, login)
.route("/profile", http::Method::GET, profile)
.route("/logout", http::Method::POST, logout)
.middleware(IdentityService::new(
policy.finish()
.expect("failed to connect to database")))
})
.bind("127.0.0.1:7070").unwrap()
.run();
}
客户端示例
extern crate reqwest;
#[macro_use]
extern crate hyper;
use hyper::header::{Authorization, Bearer, Headers};
use reqwest::{Client, Response};
// Build our custom header that will contain our returned token
header! { (XActixAuth, "X-Actix-Auth") => [String] }
/// Builds a GET request to send to the server, with optional authentication
///
/// # Arguments
///
/// * `client` - Client to build request with
/// * `uri` - Endpoint to target (e.g., /index, /profile, etc)
/// * `token` - An optional authentication token
fn build_get(client: &Client, uri: &str, token: Option<&str>) -> Response {
let mut req = client
.get(format!("http://127.0.0.1:7070{}", uri).as_str());
if let Some(token) = token {
let mut headers = Headers::new();
headers.set(Authorization(Bearer {
token: token.to_owned(),
}));
req.headers(headers);
}
let req = req.build()
.expect("failed to build request");
client.execute(req).expect("failed to send request")
}
/// Builds a POST request to send to the server, with optional authentication
///
/// # Arguments
///
/// * `client` - Client to build request with
/// * `uri` - Endpoint to target (e.g., /login, /logout)
/// * `token` - An optional authentication token
fn build_post(client: &Client, uri: &str, token: Option<&str>) -> Response {
let mut req = client
.post(format!("http://127.0.0.1:7070{}", uri).as_str());
if let Some(token) = token {
let mut headers = Headers::new();
headers.set(Authorization(Bearer {
token: token.to_owned(),
}));
req.headers(headers);
}
let req = req.build()
.expect("failed to build request");
client.execute(req).expect("failed to send request")
}
/// Pretty print a response
///
/// # Arguments
///
/// * `resp` - Response to print
fn print_response(resp: &mut Response) {
let uri = resp.url().clone();
println!("[{0: <15}] {1: <30} {2}", resp.status(), uri.as_str(), resp.text().expect("failed to read response"));
}
fn main() {
let client = Client::new();
// Get Index
let mut resp = build_get(&client, "/", None);
print_response(&mut resp);
// Get Profile (no auth)
let mut resp = build_get(&client, "/profile", None);
print_response(&mut resp);
// Login
let mut resp = build_post(&client, "/login", None);
print_response(&mut resp);
// Extract the auth token from the header
// (Header field can be changed on server, default is used here)
let hdrs = resp.headers();
let token = hdrs.get::<XActixAuth>().unwrap();
//println!("[token]: {:?}", token.0);
// Get Profile (auth)
let mut resp = build_get(&client, "/profile", Some(token.0.as_ref()));
print_response(&mut resp);
// Logout
let mut resp = build_post(&client, "/logout", Some(token.0.as_str()));
print_response(&mut resp);
// Get Profile (auth)
let mut resp = build_get(&client, "/profile", Some(token.0.as_ref()));
print_response(&mut resp);
}
许可
BSD 3-Clause 许可协议
作者
Kevin Allison kvnallsn AT gmail.com
依赖项
~26–38MB
~613K SLoC