Skip to content

Commit

Permalink
debug network
Browse files Browse the repository at this point in the history
  • Loading branch information
liplum committed Apr 30, 2024
1 parent a91d2e7 commit 2ad0c84
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 33 deletions.
4 changes: 0 additions & 4 deletions lib/init.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,6 @@ class Init {
ssoSession = SsoSession(
dio: dio,
cookieJar: cookieJar,
onError: (error, stackTrace) {
debugPrint(error.toString());
debugPrintStack(stackTrace: stackTrace);
},
inputCaptcha: (Uint8List imageBytes) async {
final context = $key.currentContext!;
// return await context.show$Sheet$(
Expand Down
29 changes: 29 additions & 0 deletions lib/network/dio.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:fk_user_agent/fk_user_agent.dart';
import 'package:flutter/foundation.dart';
import 'package:sit/r.dart';
import 'package:sit/session/sso.dart';

final _rand = Random();

Expand All @@ -18,6 +19,14 @@ class DioInit {
if (!kIsWeb) {
dio.interceptors.add(CookieManager(cookieJar));
}
if (kDebugMode) {
dio.interceptors.add(LogInterceptor(logPrint: (obj) {
networkLogger.i(obj);
}));
}
if (kDebugMode && R.debugNetwork) {
dio.interceptors.add(PoorNetworkDioInterceptor());
}
if (config != null) {
dio.options = config;
}
Expand All @@ -44,3 +53,23 @@ class DioInit {
}
}
}

class PoorNetworkDioInterceptor extends Interceptor {
@override
Future<void> onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
final duration = Duration(milliseconds: _rand.nextInt(5000));
debugPrint("Start to request ${options.uri}");
await Future.delayed(duration);
debugPrint("Delayed Request ${options.uri} $duration");
handler.next(options);
}

@override
Future<void> onResponse(Response response, ResponseInterceptorHandler handler) async {
final duration = Duration(milliseconds: _rand.nextInt(5000));
debugPrint("Start to response ${response.realUri}");
await Future.delayed(duration);
debugPrint("Delayed Response ${response.realUri} $duration");
handler.next(response);
}
}
3 changes: 3 additions & 0 deletions lib/network/proxy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ class SitHttpOverrides extends HttpOverrides {
final client = super.createHttpClient(context);
client.badCertificateCallback = (cert, host, port) => true;
client.findProxy = (url) {
if (kDebugMode) {
print('Accessing "$url", captured by $SitHttpOverrides');
}
final host = url.host;
final isSchoolLanRequired = _isSchoolLanRequired(host);
final profiles = _buildProxy(isSchoolLanRequired);
Expand Down
2 changes: 2 additions & 0 deletions lib/r.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class R {
/// For debugging iOS on other platforms.
static const debugCupertino = kDebugMode ? false : false;

static const debugNetwork = true;

/// The default window size is small enough for any modern desktop device.
static const Size defaultWindowSize = Size(500, 800);

Expand Down
58 changes: 31 additions & 27 deletions lib/session/sso.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:collection/collection.dart';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart' hide Key;
import 'package:logger/logger.dart';
import 'package:sit/credentials/entity/credential.dart';
import 'package:sit/credentials/error.dart';
import 'package:sit/credentials/init.dart';
Expand Down Expand Up @@ -46,6 +47,16 @@ const _neededHeaders = {
"Referer": "https://authserver.sit.edu.cn/authserver/login",
};

final networkLogger = Logger(
printer: PrettyPrinter(
methodCount: 8,
// Number of method calls to be displayed
errorMethodCount: 8,
// Print an emoji for each log message
printTime: true, // Should each log print contain a timestamp
),
);

/// Single Sign-On
class SsoSession {
static const String _authServerUrl = 'https://authserver.sit.edu.cn/authserver';
Expand All @@ -57,39 +68,36 @@ class SsoSession {
final Dio dio;
final CookieJar cookieJar;

/// Session错误拦截器
final void Function(Object error, StackTrace stackTrace)? onError;

/// Input captcha manually
final Future<String?> Function(Uint8List imageBytes) inputCaptcha;

/// Lock it to prevent simultaneous login.
final _loginLock = Lock();
static final _loginLock = Lock();

SsoSession({
required this.dio,
required this.cookieJar,
required this.inputCaptcha,
this.onError,
});

/// - User try to log in actively on a login page.
Future<Response> loginLocked(Credentials credentials) async {
return await _loginLock.synchronized(() async {
networkLogger.i("loginLocked ${DateTime.now().toIso8601String()}");
try {
final autoCaptcha = await _login(
final byAutoCaptcha = await _login(
credentials: credentials,
inputCaptcha: (captchaImage) => AuthSession.recognizeOaCaptcha(captchaImage),
);
return autoCaptcha;
return byAutoCaptcha;
} catch (error, stackTrace) {
debugPrintError(error, stackTrace);
}
final manuallyCaptcha = await _login(
final byManualCaptcha = await _login(
credentials: credentials,
inputCaptcha: inputCaptcha,
);
return manuallyCaptcha;
return byManualCaptcha;
});
}

Expand All @@ -103,7 +111,6 @@ class SsoSession {
}) async {
options ??= Options();

/// 正常地请求
Future<Response> requestNormally() async {
final response = await dio.request(
url,
Expand All @@ -118,11 +125,10 @@ class SsoSession {
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
// 处理重定向
return await processRedirect(dio, response, headers: _neededHeaders);
}

// 第一次先正常请求
// request normally at first
final firstResponse = await requestNormally();

// check if the response is the login page. if so, login it first.
Expand Down Expand Up @@ -166,7 +172,7 @@ class SsoSession {
final Response response;
try {
// 首先获取AuthServer首页
final html = await _getAuthServerHtml();
final html = await _fetchAuthServerHtml();
var captcha = '';
if (await isCaptchaRequired(credentials.account)) {
final captchaImage = await getCaptcha();
Expand All @@ -179,9 +185,9 @@ class SsoSession {
}
}
// 获取casTicket
final casTicket = _getCasTicketFromAuthHtml(html);
final casTicket = _extractCasTicketFromAuthHtml(html);
// 获取salt
final salt = _getSaltFromAuthHtml(html);
final salt = _extractSaltFromAuthHtml(html);
// 加密密码
final hashedPwd = _hashPassword(salt, credentials.password);
// 登录系统,获得cookie
Expand Down Expand Up @@ -234,34 +240,34 @@ class SsoSession {
return CredentialsErrorType.accountPassword;
}

/// 提取认证页面中的加密盐
String _getSaltFromAuthHtml(String htmlText) {
/// Extract the Salt from the auth page
String _extractSaltFromAuthHtml(String htmlText) {
final a = RegExp(r'var pwdDefaultEncryptSalt = "(.*?)";');
final matchResult = a.firstMatch(htmlText)!.group(0)!;
final salt = matchResult.substring(29, matchResult.length - 2);
debugPrint('Salt: $salt');
return salt;
}

/// 提取认证页面中的Cas Ticket
String _getCasTicketFromAuthHtml(String htmlText) {
/// Extract the CAS ticket from the auth page
String _extractCasTicketFromAuthHtml(String htmlText) {
final a = RegExp(r'<input type="hidden" name="lt" value="(.*?)"');
final matchResult = a.firstMatch(htmlText)!.group(0)!;
final casTicket = matchResult.substring(38, matchResult.length - 1);
debugPrint('CAS Ticket: $casTicket');
return casTicket;
}

/// 获取认证页面内容
Future<String> _getAuthServerHtml() async {
/// Fetch the auth page, where the account, password and captcha box are.
Future<String> _fetchAuthServerHtml() async {
final response = await dio.get(
_loginUrl,
options: Options(headers: Map.from(_neededHeaders)..remove('Referer')),
);
return response.data;
}

/// 判断是否需要验证码
/// check if captcha is required for this logging in
Future<bool> isCaptchaRequired(String username) async {
final response = await dio.get(
_needCaptchaUrl,
Expand All @@ -276,7 +282,6 @@ class SsoSession {
return needCaptcha;
}

/// 获取验证码
Future<Uint8List> getCaptcha() async {
final response = await dio.get(
_captchaUrl,
Expand All @@ -289,9 +294,9 @@ class SsoSession {
return captchaData;
}

/// 登录统一认证平台
/// Login the single sign-on
Future<Response> _postLoginRequest(String username, String hashedPassword, String captcha, String casTicket) async {
// 登录系统
// Login
final res = await dio.post(_loginUrl,
data: {
'username': username,
Expand All @@ -311,7 +316,6 @@ class SsoSession {
},
headers: _neededHeaders,
));
// 处理重定向
return await processRedirect(dio, res, headers: _neededHeaders);
}

Expand All @@ -331,7 +335,7 @@ class SsoSession {
options: options,
);
} catch (error, stackTrace) {
onError?.call(error, stackTrace);
debugPrintError(error, stackTrace);
rethrow;
}
}
Expand Down
5 changes: 3 additions & 2 deletions lib/settings/page/storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ class _BoxSectionState extends State<BoxSection> {
final boxNameStyle = ctx.textTheme.headlineSmall;
final action = PullDownMenuButton(
itemBuilder: (ctx) => [
PullDownItem.edit(
title: i18n.edit,
PullDownItem.delete(
icon: context.icons.delete,
title: i18n.delete,
onTap: () async {
final confirm = await _showDeleteBoxRequest(ctx);
if (confirm == true) {
Expand Down

0 comments on commit 2ad0c84

Please sign in to comment.