auth.py 13 KB

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