You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dart学生门户登录:修改NTLMClient传递CSRF令牌方案咨询

解决方案:结合NTLMClient与CSRF令牌传递

可以不用无头浏览器,只需调整NTLMClient的使用流程,先获取CSRF相关令牌再发起登录请求即可。核心思路是复用NTLMClient实例保持会话,先GET登录页面提取CSRF字段,再携带这些字段发起POST登录请求,具体步骤和代码如下:

关键逻辑说明

  • NTLM会话保持:NTLMClient会自动处理NTLM认证的握手流程,必须复用同一个实例维护会话cookie,否则CSRF令牌会因会话不匹配失效。
  • CSRF字段提取:ASP.NET的CSRF防护依赖__VIEWSTATE、__EVENTVALIDATION、__VIEWSTATEGENERATOR这些隐藏字段,必须从登录页面的HTML中解析获取。
  • 请求头对齐:确保请求头(User-Agent、Content-Type)与浏览器一致,避免被服务器拦截。

修改后的登录代码

import 'package:http/http.dart' as http;
import 'package:html/parser.dart' show parse;
import 'package:html/dom.dart';
import 'package:ntlm/ntlm.dart';
import 'package:my_portal/globals.dart' as globals;

Future<bool> loginGUC(String username, String password) async {
  // 创建NTLMClient实例,复用会话
  final client = NTLMClient(
    domain: "",
    workstation: "",
    username: username,
    password: password,
  );
  
  try {
    // 第一步:GET登录页面,获取CSRF字段和会话cookie
    final loginPageResponse = await client.get(
      Uri.parse('https://portal.giu-uni.de/GIUb/EXTStudent/Home.aspx'),
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
      },
    );

    if (loginPageResponse.statusCode != 200) {
      print('Failed to load login page');
      client.close();
      return false;
    }

    // 解析HTML,提取CSRF相关字段
    final document = parse(loginPageResponse.body);
    String _getInputValue(String inputName) {
      final input = document.querySelector('input[name="$inputName"]');
      return input?.attributes['value'] ?? '';
    }

    final viewState = _getInputValue('__VIEWSTATE');
    final eventValidation = _getInputValue('__EVENTVALIDATION');
    final viewStateGenerator = _getInputValue('__VIEWSTATEGENERATOR');

    // 第二步:构造登录表单数据,携带CSRF字段
    final loginData = {
      '__VIEWSTATE': viewState,
      '__EVENTVALIDATION': eventValidation,
      '__VIEWSTATEGENERATOR': viewStateGenerator,
      '__EVENTTARGET': '',
      '__EVENTARGUMENT': '',
      'username': username,
      'password': password,
    };

    // 发起POST登录请求
    final loginResponse = await client.post(
      Uri.parse('https://portal.giu-uni.de/GIUb/EXTStudent/Home.aspx/login'),
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
      },
      body: loginData,
    );

    // 检查登录是否成功
    bool isLoggedIn = loginResponse.body.contains('Home.aspx');
    if (isLoggedIn) {
      globals.username = username;
      globals.password = password;
    }

    client.close();
    return isLoggedIn;
  } catch (e) {
    print('Login error: $e');
    client.close();
    return false;
  }
}

额外注意事项

  • 如果NTLMClient未自动管理cookie,可复用你现有GIUPortalLogin类中的_updateCookies和_formatCookies方法,手动提取并携带cookie。
  • 若登录接口的URL不是Home.aspx/login,请根据页面表单的action属性调整。
  • 部分ASP.NET站点会在NTLM认证后才返回带CSRF字段的页面,此代码已覆盖该场景。

内容的提问来源于stack exchange,提问作者abtlb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 11:44:52