Rust完全是新的。我试图实现oauth身份验证,我使用axum,没有成功。.以下是我丑陋的代码:
use axum::{
Json,
extract::Query,
extract::Extension,
http::StatusCode,
response::IntoResponse
};
use serde_json::{json, Value};
use hyper;
use hyper_tls::HttpsConnector;
use hyper::header;
use cookie::Cookie;
use serde::{ Deserialize, Serialize };
#[derive(Clone)]
pub struct GitHubOAuth2 {
client_id: String,
redirect_uri: String,
client_secret: String
}
#[derive(Serialize, Deserialize)]
pub struct CallbackAuthCode {
code: String
}
impl GitHubOAuth2 {
pub fn new(conf: String) -> GitHubOAuth2 {
let json_content : Value = serde_json::from_str(&conf).expect("Invalid configuration.");
GitHubOAuth2 {
client_id: json_content["github_oauth2"]["client_id"].as_str().unwrap().to_string(),
redirect_uri: json_content["github_oauth2"]["redirect_uri"].as_str().unwrap().to_string(),
client_secret: json_content["github_oauth2"]["client_secret"].as_str().unwrap().to_string()
}
}
}
pub async fn callback(Query(params): Query<CallbackAuthCode>, Extension(conf): Extension<GitHubOAuth2>) -> impl IntoResponse {
let params_code = ¶ms.code;
let mut get_token_url: String = String::from("https://www.github.com/login/oauth/access_token?client_id=");
get_token_url.push_str(&conf.client_id);
get_token_url.push_str("&redirect_uri=");
get_token_url.push_str(&conf.redirect_uri);
get_token_url.push_str("&client_secret=");
get_token_url.push_str(&conf.client_secret);
get_token_url.push_str("&code=");
get_token_url.push_str(¶ms_code);
println!("get_token_url: {}", get_token_url);
let https = HttpsConnector::new();
let client = hyper::Client::builder().build::<_, hyper::Body>(https);
let req = hyper::Request::builder()
.method(hyper::Method::POST)
.uri(get_token_url)
.header("Accept", "application/json")
.body(hyper::Body::empty()).unwrap();
match client.request(req).await {
Ok(resp) => {
println!("response: {}", resp.status());
let redirectUri : String = resp.headers().get("Location").unwrap().to_str().unwrap().to_string();
if resp.status() == 301 {
let redirectReq = hyper::Request::builder()
.method(hyper::Method::POST)
.uri(redirectUri)
.header("Accept", "application/json")
.body(hyper::Body::empty()).unwrap();
match client.request(redirectReq).await {
Ok(mut redirectResp) => {
let body = hyper::body::to_bytes(redirectResp.body_mut()).await.unwrap();
println!("{} {:?}", redirectResp.status(), body);
let body_as_json : Value = serde_json::from_slice(&body).unwrap();
let bearer_token = body_as_json["access_token"].as_str().unwrap().to_string();
let cookie = Cookie::build("hey", bearer_token).secure(true).http_only(true).finish();
return (
StatusCode::OK,
[(header::SET_COOKIE, &cookie.value())],
Json(json!({
"msg": "got that cookie"
}))
);
},
Err(mut redirect_e) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
[(header::CONTENT_TYPE, &"application/json")],
Json(json!({
"error": redirect_e.to_string()
}))
);
}
}
} else {
return (
StatusCode::NOT_IMPLEMENTED,
[(header::CONTENT_TYPE, &"application/json")],
Json(json!({
"error": String::from("service replies with unexpected response.")
}))
);
}
},
Err(e) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
[(header::CONTENT_TYPE, &"application/json")],
Json(json!({
"error": e.to_string()
}))
);
}
}
}
callback
函数旨在实现oauth2中的'callback'阶段,因此它选择了auth代码并使用它来调用github IdP以收集授权令牌。没什么大不了的。问题是这段代码无法编译,我不明白为什么。编译器说:
error[E0277]: the trait bound `(StatusCode, [(HeaderName, &&str); 1], Json<serde_json::Value>): IntoResponse` is not satisfied
callback
函数作为get
处理程序附加到服务器。
我从axum的基本示例开始,尝试一步一步地构建我的怪物,但现在我被卡住了。我错过了什么?
我的Cargo.toml
:
[package]
name = "keymaster"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = "0.5.16"
axum-macros = "0.2.3"
hyper = { version = "0.14.20", features = ["full"] }
tokio = { version = "1.21.2", features = ["full"] }
tower = "0.4.13"
serde_json = "1.0.85"
serde = "1.0.145"
jwt-simple = "0.10"
clap = { version = "4.0.19", features = ["derive"] }
hyper-tls = "0.5.0"
cookie = "0.17.0"
follow-redirects = "0.1.3"
http = "0.2.9"
[dependencies.uuid]
version = "1.2.1"
features = [
"v4", # Lets you generate random UUIDs
"fast-rng", # Use a faster (but still sufficiently random) RNG
"macro-diagnostics", # Enable better diagnostics for compile-time UUIDs
]
20230409更新
从我的&"application/json"
中删除&
会导致编译器报告更多错误。完整的日志在这里:
--> src/server/handlers/github_oauth2.rs:14:5
|
14 | use http::header::HeaderValue;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
error[E0308]: mismatched types
--> src/server/handlers/github_oauth2.rs:92:41
|
92 | [(header::CONTENT_TYPE, "application/json")],
| ^^^^^^^^^^^^^^^^^^ expected `&str`, found `str`
|
= note: expected reference `&&str`
found reference `&'static str`
note: return type inferred to be `&&str` here
--> src/server/handlers/github_oauth2.rs:81:22
|
81 | return (
| ______________________^
82 | | StatusCode::OK,
83 | | [(header::SET_COOKIE, &cookie.value())],
84 | | Json(json!({
85 | | "msg": "got that cookie"
86 | | }))
87 | | );
| |_______________^
error[E0277]: the trait bound `(StatusCode, [(HeaderName, &&str); 1], Json<serde_json::Value>): IntoResponse` is not satisfied
--> src/server/handlers/github_oauth2.rs:40:126
|
40 | pub async fn callback(Query(params): Query<CallbackAuthCode>, Extension(conf): Extension<GitHubOAuth2>) -> impl IntoResponse {
| ______________________________________________________________________________________________________________________________^
41 | | let params_code = ¶ms.code;
42 | |
43 | | let mut get_token_url: String = String::from("https://www.github.com/login/oauth/access_token?client_id=");
... |
118 | | }
119 | | }
| |_^ the trait `IntoResponse` is not implemented for `(StatusCode, [(HeaderName, &&str); 1], Json<serde_json::Value>)`
|
= help: the following other types implement trait `IntoResponse`:
()
(Response<()>, R)
(Response<()>, T1, R)
(Response<()>, T1, T2, R)
(Response<()>, T1, T2, T3, R)
(Response<()>, T1, T2, T3, T4, R)
(Response<()>, T1, T2, T3, T4, T5, R)
(Response<()>, T1, T2, T3, T4, T5, T6, R)
and 60 others
error[E0308]: mismatched types
--> src/server/handlers/github_oauth2.rs:102:35
|
102 | [(header::CONTENT_TYPE, "application/json")],
| ^^^^^^^^^^^^^^^^^^ expected `&str`, found `str`
|
= note: expected reference `&&str`
found reference `&'static str`
note: return type inferred to be `&&str` here
--> src/server/handlers/github_oauth2.rs:81:22
|
81 | return (
| ______________________^
82 | | StatusCode::OK,
83 | | [(header::SET_COOKIE, &cookie.value())],
84 | | Json(json!({
85 | | "msg": "got that cookie"
86 | | }))
87 | | );
| |_______________^
error[E0308]: mismatched types
--> src/server/handlers/github_oauth2.rs:112:33
|
112 | [(header::CONTENT_TYPE, "application/json")],
| ^^^^^^^^^^^^^^^^^^ expected `&str`, found `str`
|
= note: expected reference `&&str`
found reference `&'static str`
note: return type inferred to be `&&str` here
--> src/server/handlers/github_oauth2.rs:81:22
|
81 | return (
| ______________________^
82 | | StatusCode::OK,
83 | | [(header::SET_COOKIE, &cookie.value())],
84 | | Json(json!({
85 | | "msg": "got that cookie"
86 | | }))
87 | | );
| |_______________^
error[E0277]: the trait bound `(StatusCode, [(HeaderName, &&str); 1], Json<serde_json::Value>): IntoResponse` is not satisfied
--> src/server/handlers/github_oauth2.rs:40:108
|
40 | pub async fn callback(Query(params): Query<CallbackAuthCode>, Extension(conf): Extension<GitHubOAuth2>) -> impl IntoResponse {
| ^^^^^^^^^^^^^^^^^ the trait `IntoResponse` is not implemented for `(StatusCode, [(HeaderName, &&str); 1], Json<serde_json::Value>)`
|
= help: the following other types implement trait `IntoResponse`:
()
(Response<()>, R)
(Response<()>, T1, R)
(Response<()>, T1, T2, R)
(Response<()>, T1, T2, T3, R)
(Response<()>, T1, T2, T3, T4, R)
(Response<()>, T1, T2, T3, T4, T5, R)
(Response<()>, T1, T2, T3, T4, T5, T6, R)
and 60 others
Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
warning: `keymaster` (bin "keymaster") generated 1 warning
error: could not compile `keymaster` due to 5 previous errors; 1 warning emitted
1条答案
按热度按时间e3bfsja21#
错误看起来很清楚?它在告诉你
不支持
IntoResponse
。由于这是一个3元组,第一个成员是
StatusCode
,因此最接近的impl为有一个
serde_value::Value
实现了Serialize
,剩下中间的项:这是一个(k,v)元组的数组
HeaderName
可以简单地转换为HeaderName
,但是&&str as TryInto<HeaderValue>
呢?看看impl TryFrom<> for HeaderValue
(它是镜像,通常是实现的trait),我们可以看到:实际上,没有
&&str
的实现,所以&&str
不能转换为HeaderValue
。只要删除完全不必要的字符串字面量引用,字符串字面量已经是
&'static str
。