Skip to content

Commit 4bf9e6c

Browse files
committed
鉴权和用户接口
1 parent 5f2476d commit 4bf9e6c

File tree

19 files changed

+1463
-3
lines changed

19 files changed

+1463
-3
lines changed

Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,12 @@ uuid = { version = "0.8", features = ["v4", "serde"] }
2222
redis = { version = "0.21", features = ["tokio-comp"] }
2323
mobc = "0.7"
2424
regex = "1"
25+
futures = "0.3"
26+
md5 = "0.7"
27+
sha2 = "0.10"
28+
jsonwebtoken = "7"
29+
aes = "0.7"
30+
block-modes = "0.8"
31+
base64 = "0.13"
32+
rand = "0.8"
33+
bytebuffer = "0.2"

data/config/app.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ pool_get_timeout_seconds = 10
2121
pool_max_open = 100
2222
pool_max_idle = 8
2323
pool_max_lifetime_seconds = 60
24+
25+
[auth]
26+
access_token_expire = 7200
27+
refresh_token_expire = 604800

src/api/authorizations/controller.rs

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
use actix_web::{web, post, put, delete, HttpResponse, HttpRequest};
2+
use actix_web::dev::ConnectionInfo;
3+
use serde::{Serialize, Deserialize};
4+
use crate::AppState;
5+
use crate::lib::{error, validator, client, auth};
6+
use crate::api::user;
7+
use super::{service, AuthBlacklist, Authorization};
8+
use chrono::prelude::*;
9+
10+
#[derive(Deserialize)]
11+
pub struct CreateAuthReqJson {
12+
username: Option<String>,
13+
password: Option<String>,
14+
}
15+
16+
#[derive(Serialize)]
17+
struct ResTokenJson {
18+
id: String,
19+
access_token: String,
20+
expires_in: i64,
21+
refresh_token: String,
22+
created_at: String,
23+
updated_at: String,
24+
}
25+
26+
// 创建授权
27+
#[post("/authorizations")]
28+
pub async fn create_auth(req_info: web::Json<CreateAuthReqJson>, state: web::Data<AppState>, req: HttpRequest, conn: ConnectionInfo) -> Result<web::HttpResponse, error::Error> {
29+
let username = validator::required_str(&req_info.username, "用户名")?;
30+
let password = validator::required_str(&req_info.password, "密码")?;
31+
32+
let client = client::get_client_info(&state, &req, &conn);
33+
34+
let result = user::service::get_by_username(&username, &state).await?;
35+
let u = match result {
36+
None => {
37+
service::insert_log(1003, &username, 0, 0, &client, &state).await?;
38+
return Err(error::new(100400, "帐号或密码不正确", 422));
39+
},
40+
Some(v) => v
41+
};
42+
43+
let user_id = match u.id {
44+
None => return Err(error::new(100400, "帐号或密码不正确", 422)),
45+
Some(v) => v,
46+
};
47+
48+
let user_type = match u.user_type {
49+
None => return Err(error::new(100400, "帐号或密码不正确", 422)),
50+
Some(v) => v,
51+
};
52+
53+
let is_del = match u.is_del {
54+
None => return Err(error::new(100400, "帐号或密码不正确", 422)),
55+
Some(v) => v,
56+
};
57+
if is_del != 0 {
58+
service::insert_log(1004, "", user_id, 0, &client, &state).await?;
59+
return Err(error::new(100400, "帐号或密码不正确", 422));
60+
}
61+
62+
let is_enabled = match u.is_enabled {
63+
None => return Err(error::new(100400, "帐号或密码不正确", 422)),
64+
Some(v) => v,
65+
};
66+
if is_enabled != 1 {
67+
service::insert_log(1002, "", user_id, 0, &client, &state).await?;
68+
return Err(error::new(100400, "帐号或密码不正确", 422));
69+
}
70+
71+
let user_password = match u.password {
72+
None => return Err(error::new(100400, "帐号或密码不正确", 422)),
73+
Some(v) => v,
74+
};
75+
let salt = match u.salt {
76+
None => return Err(error::new(100400, "帐号或密码不正确", 422)),
77+
Some(v) => v,
78+
};
79+
let pwd = auth::crypt_password(&password, &salt);
80+
if user_password != pwd {
81+
service::insert_log(1001, "", user_id, 0, &client, &state).await?;
82+
return Err(error::new(100400, "帐号或密码不正确", 422));
83+
}
84+
85+
let auth = auth::create_auth(user_id, user_type, &client, &state).await?;
86+
service::insert_log(1, "", user_id, auth.auth_id, &client, &state).await?;
87+
88+
Ok(HttpResponse::Ok().json(ResTokenJson {
89+
id: auth.refresh_token_id.to_string(),
90+
access_token: auth.access_token.token,
91+
expires_in: auth.access_token.expire,
92+
refresh_token: auth.refresh_token.token,
93+
created_at: format!("{:?}", auth.access_token.create_time),
94+
updated_at: format!("{:?}", auth.access_token.create_time),
95+
}))
96+
}
97+
98+
// 刷新授权
99+
#[put("/authorizations/{id}")]
100+
pub async fn refresh_auth(req: HttpRequest, state: web::Data<AppState>, conn: ConnectionInfo) -> Result<web::HttpResponse, error::Error> {
101+
let id: String = req.match_info().get("id").unwrap().parse().unwrap();
102+
validator::uuid(&id, "授权id")?;
103+
104+
let token = match req.headers().get("Authorization") {
105+
None => return Err(error::new(100403, "Authentication failure", 401)),
106+
Some(v) => {
107+
if v.len() <= 6 {
108+
return Err(error::new(100403, "Authentication failure", 401));
109+
}
110+
let v = v.to_str().unwrap_or_default().to_string();
111+
if &v[..7] != "Bearer " {
112+
return Err(error::new(100403, "Authentication failure", 401));
113+
}
114+
String::from(&v[7..])
115+
}
116+
};
117+
118+
let client = client::get_client_info(&state, &req, &conn);
119+
120+
let claims = auth::parse_token(&token)?;
121+
let mut have_permission = false;
122+
for v in claims.scopes {
123+
if v == "ROLE_REFRESH_TOKEN" {
124+
have_permission = true;
125+
break;
126+
}
127+
}
128+
if !have_permission {
129+
service::insert_log(1053, "", 0, 0, &client, &state).await?;
130+
return Err(error::new(100404, "No permission", 403));
131+
}
132+
133+
let auth_id = claims.sub.parse::<i32>().unwrap();
134+
135+
let auth_data = match service::get_by_id(auth_id, &state).await? {
136+
None => {
137+
service::insert_log(1058, "", 0, auth_id, &client, &state).await?;
138+
return Err(error::new(100403, "Authentication failure", 401));
139+
},
140+
Some(v) => v
141+
};
142+
143+
let user_id = match auth_data.user_id {
144+
None => return Err(error::new(100403, "Authentication failure", 401)),
145+
Some(v) => v,
146+
};
147+
148+
let user_data = match user::service::get_by_id(user_id, &state).await? {
149+
None => {
150+
service::insert_log(1058, "", 0, auth_id, &client, &state).await?;
151+
return Err(error::new(100403, "Authentication failure", 401));
152+
},
153+
Some(v) => v
154+
};
155+
156+
match auth_data.uuid {
157+
None => return Err(error::new(100403, "Authentication failure", 401)),
158+
Some(v) => {
159+
if v.to_string() != id {
160+
service::insert_log(1059, "", user_id, auth_id, &client, &state).await?;
161+
return Err(error::new(100403, "Authentication failure", 401));
162+
}
163+
},
164+
};
165+
166+
match auth_data.is_enabled {
167+
None => return Err(error::new(100403, "Authentication failure", 401)),
168+
Some(v) => {
169+
if v != 1 {
170+
service::insert_log(1060, "", user_id, auth_id, &client, &state).await?;
171+
return Err(error::new(100403, "Authentication failure", 401));
172+
}
173+
},
174+
};
175+
176+
match auth_data.refresh_token {
177+
None => return Err(error::new(100403, "Authentication failure", 401)),
178+
Some(v) => {
179+
if v.to_string() != claims.jti {
180+
service::insert_log(1060, "", user_id, auth_id, &client, &state).await?;
181+
return Err(error::new(100403, "Authentication failure", 401));
182+
}
183+
},
184+
};
185+
186+
match user_data.is_enabled {
187+
None => return Err(error::new(100403, "Authentication failure", 401)),
188+
Some(v) => {
189+
if v != 1 {
190+
service::insert_log(1061, "", user_id, auth_id, &client, &state).await?;
191+
return Err(error::new(100403, "Authentication failure", 401));
192+
}
193+
},
194+
};
195+
196+
match user_data.is_del {
197+
None => return Err(error::new(100403, "Authentication failure", 401)),
198+
Some(v) => {
199+
if v != 0 {
200+
service::insert_log(1062, "", user_id, auth_id, &client, &state).await?;
201+
return Err(error::new(100403, "Authentication failure", 401));
202+
}
203+
},
204+
};
205+
206+
let user_type = match user_data.user_type {
207+
None => return Err(error::new(100403, "Authentication failure", 401)),
208+
Some(v) => v,
209+
};
210+
211+
let access_token_id = match auth_data.access_token_id {
212+
None => return Err(error::new(100403, "Authentication failure", 401)),
213+
Some(v) => v,
214+
};
215+
216+
let access_token_exp = match auth_data.access_token_exp {
217+
None => return Err(error::new(100403, "Authentication failure", 401)),
218+
Some(v) => v,
219+
};
220+
221+
let refresh_token_jti = uuid::Uuid::new_v4();
222+
let update_time = Utc::now();
223+
224+
let access_token = auth::create_access_token(user_id, user_type, &state.config);
225+
let refresh_token = auth::create_refresh_token(auth_id, refresh_token_jti, &state.config);
226+
227+
let authorization_blacklist = AuthBlacklist {
228+
id: None,
229+
access_token_id,
230+
access_token_exp,
231+
user_id,
232+
};
233+
234+
service::add_black_list(&authorization_blacklist, &state).await?;
235+
236+
let authorization = Authorization {
237+
id: Some(auth_id),
238+
user_id: None,
239+
uuid: None,
240+
client_type: None,
241+
refresh_token: Some(refresh_token_jti),
242+
create_time: None,
243+
update_time: Some(update_time),
244+
last_refresh_time: Some(update_time),
245+
access_token_id: Some(access_token.jti),
246+
access_token_exp: Some(access_token.expire_time),
247+
access_token_iat: Some(access_token.create_time),
248+
is_enabled: None,
249+
};
250+
251+
service::update_auth(&authorization, &state).await?;
252+
service::insert_log(2, "", user_id, auth_id, &client, &state).await?;
253+
254+
Ok(HttpResponse::Ok().json(ResTokenJson {
255+
id,
256+
access_token: access_token.token,
257+
expires_in: access_token.expire,
258+
refresh_token: refresh_token.token,
259+
created_at: format!("{:?}", access_token.create_time),
260+
updated_at: format!("{:?}", update_time),
261+
}))
262+
}
263+
264+
// 删除授权
265+
#[delete("/authorizations/{id}")]
266+
pub async fn delete_auth(req: HttpRequest, state: web::Data<AppState>, conn: ConnectionInfo) -> Result<web::HttpResponse, error::Error> {
267+
let id: String = req.match_info().get("id").unwrap().parse().unwrap();
268+
validator::uuid(&id, "授权id")?;
269+
270+
let client = client::get_client_info(&state, &req, &conn);
271+
272+
let auth_data = service::get_by_uuid(&id, &state).await?;
273+
let auth_data = match auth_data {
274+
None => {
275+
service::insert_log(1101, "", 0, 0, &client, &state).await?;
276+
return Err(error::new(100403, "Authentication failure", 401));
277+
},
278+
Some(v) => v
279+
};
280+
281+
let user_id = match auth_data.user_id {
282+
None => return Err(error::new(100403, "Authentication failure", 401)),
283+
Some(v) => v,
284+
};
285+
286+
let auth_id = match auth_data.id {
287+
None => return Err(error::new(100403, "Authentication failure", 401)),
288+
Some(v) => v,
289+
};
290+
291+
match auth_data.is_enabled {
292+
None => return Err(error::new(100403, "Authentication failure", 401)),
293+
Some(v) => {
294+
if v != 1 {
295+
service::insert_log(1102, "", user_id, auth_id, &client, &state).await?;
296+
return Err(error::new(100403, "Authentication failure", 401));
297+
}
298+
},
299+
};
300+
301+
let access_token_id = match auth_data.access_token_id {
302+
None => return Err(error::new(100403, "Authentication failure", 401)),
303+
Some(v) => v,
304+
};
305+
306+
let access_token_exp = match auth_data.access_token_exp {
307+
None => return Err(error::new(100403, "Authentication failure", 401)),
308+
Some(v) => v,
309+
};
310+
311+
service::revoke_auth(auth_id, &state).await?;
312+
313+
let authorization_blacklist = AuthBlacklist {
314+
id: None,
315+
access_token_id,
316+
access_token_exp,
317+
user_id,
318+
};
319+
320+
service::add_black_list(&authorization_blacklist, &state).await?;
321+
322+
service::insert_log(3, "", user_id, auth_id, &client, &state).await?;
323+
324+
Ok(HttpResponse::Ok().body(""))
325+
}

