Files
AutoJudge-Flutter/lib/providers/auth_provider.dart

274 lines
7.9 KiB
Dart
Raw Normal View History

2025-11-13 09:14:49 +08:00
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();
}
}