这是CaiYouHui前端,一个关于flutter的安卓app,前端使用flutter实现

auth_repository.dart 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. import 'dart:convert';
  2. import 'package:shared_preferences/shared_preferences.dart';
  3. import '../../core/constants/api_constants.dart';
  4. import '../../core/utils/crypto_utils.dart';
  5. import '../datasources/remote/api_client.dart';
  6. import '../models/auth/secure_login_request.dart';
  7. import '../models/user.dart';
  8. import '../models/auth/login_request.dart';
  9. import '../models/auth/register_request.dart';
  10. import '../models/auth/token_response.dart';
  11. import '../models/api_response.dart';
  12. class AuthRepository {
  13. final ApiClient _apiClient;
  14. final SharedPreferences _prefs;
  15. AuthRepository({
  16. required ApiClient apiClient,
  17. required SharedPreferences prefs,
  18. }) : _apiClient = apiClient, _prefs = prefs;
  19. // 登录(需要使用安全登录替代)保存双token
  20. Future<ApiResponse<User>> login(LoginRequest request) async {
  21. try {
  22. final response = await _apiClient.post(
  23. ApiConstants.getLoginUrl(),
  24. request.toJson(),
  25. withAuth: false, // 登录不需要认证头
  26. );
  27. if (response.statusCode == 200) {
  28. final tokenResponse = TokenResponse.fromJson(
  29. json.decode(response.body)
  30. );
  31. // 保存access_token和refresh_token
  32. await _apiClient.saveTokens(
  33. tokenResponse.accessToken,
  34. tokenResponse.refreshToken,
  35. );
  36. // 保存用户数据
  37. await _prefs.setString(
  38. 'user_data',
  39. json.encode(tokenResponse.user.toJson()),
  40. );
  41. return ApiResponse<User>(
  42. success: true,
  43. message: '登录成功',
  44. data: tokenResponse.user,
  45. );
  46. } else {
  47. final error = json.decode(response.body);
  48. return ApiResponse<User>(
  49. success: false,
  50. message: error['detail'] ?? '登录失败',
  51. );
  52. }
  53. } catch (e) {
  54. return ApiResponse<User>(
  55. success: false,
  56. message: '登录失败: $e',
  57. );
  58. }
  59. }
  60. // 注册(需要使用安全注册替代)保存双token
  61. Future<ApiResponse<User>> register(RegisterRequest request) async {
  62. try {
  63. final response = await _apiClient.post(
  64. ApiConstants.getRegisterUrl(),
  65. request.toJson(),
  66. withAuth: false,
  67. );
  68. if (response.statusCode == 201) {
  69. final tokenResponse = TokenResponse.fromJson(
  70. json.decode(response.body)
  71. );
  72. // 保存双token
  73. await _apiClient.saveTokens(
  74. tokenResponse.accessToken,
  75. tokenResponse.refreshToken,
  76. );
  77. // 保存用户数据
  78. await _prefs.setString(
  79. 'user_data',
  80. json.encode(tokenResponse.user.toJson()),
  81. );
  82. return ApiResponse<User>(
  83. success: true,
  84. message: '注册成功',
  85. data: tokenResponse.user,
  86. );
  87. } else {
  88. final error = json.decode(response.body);
  89. return ApiResponse<User>(
  90. success: false,
  91. message: error['detail'] ?? '注册失败',
  92. );
  93. }
  94. } catch (e) {
  95. return ApiResponse<User>(
  96. success: false,
  97. message: '注册失败: $e',
  98. );
  99. }
  100. }
  101. // 安全登录方法
  102. Future<ApiResponse<User>> secureLogin(LoginRequest request) async {
  103. try {
  104. // 创建安全登录请求
  105. final secureRequest = SecureLoginRequest.create(
  106. username: request.username,
  107. password: request.password,
  108. );
  109. final response = await _apiClient.post(
  110. ApiConstants.getLoginUrl(),
  111. secureRequest.toJson(),
  112. withAuth: false,
  113. );
  114. // ... 处理响应
  115. if (response.statusCode == 200) {
  116. final tokenResponse = TokenResponse.fromJson(
  117. json.decode(response.body)
  118. );
  119. // 保存access_token和refresh_token
  120. await _apiClient.saveTokens(
  121. tokenResponse.accessToken,
  122. tokenResponse.refreshToken,
  123. );
  124. // 保存用户数据
  125. await _prefs.setString(
  126. 'user_data',
  127. json.encode(tokenResponse.user.toJson()),
  128. );
  129. return ApiResponse<User>(
  130. success: true,
  131. message: '登录成功',
  132. data: tokenResponse.user,
  133. );
  134. } else {
  135. final error = json.decode(response.body);
  136. return ApiResponse<User>(
  137. success: false,
  138. message: error['detail'] ?? '登录失败',
  139. );
  140. }
  141. } catch (e) {
  142. return ApiResponse<User>(
  143. success: false,
  144. message: '登录失败: $e',
  145. );
  146. }
  147. }
  148. // 安全注册方法
  149. Future<ApiResponse<User>> secureRegister(RegisterRequest request) async {
  150. try {
  151. // 创建安全注册数据
  152. final salt = CryptoUtils.generateSalt();
  153. final passwordHash = CryptoUtils.sha256Hash(request.password + salt);
  154. final registerData = {
  155. 'username': request.username,
  156. 'email': request.email,
  157. 'password_hash': passwordHash,
  158. 'salt': salt,
  159. 'full_name': request.fullName,
  160. 'password_confirm': request.passwordConfirm,
  161. };
  162. final response = await _apiClient.post(
  163. ApiConstants.getRegisterUrl(),
  164. registerData,
  165. withAuth: false,
  166. );
  167. // ... 处理响应
  168. if (response.statusCode == 201) {
  169. final tokenResponse = TokenResponse.fromJson(
  170. json.decode(response.body)
  171. );
  172. // 保存双token
  173. await _apiClient.saveTokens(
  174. tokenResponse.accessToken,
  175. tokenResponse.refreshToken,
  176. );
  177. // 保存用户数据
  178. await _prefs.setString(
  179. 'user_data',
  180. json.encode(tokenResponse.user.toJson()),
  181. );
  182. return ApiResponse<User>(
  183. success: true,
  184. message: '注册成功',
  185. data: tokenResponse.user,
  186. );
  187. } else {
  188. final error = json.decode(response.body);
  189. return ApiResponse<User>(
  190. success: false,
  191. message: error['detail'] ?? '注册失败',
  192. );
  193. }
  194. } catch (e) {
  195. return ApiResponse<User>(
  196. success: false,
  197. message: '注册失败: $e',
  198. );
  199. }
  200. }
  201. // 获取当前用户
  202. Future<ApiResponse<User>> getCurrentUser() async {
  203. try {
  204. final response = await _apiClient.get(
  205. ApiConstants.getCurrentUserUrl(),
  206. withAuth: true,
  207. );
  208. if (response.statusCode == 200) {
  209. final userData = json.decode(response.body);
  210. final user = User.fromJson(userData);
  211. // 更新本地用户数据
  212. await _prefs.setString('user_data', json.encode(user.toJson()));
  213. return ApiResponse<User>(
  214. success: true,
  215. message: '获取成功',
  216. data: user,
  217. );
  218. } else if (response.statusCode == 401) {
  219. // Token过期,尝试刷新
  220. final refreshResult = await _refreshToken();
  221. if (refreshResult) {
  222. // 刷新成功,重新获取用户信息
  223. return await getCurrentUser();
  224. } else {
  225. return ApiResponse<User>(
  226. success: false,
  227. message: '登录已过期,请重新登录',
  228. );
  229. }
  230. } else {
  231. return ApiResponse<User>(
  232. success: false,
  233. message: '获取用户信息失败',
  234. );
  235. }
  236. } catch (e) {
  237. return ApiResponse<User>(
  238. success: false,
  239. message: '获取失败: $e',
  240. );
  241. }
  242. }
  243. // 刷新Token
  244. Future<bool> _refreshToken() async {
  245. try {
  246. final refreshToken = _apiClient.getRefreshToken();
  247. if (refreshToken == null || refreshToken.isEmpty) {
  248. return false;
  249. }
  250. final response = await _apiClient.post(
  251. ApiConstants.getRefreshUrl(), // 需要添加这个常量
  252. {'refresh_token': refreshToken},
  253. withAuth: false,
  254. );
  255. if (response.statusCode == 200) {
  256. final data = json.decode(response.body);
  257. final newAccessToken = data['access_token'];
  258. final newRefreshToken = data['refresh_token'];
  259. // 保存新的tokens
  260. await _apiClient.saveTokens(newAccessToken, newRefreshToken);
  261. return true;
  262. }
  263. return false;
  264. } catch (e) {
  265. return false;
  266. }
  267. }
  268. // 登出
  269. Future<ApiResponse<bool>> logout() async {
  270. try {
  271. // 获取refresh_token
  272. final refreshToken = _apiClient.getRefreshToken();
  273. if (refreshToken == null || refreshToken.isEmpty) {
  274. // 没有refresh_token,只清除本地数据
  275. await _clearLocalData();
  276. return ApiResponse<bool>(
  277. success: true,
  278. message: '已退出登录',
  279. data: true,
  280. );
  281. }
  282. // 发送登出请求到服务器
  283. final response = await _apiClient.post(
  284. ApiConstants.getLogoutUrl(),
  285. {'refresh_token': refreshToken},
  286. withAuth: true, // 需要access_token认证头
  287. );
  288. // 无论服务器响应如何,都清除本地数据
  289. await _clearLocalData();
  290. if (response.statusCode == 200) {
  291. return ApiResponse<bool>(
  292. success: true,
  293. message: '已成功退出登录',
  294. data: true,
  295. );
  296. } else {
  297. // 服务器登出失败,但本地已清除
  298. return ApiResponse<bool>(
  299. success: true,
  300. message: '已退出登录(服务器通信失败)',
  301. data: true,
  302. );
  303. }
  304. } catch (e) {
  305. // 网络异常,仍然清除本地数据
  306. await _clearLocalData();
  307. return ApiResponse<bool>(
  308. success: true,
  309. message: '已退出登录(网络异常)',
  310. data: true,
  311. );
  312. }
  313. }
  314. // 清除本地数据
  315. Future<void> _clearLocalData() async {
  316. await _apiClient.clearTokens();
  317. await _prefs.remove('user_data');
  318. }
  319. // 检查登录状态
  320. Future<bool> isLoggedIn() async {
  321. // 检查是否有access_token
  322. if (!_apiClient.isLoggedIn()) {
  323. return false;
  324. }
  325. // 可以进一步验证token是否有效
  326. try {
  327. final response = await _apiClient.get(
  328. ApiConstants.getCurrentUserUrl(),
  329. withAuth: true,
  330. );
  331. return response.statusCode == 200;
  332. } catch (e) {
  333. return false;
  334. }
  335. }
  336. }