src/api/authorizations/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
pub mod controller;
2+
pub mod model;
3+
pub mod service;
4+
5+
use chrono::prelude::*;
6+
7+
#[derive(Debug)]
8+
pub struct AuthBlacklist {
9+
pub id: Option<i32>,
10+
pub access_token_id: uuid::Uuid,
11+
pub access_token_exp: DateTime<Utc>,
12+
pub user_id: i32,
13+
}
14+
15+
#[derive(Debug, sqlx::FromRow)]
16+
pub struct Authorization {
17+
pub id: Option<i32>,
18+
pub user_id: Option<i32>,
19+
pub uuid: Option<uuid::Uuid>,
20+
pub client_type: Option<i16>,
21+
pub refresh_token: Option<uuid::Uuid>,
22+
pub create_time: Option<DateTime<Utc>>,
23+
pub update_time: Option<DateTime<Utc>>,
24+
pub last_refresh_time: Option<DateTime<Utc>>,
25+
pub access_token_id: Option<uuid::Uuid>,
26+
pub access_token_exp: Option<DateTime<Utc>>,
27+
pub access_token_iat: Option<DateTime<Utc>>,
28+
pub is_enabled: Option<i16>,
29+
}
30+
31+
#[derive(Debug)]
32+
pub struct AuthorizationInfo {
33+
pub id: i32,
34+
pub scopes: Vec<String>,
35+
}

0 commit comments

Comments
 (0)