auth.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. # -*- coding: utf-8 -*-
  2. from fastapi import APIRouter, Depends, Request, Header, Form, Body
  3. from fastapi.responses import FileResponse, StreamingResponse
  4. from sqlalchemy.orm import Session
  5. from fastapi.responses import JSONResponse
  6. from database import get_db
  7. from utils import *
  8. from utils.vcode import *
  9. from utils.redis_util import *
  10. import base64
  11. from common.const import *
  12. from io import BytesIO
  13. from utils.StripTagsHTMLParser import *
  14. from common import security
  15. from datetime import timedelta
  16. from common.security import verify_password
  17. from utils import ase_utils
  18. from common.auth_user import *
  19. from common import YzyApi, TassApi
  20. from models import *
  21. from urllib.parse import quote
  22. from exceptions import *
  23. import traceback
  24. from common.db import db_czrz
  25. from common.enc import mpfun, sys_user_data
  26. router = APIRouter()
  27. @router.get('/tenant/list')
  28. async def tenant_list(
  29. request: Request,
  30. db: Session = Depends(get_db)
  31. ):
  32. return {
  33. "code": 200,
  34. "msg": "操作成功",
  35. "data": {
  36. "tenantEnabled": False,
  37. "voList": []
  38. }
  39. }
  40. def buildVerificationCodeRedisKey(uuid: str) -> str:
  41. return VERIFICATION_CODE_REDIS_PREFIX.format(uuid)
  42. @router.get('/code')
  43. async def code(
  44. request: Request,
  45. db: Session = Depends(get_db)
  46. ):
  47. uuid_str = md5(new_guid())
  48. image, code = getVerifyCode()
  49. buf = BytesIO()
  50. image.save(buf, 'png')
  51. img_data = buf.getvalue()
  52. image_bytes_io = BytesIO(img_data)
  53. image_bytes_io.seek(0)
  54. image_bytes = image_bytes_io.read()
  55. base64_str = base64.b64encode(image_bytes).decode('utf-8')
  56. redis_key = "kaptcha_" + buildVerificationCodeRedisKey(uuid_str)
  57. redis_set(redis_key, code)
  58. return {
  59. "code": 200,
  60. "msg": "操作成功",
  61. "data": {
  62. "captchaEnabled": True,
  63. "uuid": uuid_str,
  64. "img": base64_str
  65. }
  66. }
  67. @router.post('/login')
  68. async def login(
  69. request: Request,
  70. db: Session = Depends(get_db),
  71. data: dict = Depends(remove_xss_json)
  72. ):
  73. try:
  74. # tenantId = data['tenantId']
  75. username = data['username']
  76. password = data['password']
  77. # rememberMe = data['rememberMe']
  78. uuid_str = data['uuid']
  79. code = data['code']
  80. # clientId = data['clientId']
  81. fromSystem = ''
  82. if 'fromSystem' in data:
  83. fromSystem = data['fromSystem']
  84. # 仅为了可能的兼容
  85. clientId = "e5cd7e4891bf95d1d19206ce24a7b32e"
  86. grantType = "password"
  87. uuid = buildVerificationCodeRedisKey(uuid_str)
  88. redis_key = "kaptcha_" + uuid
  89. redis_code = redis_get(redis_key)
  90. if code is None or code != redis_code:
  91. raise AppException(500, "图片验证码不正确")
  92. redis_login_key = "login_user_" + username
  93. login_error_times = redis_get(redis_login_key)
  94. if login_error_times is None:
  95. login_error_times = 0
  96. else:
  97. login_error_times = int(login_error_times)
  98. if login_error_times >= 5:
  99. raise AppException(500, "登录错误多,请5分钟后再尝试!")
  100. # 对用户账号进行密码机接口加密处理
  101. username = mpfun.enc_data(username)
  102. password = ase_utils.aesDecrypt(uuid_str, password)
  103. logger.info('userpass: {}', password)
  104. row = db.query(SysUser).filter(SysUser.user_name == username).first()
  105. if sys_user_data.sign_valid_row(row) == False:
  106. raise AppException(500, "系统用户表验证异常,已被非法篡改")
  107. if row is None:
  108. login_error_times = login_error_times + 1
  109. redis_set_with_time(redis_login_key, str(login_error_times), 300)
  110. raise AppException(500, "帐号或者密码错误")
  111. logger.info('row.password: {}', row.password)
  112. # bcrypt 加密校验
  113. # if verify_password(password, row.password) == False:
  114. # 密码机加密校验
  115. if mpfun.enc_data(password) != row.password:
  116. login_error_times = login_error_times + 1
  117. redis_set_with_time(redis_login_key, str(login_error_times), 300)
  118. raise AppException(500, "帐号或者密码错误")
  119. # 校验账号是否停用
  120. if row.status != "0":
  121. raise AppException(500, "帐号已停用")
  122. # 校验长期(超过1个月)未使用的账号和及开通后未及时(如72小时)修改初始密码的账号做清除
  123. login_date = row.login_date
  124. if row.login == 0:
  125. # 计算初始化的时间和当前时间相差的小时数
  126. diff_hour = (datetime.now() - login_date).seconds/3600
  127. if diff_hour > 72:
  128. raise AppException(500, "你的账号在开通后(72小时)内未登录及修改初始密码,账号已被锁定,请联系管理员处理,否则将被清除。")
  129. else:
  130. # 计算上次登录到当前时间的相差天数
  131. diff_day = (datetime.now() - login_date).days
  132. if diff_day > 30:
  133. raise AppException(500, "你的账号在超过30天未登录使用,账号已被锁定,请联系管理员处理,否则将被清除。")
  134. redis_set_with_time(redis_login_key, str(0), 1)
  135. user_id = str(row.user_id)
  136. auth = {
  137. "user_id": user_id,
  138. "user_name": mpfun.dec_data(row.user_name),
  139. "nick_name": row.nick_name,
  140. "is_yzy_user": "0"
  141. }
  142. request.session.update({
  143. 'user_auth': auth,
  144. 'user_auth_sign': data_auth_sign(auth),
  145. 'user_name': username
  146. })
  147. action = '登录'
  148. czrz = '后台管理登录成功'
  149. if fromSystem == 'yjdp':
  150. action = '应急一张图'
  151. czrz = '大屏登录成功'
  152. db_czrz.log_username(db, row.user_id, auth['user_name'], row.nick_name, action, czrz, request.client.host)
  153. row.login_date = datetime.now()
  154. row.login_ip = request.client.host
  155. row.login = row.login + 1
  156. db.commit()
  157. access_token_expires = timedelta(days = 5)
  158. access_token = security.create_access_token(
  159. data={"sub": user_id}, expires_delta = access_token_expires
  160. )
  161. refresh_token_expires = timedelta(days = 10)
  162. refresh_token = security.create_access_token(
  163. data={"sub": user_id}, expires_delta = refresh_token_expires
  164. )
  165. return {
  166. "code": 200,
  167. "msg": "操作成功",
  168. "data": {
  169. "access_token": access_token,
  170. "refresh_token": refresh_token,
  171. "expire_in": 7200,
  172. "refresh_expire_in": 7200,
  173. "client_id": clientId,
  174. "scope": "",
  175. "openid": ""
  176. }
  177. }
  178. except AppException as e:
  179. return {
  180. "code": e.code,
  181. "msg": e.msg
  182. }
  183. except Exception as e:
  184. traceback.print_exc()
  185. return {
  186. "code": 500,
  187. "msg": "帐号或者密码错误"
  188. }
  189. @router.post('/logout')
  190. async def logout(
  191. request: Request,
  192. user: AuthUser = Depends(get_auth_user),
  193. db: Session = Depends(get_db)
  194. ):
  195. logger.info("logout ok")
  196. request.session.clear()
  197. try:
  198. # db_czrz.log(db, user, "退出", "后台管理退出成功", request.client.host)
  199. if user.is_yzy_user == 1:
  200. logout_url = settings.TYRZ_LOGOUT.format(settings.TYRZ_CLIENT_ID) + quote(settings.HOME_URL+"/yjzp/")
  201. logger.info(logout_url)
  202. else:
  203. logout_url = settings.HOME_URL + "/yjzp/"
  204. except Exception as e:
  205. traceback.print_exc()
  206. logout_url = settings.HOME_URL+"/yjzp/"
  207. return {
  208. "code": 200,
  209. "msg": "退出成功",
  210. "data": logout_url
  211. }
  212. # 小屏专用
  213. @router.post('/yzy/callback')
  214. async def yzy(
  215. request: Request,
  216. db: Session = Depends(get_db),
  217. data: dict = Depends(remove_xss_json)
  218. ):
  219. code = data['code']
  220. state = data['state']
  221. state_str = base64.b64decode(state).decode('utf-8')
  222. state_json = json.loads(state_str)
  223. logger.info("code:{}, {}", code, state_json)
  224. resp = YzyApi.get_user_info(code)
  225. if resp['errcode'] != 0:
  226. return {
  227. "code": 500,
  228. "msg": "Code异常"
  229. }
  230. user_id = resp['UserId']
  231. row = db.query(YzyOrgUserEntity).filter(YzyOrgUserEntity.userid == user_id).first()
  232. if row is None:
  233. return {
  234. "code": 500,
  235. "msg": "user_id异常"
  236. }
  237. yzy_account = row.account
  238. row = db.query(SysUser).filter(SysUser.yzy_account == yzy_account).first()
  239. if row is None:
  240. return {
  241. "code": 500,
  242. "msg": "用户不是本系统用户"
  243. }
  244. user_id = str(row.user_id)
  245. access_token_expires = timedelta(seconds = 7200)
  246. access_token = security.create_access_token(
  247. data={"sub": user_id}, expires_delta = access_token_expires
  248. )
  249. refresh_token_expires = timedelta(seconds = 7200)
  250. refresh_token = security.create_access_token(
  251. data={"sub": user_id}, expires_delta = refresh_token_expires
  252. )
  253. return {
  254. "code": 200,
  255. "msg": "粤政易登录成功",
  256. "data": {
  257. "access_token": access_token,
  258. "refresh_token": refresh_token,
  259. "expire_in": 7200,
  260. "refresh_expire_in": 7200,
  261. "scope": "",
  262. "redirect_url": state_json['redirect_url']
  263. }
  264. }
  265. # 粤政易token登录
  266. @router.post('/yzylogin')
  267. async def login(
  268. request: Request,
  269. data: dict = Depends(remove_xss_json),
  270. db: Session = Depends(get_db)
  271. ):
  272. code = data['code']
  273. redis_key = "yzy_" + code
  274. user_id = redis_get(redis_key)
  275. if user_id is None:
  276. return {
  277. "code": 500,
  278. "msg": "用户不是本系统用户"
  279. }
  280. row = db.query(SysUser).filter(SysUser.user_id == int(user_id)).first()
  281. if row is None:
  282. return {
  283. "code": 500,
  284. "msg": "用户不是本系统用户"
  285. }
  286. user_id = str(row.user_id)
  287. auth = {
  288. "user_id": user_id,
  289. "user_name": row.user_name,
  290. "nick_name": row.nick_name,
  291. "is_yzy_user": "1"
  292. }
  293. logger.info('auth {}', auth)
  294. request.session['user_auth'] = auth
  295. request.session['user_auth_sign'] = data_auth_sign(auth)
  296. request.session['user_name'] = row.user_name
  297. db_czrz.log_username(db, row.user_id, auth['user_name'], row.nick_name, "登录", "粤政易登录登录成功", request.client.host)
  298. row.login_date = datetime.now()
  299. row.login_ip = request.client.host
  300. row.login = row.login + 1
  301. db.commit()
  302. access_token_expires = timedelta(days = 5)
  303. access_token = security.create_access_token(
  304. data={"sub": user_id}, expires_delta = access_token_expires
  305. )
  306. refresh_token_expires = timedelta(days = 10)
  307. refresh_token = security.create_access_token(
  308. data={"sub": user_id}, expires_delta = refresh_token_expires
  309. )
  310. return {
  311. "code": 200,
  312. "msg": "操作成功",
  313. "data": {
  314. "access_token": access_token,
  315. "refresh_token": refresh_token,
  316. "expire_in": 7200,
  317. "refresh_expire_in": 7200,
  318. "client_id": 0,
  319. "scope": "",
  320. "openid": ""
  321. }
  322. }
  323. # USBKEY登录
  324. @router.post("/login_with_usbkey")
  325. def login_with_usbkey(
  326. request: Request,
  327. username: str = Body(...),
  328. keyID: str = Body(...),
  329. p7SignData: str = Body(...),
  330. p7SignValue: str = Body(...),
  331. db: Session = Depends(get_db)
  332. ):
  333. result = TassApi.verifyP7Sign(p7SignData, p7SignValue)
  334. if result is None:
  335. return {
  336. "code": 500,
  337. "msg": "证书验签失败",
  338. }
  339. logger.info('keyID: {}', keyID)
  340. logger.info('verifyP7Sign: {}', result)
  341. try:
  342. # 对用户账号进行密码机接口加密处理
  343. username = mpfun.enc_data(username)
  344. redis_login_key = "login_user_" + username
  345. login_error_times = redis_get(redis_login_key)
  346. if login_error_times is None:
  347. login_error_times = 0
  348. else:
  349. login_error_times = int(login_error_times)
  350. if login_error_times >= 5:
  351. raise AppException(500, "登录错误多,请5分钟后再尝试!")
  352. row = db.query(SysUser).filter(SysUser.user_name == username).first()
  353. if row is None:
  354. login_error_times = login_error_times + 1
  355. redis_set_with_time(redis_login_key, str(login_error_times), 300)
  356. raise AppException(500, "帐号或者密码错误")
  357. if sys_user_data.sign_valid_row(row) == False:
  358. raise AppException(500, "系统用户表验证异常,已被非法篡改")
  359. # 校验账号是否停用
  360. if row.status != "0":
  361. raise AppException(500, "帐号已停用")
  362. # 校验长期(超过1个月)未使用的账号和及开通后未及时(如72小时)修改初始密码的账号做清除
  363. login_date = row.login_date
  364. if row.login == 0:
  365. # 计算初始化的时间和当前时间相差的小时数
  366. diff_hour = (datetime.now() - login_date).seconds/3600
  367. if diff_hour > 72:
  368. raise AppException(500, "你的账号在开通后(72小时)内未登录及修改初始密码,账号已被锁定,请联系管理员处理,否则将被清除。")
  369. else:
  370. # 计算上次登录到当前时间的相差天数
  371. diff_day = (datetime.now() - login_date).days
  372. if diff_day > 30:
  373. raise AppException(500, "你的账号在超过30天未登录使用,账号已被锁定,请联系管理员处理,否则将被清除。")
  374. redis_set_with_time(redis_login_key, str(0), 1)
  375. user_id = str(row.user_id)
  376. auth = {
  377. "user_id": user_id,
  378. "user_name": mpfun.dec_data(row.user_name),
  379. "nick_name": row.nick_name,
  380. "is_yzy_user": "0"
  381. }
  382. logger.info('auth {}', auth)
  383. request.session['user_auth'] = auth
  384. request.session['user_auth_sign'] = data_auth_sign(auth)
  385. request.session['username'] = username
  386. db_czrz.log_username(db, row.user_id, auth['user_name'], row.nick_name, "登录", "后台USBKEY登录成功", request.client.host)
  387. row.login_date = datetime.now()
  388. row.login_ip = request.client.host
  389. row.login = row.login + 1
  390. db.commit()
  391. access_token_expires = timedelta(days = 5)
  392. access_token = security.create_access_token(
  393. data={"sub": user_id}, expires_delta = access_token_expires
  394. )
  395. refresh_token_expires = timedelta(days = 10)
  396. refresh_token = security.create_access_token(
  397. data={"sub": user_id}, expires_delta = refresh_token_expires
  398. )
  399. return {
  400. "code": 200,
  401. "msg": "操作成功",
  402. "data": {
  403. "access_token": access_token,
  404. "refresh_token": refresh_token,
  405. "expire_in": 7200,
  406. "refresh_expire_in": 7200,
  407. "client_id": "e5cd7e4891bf95d1d19206ce24a7b32e",
  408. "scope": "",
  409. "openid": ""
  410. }
  411. }
  412. except AppException as e:
  413. return {
  414. "code": e.code,
  415. "msg": e.msg
  416. }
  417. except Exception as e:
  418. traceback.print_exc()
  419. return {
  420. "code": 500,
  421. "msg": "帐号或者密码错误"
  422. }