auth_controller.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. from flask import request, g
  2. from flask_restx import Resource, Namespace, fields
  3. from app.service.user_service import UserService
  4. from app.utils.jwt_utils import JWTUtils, token_required
  5. from app.utils.logger import Logger, log_request_info, ErrorHandler
  6. from app import db
  7. # 创建认证API命名空间
  8. api = Namespace('auth', description='用户认证相关操作')
  9. # 定义API模型
  10. login_model = api.model('Login', {
  11. 'username': fields.String(required=True, description='用户名'),
  12. 'password': fields.String(required=True, description='密码')
  13. })
  14. register_model = api.model('Register', {
  15. 'username': fields.String(required=True, description='用户名'),
  16. 'email': fields.String(required=True, description='邮箱'),
  17. 'password': fields.String(required=True, description='密码')
  18. })
  19. change_password_model = api.model('ChangePassword', {
  20. 'old_password': fields.String(required=True, description='当前密码'),
  21. 'new_password': fields.String(required=True, description='新密码')
  22. })
  23. user_model = api.model('User', {
  24. 'id': fields.Integer(readOnly=True, description='用户ID'),
  25. 'username': fields.String(description='用户名'),
  26. 'email': fields.String(description='邮箱'),
  27. 'is_active': fields.Boolean(description='是否激活'),
  28. 'created_at': fields.String(description='创建时间'),
  29. 'updated_at': fields.String(description='更新时间')
  30. })
  31. token_response_model = api.model('TokenResponse', {
  32. 'access_token': fields.String(description='访问令牌'),
  33. 'token_type': fields.String(description='令牌类型'),
  34. 'expires_in': fields.Integer(description='过期时间(秒)'),
  35. 'user': fields.Nested(user_model, description='用户信息')
  36. })
  37. @api.route('/register')
  38. class RegisterResource(Resource):
  39. @api.expect(register_model, validate=True)
  40. @api.marshal_with(user_model, code=201)
  41. @api.response(400, '注册失败')
  42. @log_request_info
  43. def post(self):
  44. """用户注册"""
  45. data = request.get_json()
  46. service = UserService(db.session)
  47. # 记录注册尝试
  48. Logger.info("用户注册尝试", {
  49. 'username': data.get('username'),
  50. 'email': data.get('email'),
  51. 'ip_address': request.remote_addr,
  52. 'user_agent': request.headers.get('User-Agent')
  53. })
  54. try:
  55. new_user = service.create_user(
  56. username=data['username'],
  57. email=data['email'],
  58. password=data['password']
  59. )
  60. # 记录注册成功
  61. Logger.info("用户注册成功", {
  62. 'user_id': new_user.id,
  63. 'username': new_user.username,
  64. 'email': new_user.email,
  65. 'ip_address': request.remote_addr
  66. })
  67. return new_user.to_dict(), 201
  68. except ValueError as e:
  69. # 记录注册失败
  70. Logger.warning("用户注册失败", {
  71. 'username': data.get('username'),
  72. 'email': data.get('email'),
  73. 'error': str(e),
  74. 'ip_address': request.remote_addr
  75. })
  76. api.abort(400, str(e))
  77. @api.route('/login')
  78. class LoginResource(Resource):
  79. @api.expect(login_model, validate=True)
  80. @api.marshal_with(token_response_model)
  81. @api.response(401, '登录失败')
  82. @log_request_info
  83. def post(self):
  84. """用户登录"""
  85. data = request.get_json()
  86. service = UserService(db.session)
  87. # 记录登录尝试
  88. Logger.info("用户登录尝试", {
  89. 'username': data.get('username'),
  90. 'ip_address': request.remote_addr,
  91. 'user_agent': request.headers.get('User-Agent')
  92. })
  93. try:
  94. user = service.authenticate_user(
  95. username=data['username'],
  96. password=data['password']
  97. )
  98. # 生成永不过期的JWT token(expires_in=0)
  99. token = JWTUtils.generate_token(user.id, user.username, expires_in=0)
  100. # 记录登录成功
  101. Logger.info("用户登录成功", {
  102. 'user_id': user.id,
  103. 'username': user.username,
  104. 'ip_address': request.remote_addr
  105. })
  106. return {
  107. 'access_token': token,
  108. 'token_type': 'Bearer',
  109. 'expires_in': 0,
  110. 'user': user.to_dict_public()
  111. }
  112. except ValueError as e:
  113. # 记录登录失败
  114. Logger.warning("用户登录失败", {
  115. 'username': data.get('username'),
  116. 'error': str(e),
  117. 'ip_address': request.remote_addr
  118. })
  119. api.abort(401, str(e))
  120. @api.route('/me')
  121. class UserProfileResource(Resource):
  122. @token_required
  123. @api.marshal_with(user_model)
  124. @log_request_info
  125. def get(self):
  126. """获取当前用户信息"""
  127. Logger.info("获取用户信息", {
  128. 'user_id': g.current_user.id,
  129. 'username': g.current_user.username
  130. })
  131. return g.current_user.to_dict()
  132. @token_required
  133. @api.expect(api.model('UpdateProfile', {
  134. 'username': fields.String(description='用户名'),
  135. 'email': fields.String(description='邮箱')
  136. }))
  137. @api.marshal_with(user_model)
  138. @api.response(400, '更新失败')
  139. @log_request_info
  140. def put(self):
  141. """更新当前用户信息"""
  142. data = request.get_json()
  143. service = UserService(db.session)
  144. Logger.info("用户信息更新尝试", {
  145. 'user_id': g.current_user.id,
  146. 'username': g.current_user.username,
  147. 'new_username': data.get('username'),
  148. 'new_email': data.get('email')
  149. })
  150. try:
  151. updated_user = service.update_user(
  152. user_id=g.current_user.id,
  153. username=data.get('username'),
  154. email=data.get('email')
  155. )
  156. Logger.info("用户信息更新成功", {
  157. 'user_id': updated_user.id,
  158. 'username': updated_user.username,
  159. 'email': updated_user.email
  160. })
  161. return updated_user.to_dict()
  162. except ValueError as e:
  163. Logger.warning("用户信息更新失败", {
  164. 'user_id': g.current_user.id,
  165. 'error': str(e)
  166. })
  167. api.abort(400, str(e))
  168. @api.route('/change-password')
  169. class ChangePasswordResource(Resource):
  170. @token_required
  171. @api.expect(change_password_model, validate=True)
  172. @api.response(200, '密码修改成功')
  173. @api.response(400, '密码修改失败')
  174. @log_request_info
  175. def post(self):
  176. """修改密码"""
  177. data = request.get_json()
  178. service = UserService(db.session)
  179. Logger.info("用户密码修改尝试", {
  180. 'user_id': g.current_user.id,
  181. 'username': g.current_user.username
  182. })
  183. try:
  184. service.change_password(
  185. user_id=g.current_user.id,
  186. old_password=data['old_password'],
  187. new_password=data['new_password']
  188. )
  189. Logger.info("用户密码修改成功", {
  190. 'user_id': g.current_user.id,
  191. 'username': g.current_user.username
  192. })
  193. return {'message': '密码修改成功'}, 200
  194. except ValueError as e:
  195. Logger.warning("用户密码修改失败", {
  196. 'user_id': g.current_user.id,
  197. 'username': g.current_user.username,
  198. 'error': str(e)
  199. })
  200. api.abort(400, str(e))
  201. @api.route('/logout')
  202. class LogoutResource(Resource):
  203. @token_required
  204. @api.response(200, '登出成功')
  205. @log_request_info
  206. def post(self):
  207. """用户登出(客户端需要删除token)"""
  208. Logger.info("用户登出", {
  209. 'user_id': g.current_user.id,
  210. 'username': g.current_user.username
  211. })
  212. return {'message': '登出成功'}, 200
  213. @api.route('/verify-token')
  214. class VerifyTokenResource(Resource):
  215. @token_required
  216. @api.marshal_with(user_model)
  217. @log_request_info
  218. def get(self):
  219. """验证token有效性"""
  220. Logger.info("Token验证", {
  221. 'user_id': g.current_user.id,
  222. 'username': g.current_user.username
  223. })
  224. return g.current_user.to_dict()
  225. @api.route('/permanent-token')
  226. class PermanentTokenResource(Resource):
  227. @token_required
  228. @api.marshal_with(token_response_model)
  229. @log_request_info
  230. def post(self):
  231. """使用当前有效token换取长期有效token(无过期)"""
  232. user = g.current_user
  233. Logger.info("生成长期Token", {
  234. 'user_id': user.id,
  235. 'username': user.username
  236. })
  237. token = JWTUtils.generate_token(user.id, user.username, expires_in=0)
  238. return {
  239. 'access_token': token,
  240. 'token_type': 'Bearer',
  241. 'expires_in': 0,
  242. 'user': user.to_dict_public()
  243. }