274 lines
7.9 KiB
Dart
274 lines
7.9 KiB
Dart
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();
|
||
}
|
||
}
|