Skip to content

Commit a8ba5c0

Browse files
ivanfei-1fsy2001w568w
committed
fix: WebVPN uses new login mechanism; change: [UISTool._loginUIS] now
keeps existing cookies during the login process; fix: use correct DIRECT_CONNECT_TEST_URL Co-authored-by: fsy2001 <fsy2001@outlook.com> Co-authored-by: w568w <1278297578@qq.com>
1 parent 0d6717a commit a8ba5c0

File tree

2 files changed

+63
-11
lines changed

2 files changed

+63
-11
lines changed

lib/repository/fdu/uis_login_tool.dart

-2
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,6 @@ class UISLoginTool {
120120
workDio.interceptors.add(CookieManager(workJar));
121121
workDio.interceptors.add(DioLogInterceptor());
122122

123-
// Remove old cookies.
124-
workJar.deleteAll();
125123
Map<String?, String?> data = {};
126124
Response<String> res = await workDio.get(serviceUrl);
127125
BeautifulSoup(res.data!).findAll("input").forEach((element) {

lib/util/webvpn_proxy.dart

+63-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
import 'package:beautiful_soup_dart/beautiful_soup.dart';
12
import 'package:dan_xi/model/person.dart';
23
import 'package:dan_xi/provider/settings_provider.dart';
34
import 'package:dan_xi/repository/fdu/uis_login_tool.dart';
45
import 'package:dan_xi/repository/cookie/independent_cookie_jar.dart';
56
import 'package:dan_xi/repository/cookie/readonly_cookie_jar.dart';
67
import 'package:dan_xi/util/io/dio_utils.dart';
8+
import 'package:dan_xi/util/io/user_agent_interceptor.dart';
79
import 'package:dio/dio.dart';
10+
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
11+
import 'package:flutter/cupertino.dart';
812
import 'package:flutter/foundation.dart';
913

1014
class WebvpnRequestException implements Exception {
@@ -30,10 +34,13 @@ class WebvpnProxy {
3034
// Cookies related with webvpn
3135
static final ReadonlyCookieJar webvpnCookieJar = ReadonlyCookieJar();
3236

33-
static const String DIRECT_CONNECT_TEST_URL = "https://danta.fudan.edu.cn";
37+
static const String DIRECT_CONNECT_TEST_URL = "https://forum.fudan.edu.cn";
3438

35-
static const String WEBVPN_LOGIN_URL =
36-
"https://uis.fudan.edu.cn/authserver/login?service=https%3A%2F%2Fwebvpn.fudan.edu.cn%2Flogin%3Fcas_login%3Dtrue";
39+
static const String WEBVPN_UIS_LOGIN_URL =
40+
"https://uis.fudan.edu.cn/authserver/login?service=https%3A%2F%2Fid.fudan.edu.cn%2Fidp%2FthirdAuth%2Fcas";
41+
static const String WEBVPN_ID_REQUEST_URL =
42+
"https://id.fudan.edu.cn/idp/authCenter/authenticate?service=https%3A%2F%2Fwebvpn.fudan.edu.cn%2Flogin%3Fcas_login%3Dtrue";
43+
static const String WEBVPN_LOGIN_URL = "https://webvpn.fudan.edu.cn/login";
3744

3845
static final Map<String, String> _vpnPrefix = {
3946
"www.fduhole.com":
@@ -82,9 +89,7 @@ class WebvpnProxy {
8289
return false;
8390
}
8491

85-
if (response.realUri
86-
.toString()
87-
.startsWith("https://webvpn.fudan.edu.cn/login")) {
92+
if (response.realUri.toString().startsWith(WEBVPN_LOGIN_URL)) {
8893
return false;
8994
}
9095

@@ -119,21 +124,70 @@ class WebvpnProxy {
119124
debugPrint("Logging into WebVPN");
120125

121126
try {
127+
Dio newDio = DioUtils.newDioWithProxy();
128+
newDio.options = BaseOptions(
129+
receiveDataWhenStatusError: true,
130+
connectTimeout: const Duration(seconds: 5),
131+
receiveTimeout: const Duration(seconds: 5),
132+
sendTimeout: const Duration(seconds: 5));
133+
newDio.interceptors.add(UserAgentInterceptor(
134+
userAgent: SettingsProvider.getInstance().customUserAgent));
122135
// Temporary cookie jar
123136
IndependentCookieJar workJar = IndependentCookieJar();
124-
loginSession =
125-
UISLoginTool.loginUIS(dio, WEBVPN_LOGIN_URL, workJar, _personInfo);
137+
newDio.interceptors.add(CookieManager(workJar));
138+
139+
loginSession = _authenticateWebVPN(newDio, workJar, _personInfo);
126140
await loginSession;
127-
// Clone from temp jar to our dedicated webvpn jar
141+
128142
webvpnCookieJar.cloneFrom(workJar);
129143
isLoggedIn = true;
144+
} catch (e) {
145+
debugPrint("Failed to login to WebVPN: $e");
146+
isLoggedIn = false;
147+
rethrow;
130148
} finally {
131149
loginSession = null;
132150
}
133151
// Any exception thrown won't be catched and will be propagated to widgets
134152
}
135153
}
136154

155+
static Future<void> _authenticateWebVPN(
156+
Dio dio, IndependentCookieJar jar, PersonInfo? info) async {
157+
Response<dynamic>? res = await dio.get(WEBVPN_ID_REQUEST_URL,
158+
options: DioUtils.NON_REDIRECT_OPTION_WITH_FORM_TYPE);
159+
if (res.statusCode == 302) {
160+
await DioUtils.processRedirect(dio, res);
161+
res = await UISLoginTool.loginUIS(dio, WEBVPN_UIS_LOGIN_URL, jar, info);
162+
if (res == null) {
163+
throw WebvpnRequestException("Failed to login to UIS");
164+
}
165+
}
166+
final ticket = _retrieveTicket(res);
167+
168+
Map<String, dynamic> queryParams = {
169+
'cas_login': 'true',
170+
'ticket': ticket,
171+
};
172+
173+
final response = await dio.get(WEBVPN_LOGIN_URL,
174+
queryParameters: queryParams,
175+
options: DioUtils.NON_REDIRECT_OPTION_WITH_FORM_TYPE);
176+
await DioUtils.processRedirect(dio, response);
177+
}
178+
179+
static String? _retrieveTicket(Response<dynamic> response) {
180+
// Check if the URL host matches the expected value
181+
if (response.realUri.host != "id.fudan.edu.cn") {
182+
return null;
183+
}
184+
185+
BeautifulSoup soup = BeautifulSoup(response.data!);
186+
187+
final element = soup.find('', selector: '#ticket');
188+
return element?.attributes['value'];
189+
}
190+
137191
/// Check if we are able to connect to the service directly (without WebVPN).
138192
/// This method uses a low-timeout dio to reduce wait time.
139193
static Future<bool> tryDirect<T>() async {

0 commit comments

Comments
 (0)