Files
AutoJudge-Flutter/lib/providers/auth_provider.dart
2025-11-13 09:14:49 +08:00

274 lines
7.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/foundation.dart';
import '../models/user_credentials.dart';
import '../services/aufe_connection.dart';
/// Authentication state enum
enum AuthState { initial, loading, authenticated, unauthenticated, error }
/// Provider for managing authentication state and user sessions
///
/// Handles login, logout, session checking, and credential management
/// Uses ChangeNotifier to notify listeners of state changes
///
/// Usage example:
/// ```dart
/// final authProvider = Provider.of<AuthProvider>(context);
///
/// // Login
/// await authProvider.login(
/// userId: '学号',
/// ecPassword: 'EC密码',
/// password: 'UAAP密码',
/// );
///
/// // Check session
/// final isValid = await authProvider.checkSession();
///
/// // Logout
/// await authProvider.logout();
/// ```
class AuthProvider extends ChangeNotifier {
AUFEConnection? _connection;
AuthState _state = AuthState.initial;
String? _errorMessage;
UserCredentials? _credentials;
/// Get current authentication state
AuthState get state => _state;
/// Get current error message (if any)
String? get errorMessage => _errorMessage;
/// Get current connection instance
AUFEConnection? get connection => _connection;
/// Get current user credentials
UserCredentials? get credentials => _credentials;
/// Check if user is authenticated
bool get isAuthenticated => _state == AuthState.authenticated;
/// Login with user credentials
///
/// Creates AUFEConnection and performs both EC and UAAP login
/// Saves credentials securely on successful login
///
/// [userId] - Student ID
/// [ecPassword] - EC system password
/// [password] - UAAP system password
///
/// Returns true if login succeeds, false otherwise
Future<bool> login({
required String userId,
required String ecPassword,
required String password,
}) async {
try {
print('🔐 Starting login process...');
print('🔐 User ID: $userId');
_setState(AuthState.loading);
_errorMessage = null;
// Create credentials
final credentials = UserCredentials(
userId: userId,
ecPassword: ecPassword,
password: password,
);
// Create connection
print('🔐 Creating AUFEConnection...');
final connection = AUFEConnection(
userId: userId,
ecPassword: ecPassword,
password: password,
);
// Initialize HTTP client
print('🔐 Starting HTTP client...');
connection.startClient();
// Perform EC login
print('🔐 Performing EC login...');
final ecLoginStatus = await connection.ecLogin();
print('🔐 EC login result: ${ecLoginStatus.success}');
if (!ecLoginStatus.success) {
_errorMessage = _getEcLoginErrorMessage(ecLoginStatus);
print('❌ EC login failed: $_errorMessage');
_setState(AuthState.error);
await connection.close();
return false;
}
// Perform UAAP login
print('🔐 Performing UAAP login...');
final uaapLoginStatus = await connection.uaapLogin();
print('🔐 UAAP login result: ${uaapLoginStatus.success}');
if (!uaapLoginStatus.success) {
_errorMessage = _getUaapLoginErrorMessage(uaapLoginStatus);
print('❌ UAAP login failed: $_errorMessage');
_setState(AuthState.error);
await connection.close();
return false;
}
// Save credentials securely
print('🔐 Saving credentials...');
await credentials.saveSecurely();
// Update state
_connection = connection;
_credentials = credentials;
_setState(AuthState.authenticated);
print('✅ Login successful!');
return true;
} catch (e, stackTrace) {
_errorMessage = '登录过程出错: $e';
print('❌ Login error: $e');
print('❌ Stack trace: $stackTrace');
_setState(AuthState.error);
return false;
}
}
/// Logout and clear all session data
///
/// Closes connection, clears credentials from secure storage,
/// and resets authentication state
Future<void> logout() async {
try {
// Close connection
if (_connection != null) {
await _connection!.close();
_connection = null;
}
// Clear credentials from secure storage
await UserCredentials.clearSecurely();
// Reset state
_credentials = null;
_errorMessage = null;
_setState(AuthState.unauthenticated);
} catch (e) {
debugPrint('Error during logout: $e');
// Still reset state even if cleanup fails
_connection = null;
_credentials = null;
_setState(AuthState.unauthenticated);
}
}
/// Check if current session is still valid
///
/// Performs health check on the connection
/// If session is invalid, updates state to unauthenticated
///
/// Returns true if session is valid, false otherwise
Future<bool> checkSession() async {
if (_connection == null || _state != AuthState.authenticated) {
_setState(AuthState.unauthenticated);
return false;
}
try {
final isHealthy = await _connection!.healthCheck();
if (!isHealthy) {
_errorMessage = '会话已过期,请重新登录';
_setState(AuthState.unauthenticated);
return false;
}
return true;
} catch (e) {
_errorMessage = '检查会话状态失败: $e';
_setState(AuthState.error);
return false;
}
}
/// Attempt to restore session from saved credentials
///
/// Loads credentials from secure storage and attempts to login
/// Useful for auto-login on app startup
///
/// Returns true if session restored successfully, false otherwise
Future<bool> restoreSession() async {
try {
_setState(AuthState.loading);
// Load saved credentials
final credentials = await UserCredentials.loadSecurely();
if (credentials == null) {
_setState(AuthState.unauthenticated);
return false;
}
// Attempt login with saved credentials
return await login(
userId: credentials.userId,
ecPassword: credentials.ecPassword,
password: credentials.password,
);
} catch (e) {
_errorMessage = '恢复会话失败: $e';
_setState(AuthState.unauthenticated);
return false;
}
}
/// Update authentication state and notify listeners
void _setState(AuthState newState) {
_state = newState;
notifyListeners();
}
/// Get user-friendly error message for EC login status
String _getEcLoginErrorMessage(dynamic status) {
if (status.failInvalidCredentials) {
return 'EC系统用户名或密码错误';
} else if (status.failNotFoundTwfid) {
return '无法获取TwfID请稍后重试';
} else if (status.failNotFoundRsaKey) {
return '无法获取RSA密钥请稍后重试';
} else if (status.failNotFoundRsaExp) {
return '无法获取RSA指数请稍后重试';
} else if (status.failNotFoundCsrfCode) {
return '无法获取CSRF代码请稍后重试';
} else if (status.failMaybeAttacked) {
return '登录频繁,请稍后重试';
} else if (status.failNetworkError) {
return 'EC系统网络连接失败';
} else {
return 'EC系统登录失败';
}
}
/// Get user-friendly error message for UAAP login status
String _getUaapLoginErrorMessage(dynamic status) {
if (status.failInvalidCredentials) {
return 'UAAP系统用户名或密码错误';
} else if (status.failNotFoundLt) {
return '无法获取lt参数请稍后重试';
} else if (status.failNotFoundExecution) {
return '无法获取execution参数请稍后重试';
} else if (status.failNetworkError) {
return 'UAAP系统网络连接失败';
} else {
return 'UAAP系统登录失败';
}
}
@override
void dispose() {
// Close connection when provider is disposed
_connection?.close();
super.dispose();
}
}