auth.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. use axum::{extract::{Query, State}, response::Redirect};
  2. use crate::{AppState, datasource::Datasource, log, LogLevel::*};
  3. use serde::Deserialize;
  4. const APPID: &str = "wx664469117500d259";
  5. // // const APPSECRET : &str = "11ec6e6d4b9062f00a4fc08c529293be";
  6. // const APPSECRET : &str = "a78ad639142e34447bdff5054b2f35a8";
  7. // // const APPSECRET : &str = "wx664469117500d259";
  8. #[derive(serde::Deserialize)]
  9. pub struct QueryParams {
  10. code: String,
  11. state: String,
  12. }
  13. #[allow(dead_code)]
  14. #[derive(Deserialize)]
  15. pub struct WxGetToken{
  16. access_token: String,
  17. expires_in: usize,
  18. refresh_token: String,
  19. openid: String,
  20. scope:String
  21. }
  22. /*
  23. https://open.weixin.qq.com/connect/oauth2/authorize?appid=
  24. &redirect_uri=http://192.168.24.101:3000/auth&response_type=code&scope=snsapi_base&state=1#wechat_redirect
  25. wx664469117500d259
  26. wxdf57fde70a442828
  27. */
  28. #[allow(dead_code)]
  29. #[derive(Deserialize)]
  30. pub struct WxUserInfo{
  31. openid: String,
  32. nickname: String,
  33. sex: u16,
  34. headimgurl: String,
  35. }
  36. pub async fn auth(
  37. // headers: HeaderMap,
  38. State(state): State<AppState>,
  39. Query(q): Query<QueryParams>
  40. ) -> Redirect {
  41. /*
  42. 从公众号跳转时会访问 http://{{host}}/api/auth?code={code}&state={state}
  43. code为获取token所需中间码
  44. state为授权链接的state用于防止越权,用途不明
  45. */
  46. // 访问以获取微信token
  47. log(Debug, format!("authing code+:`{}` state:`{}`",q.code,q.state));
  48. // reqwest::Client::builder().build()?.get(url).send().await;
  49. let webcli = match reqwest::Client::builder()
  50. .timeout(std::time::Duration::from_secs(3))
  51. .build()
  52. {
  53. Ok(cli) => cli,
  54. Err(e) => {
  55. log(Warning, format!("rewest client builder failed:: {e}"));
  56. return Redirect::to(format!("{}/null",crate::WEBP).as_str())
  57. }
  58. };
  59. log(Debug, format!("web client init"));
  60. // webcli.get("url").send();
  61. let a = match webcli.get(format!(
  62. "https://api.weixin.qq.com/sns/oauth2/access_token?appid={}&secret={}&code={}&grant_type=authorization_code",
  63. // "https://iotplatform.worldflying.cn/api/wx/auth?appid={}&secret={}&code={}&state={}&grant_type=authorization_code",
  64. APPID,
  65. state.appsecret,
  66. q.code,
  67. // q.state
  68. )).send().await {
  69. Ok(a) => {
  70. log(Debug, format!("dealing"));
  71. let v = a.text().await;
  72. // log(Debuging, format!("dealing\n{:?}",v));
  73. let v = if let Ok(v)=v {v}else {
  74. return Redirect::to(format!("{}/null",crate::WEBP).as_str());
  75. };
  76. match
  77. serde_json::from_str::<WxGetToken>(v.as_str())
  78. {
  79. Ok(b) => b,
  80. Err(e) =>{
  81. log(Error, format!("redirect 2 {e}, raw:{v}"));
  82. return Redirect::to(format!("{}/null",crate::WEBP).as_str())
  83. }
  84. }
  85. },
  86. Err(e) => {
  87. log(Debug, format!("redirect 0 {e}"));
  88. return Redirect::to(format!("{}/null",crate::WEBP).as_str())
  89. }
  90. };
  91. // let host = headers.get("host")
  92. // .and_then(|hv| hv.to_str().ok())
  93. // .unwrap_or("localhost:3000");
  94. log(Debuging, format!("in authing"));
  95. let token=super::token(32);
  96. let mut bindfail:Option<&str>=None;
  97. let res = super::check_openid(&state, a.openid.clone()).await;
  98. if let Ok((id,mqid)) = res{
  99. // 用户已经注册 函数末尾发生跳转到主页
  100. if let Err(e) = state.db_lite.execute("update user set token=? where id=?", (token.clone(), id)).await{
  101. println!("{e}");
  102. };
  103. // 已注册时绑定设备
  104. if let Some(sn)= q.state.strip_prefix("did="){
  105. log(Debuging, format!("got did: {}",sn));
  106. match state.db_lite.execute("update device set belongto=?,name=sn where sn=? and belongto=0", (id,sn)).await{
  107. Err(e) => {println!("{e}");bindfail=Some("&bindfail=true");}
  108. Ok(n)=>{
  109. if n!=0{
  110. if let Ok(did) = state.db_lite.query("select id from device where sn=?", [sn], |r|{r.get::<usize,i64>(0)}).await{
  111. // if let Some(did)=state.db_lite.last_insert_rowid().await.ok(){ // 错误位置:update不会给last_insert_rowid
  112. if let Err(e) = state.db_lite.execute("insert into map_user_device (uid, did)values(?,?)", (id,did)).await{
  113. println!("{e}");
  114. } else{
  115. bindfail=Some("&bindfail=false");
  116. if let Some(mq) = state.mq_detail{
  117. if let Err(e)=mq.db_mq_map.execute(
  118. "insert into pub (clientid,topic)values(?,?),(?,?)",
  119. (mqid.clone(),format!("{}/cmd",sn),mqid.clone(),format!("/wf/Iot/device/{sn}"))).await{
  120. log(Warning, format!("on register with device: {e}"));
  121. };
  122. if let Err(e)=mq.db_mq_map.execute(
  123. "insert into sub (clientid,topic)values(?,?),(?,?)",
  124. (mqid.clone(),format!("{}/state",sn),mqid.clone(),format!("/wf/Iot/client/{sn}"))).await{
  125. log(Warning, format!("on register with device: {e}"));
  126. };
  127. }
  128. }
  129. }
  130. } else{
  131. bindfail=Some("&bindfail=true");
  132. };
  133. }
  134. }
  135. }
  136. } else if a.scope !="snsapi_userinfo"{
  137. // 用户未注册,跳转获取用户详细信息
  138. log(Debug, "send redirect for get uinfo".to_string());
  139. return Redirect::to(
  140. format!("https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx664469117500d259&redirect_uri=https%3A%2F%2Fwww.worldflying.cn%2Ftools%2Fwxredirect%2Fplc.html&response_type=code&scope=snsapi_userinfo&state={}#wechat_redirect",q.state).as_str()
  141. // format!("https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx664469117500d259&redirect_uri=https%3A%2F%2Fwww.worldflying.cn%2Ftools%2Fwxredirect%2Fiot.html&response_type=code&scope=snsapi_userinfo&state={}#wechat_redirect",q.state).as_str()
  142. )
  143. } else {
  144. // 带用户信息的重定向收取目标分支,保存用户数据并创建mqtt相关信息
  145. let b:WxUserInfo = if let Ok(b) = {if let Ok(b) = reqwest::get(format!(
  146. "https://api.weixin.qq.com/sns/userinfo?access_token={}&openid={}&lang=zh_CN",
  147. a.access_token,
  148. a.openid.clone()
  149. )).await{b.json().await} else {
  150. log(Info, "redirect 4".to_string());
  151. return Redirect::to(format!("{}/null",crate::WEBP).as_str())}}{b} else {
  152. log(Info, "redirect 5".to_string());
  153. return Redirect::to(format!("{}/null",crate::WEBP).as_str())};
  154. let mqid = format!("webu{}",super::token(5));
  155. if let Err(e) = state.db_lite.execute(
  156. "insert into user (uname,nickname,passwd,headimgurl,openid,sex,token,mqid) values(?,?,?,?,?,?,?,?)",
  157. (b.nickname.clone(),b.nickname.clone(),"",b.headimgurl,b.openid,b.sex,token.clone(),mqid.clone())).await{
  158. println!("{e}");
  159. }
  160. let uid = match state.db_lite.last_insert_rowid().await{
  161. Ok(id) => id,
  162. Err(e) => {
  163. println!("{e}");
  164. 0
  165. }
  166. };
  167. if let Err(e) = state.db_lite.execute(
  168. "insert into area (name,sup,createby)values(?,0,?)",
  169. (format!("{}的家",b.nickname.clone()),uid)
  170. ).await{println!("{e}")};
  171. let area_sup: i64;
  172. if let Err(e) = state.db_lite.execute(
  173. "insert into map_user_area (uid,aid)values(?,?)",
  174. [uid,{area_sup=match state.db_lite.last_insert_rowid().await{Ok(id)=>id,Err(e)=>{println!("{e}");0}};area_sup}]
  175. ).await{println!("{e}")};
  176. if let Err(e) = state.db_lite.execute(
  177. "insert into area (name,sup,createby)values('客厅',?,?)",
  178. [area_sup,uid]
  179. ).await{println!("{e}")};
  180. // 未注册时绑定设备
  181. if let Some(sn)= q.state.strip_prefix("did="){
  182. log(Debuging, format!("got did: {}",sn));
  183. match state.db_lite.execute("update device set belongto=?,name=sn where sn=? and belongto=0", (uid,sn)).await{
  184. Err(e)=>{println!("{e}");bindfail=Some("&bindfail=true");},
  185. Ok(n ) => {
  186. if n!=0{ // 设备绑定成功,update至少一条则记录并订阅
  187. bindfail=Some("&bindfail=false");
  188. log(Debuging, format!("got updated {n}"));
  189. if let Ok(did) = state.db_lite.query(
  190. "select id from device where sn=? and belongto=?",
  191. (sn,uid), |r|{r.get::<usize,i64>(0)}).await{
  192. if let Err(e) = state.db_lite.execute(
  193. "insert into map_user_device (uid, did)values(?,?)",
  194. (uid,did)).await{
  195. println!("{e}");
  196. }
  197. if let Some(mq) = state.mq_detail{
  198. if let Err(e)=mq.db_mq_map.execute(
  199. "insert into pub (clientid,topic)values(?,?),(?,?)",
  200. (mqid.clone(),format!("{}/cmd",sn),mqid.clone(),format!("/wf/Iot/device/{sn}"))).await{
  201. log(Warning, format!("on register with device: {e}"));
  202. };
  203. if let Err(e)=mq.db_mq_map.execute(
  204. "insert into sub (clientid,topic)values(?,?),(?,?)",
  205. (mqid.clone(),format!("{}/state",sn),mqid.clone(),format!("/wf/Iot/client/{sn}"))).await{
  206. log(Warning, format!("on register with device: {e}"));
  207. };
  208. }
  209. }
  210. } else{
  211. bindfail=Some("&bindfail=true");
  212. }
  213. }
  214. };
  215. }
  216. log(Debug, "register success".to_string());
  217. // 注册成功 函数末尾发生跳转到主页
  218. // return Redirect::to(format!("http://124.222.106.170/plc_ctrl?token={}#{}",token.clone(),q.state).as_str());
  219. };
  220. // request::Request::get("body");
  221. // log(Info, format!("bad happend {:?}",res));
  222. log(Debug, format!("redirect back with token, state: {}",q.state));
  223. return Redirect::to(format!("{}?token={}{}{}",
  224. crate::WEBP,token.clone(),
  225. if q.state.starts_with("?"){format!("#{}",q.state)}else{"".to_string()},
  226. if let Some(s)= bindfail{s}else{""}).as_str()
  227. );
  228. // return Redirect::to(format!("{}?token={}",crate::WEBP,token.clone()).as_str());
  229. }