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();
|
|||
|
|
}
|
|||
|
|
}
|