Flutter引入图形验证码,并保留SessionId

Posted HackShendi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter引入图形验证码,并保留SessionId相关的知识,希望对你有一定的参考价值。

Hello,I’m Shendi

百度搜索不到相关内容,官网也没有相关内容,问群里大佬得到答案,这里记录一下



问题描述

登陆时需要使用到后端提供的图形验证码,后端通过 Session 保存验证码信息

Flutter 中提供了加载网络图片的方式,但没办法保存 SessionId,于是改变思路,用 dio 读取图片数据流,拿到 SessionId,再渲染到组件


解决

关于 dio 获取 SessionId 的方法可以参考这篇文章
https://shendi.blog.csdn.net/article/details/122510279

获取图片字节流的代码如下

LoginData 
	var cookie;
	Map<String, String> session = 
		"name" : "",
		"value" : "",
		"domain" : "shendi"
	;

var loginData = LoginData();

Future<Uint8List> getVCCode() async 
	dio.options.followRedirects = false;
    dio.options.validateStatus = (status) 
      return status! < 500;
    ;
    // 这里是设置Cookie,Cookie中包含SessionId
    if (loginData.cookie != null) dio.options.headers["Cookie"] = loginData.cookie;

	Response resp = await dio.get(
      "验证码地址" + Random().nextInt(100).toString(),
      options: Options(responseType: ResponseType.stream).timeout(const Duration(seconds: 3)));

	// 如果有 set-cookie 则获取保存
	if (resp.headers["set-cookie"] != null) 
		var cookie = resp.headers["set-cookie"].toString();
		cookie = cookie.substring(1, cookie.length - 1);
		loginData.cookie = cookie;
		
		// 设置session结构, 目前COOKIE内容为 JSESSIONID=xxx; Path=/; HttpOnly
		int len = cookie.indexOf('=');
		loginData.session["name"] = cookie.substring(0, len);
		cookie = cookie.substring(len);
		len = cookie.indexOf(';');
		loginData.session["value"] = cookie.substring(1, len);
		cookie = cookie.substring(len+1);
    

	final stream = await (resp.data as ResponseBody).stream.toList();
	final result = BytesBuilder();
	for (Uint8List subList in stream) 
		result.add(subList);
	

	return result.takeBytes();

因为使用的 dio,获取到的返回是 Future<Uint8List>,我们需要拿到 Uint8List,需要使用到状态管理,可参考官网文档
https://book.flutterchina.club/chapter2/state_manage.html


这里我直接将我的登陆页代码贴上,供参考

/// 登陆页面
class Login extends StatelessWidget 
  const Login(Key? key) : super(key: key);

  final appTitle = "登录 - Shendi";

  @override
  Widget build(BuildContext context) 

    return MaterialApp(
      title: appTitle,
      home: const LoginPage(),
    );
  


/// 登录页面
class LoginPage extends StatefulWidget 
  const LoginPage(Key? key) : super(key: key);

  @override
  State<StatefulWidget> createState() 
    return LoginPageState();
  



/// 登陆状态
class LoginPageState extends State<LoginPage> 
  var account = TextEditingController();
  var pwd = TextEditingController();
  var code = TextEditingController();

  var codeImg;

  @override
  void initState() 
    super.initState();

    // 初始化验证码
    flushVCCode();

    eventBus.on<VCCodeFlush>().listen((event) 
      flushVCCode();
    );
  

  /// 刷新验证码
  flushVCCode() async 
    var data = await http.getVCCode();
    setState(() 
      codeImg = Image.memory(data);
    );
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: Text(const Login().appTitle),
        ),
        body: Padding(
        padding: const EdgeInsets.all(20),
        child: ListView(
          children: [
            TextFormField(
              controller: account,
              decoration: const InputDecoration(
                  labelText: "账号",
                  hintText: "请输入账号"
              ),
            ),
            TextField(
              controller: pwd,
              decoration: const InputDecoration(
                  labelText: "密码",
                  hintText: "请输入密码"
              ),
            ),
            Row(
              children: [
                Expanded(child: TextFormField(
                  controller: code,
                  decoration: const InputDecoration(
                      labelText: "验证码",
                      hintText: "请输入验证码"
                  ),
                )),
                GestureDetector(
                  child: codeImg ?? Container(),
                  onTap: () 
                    flushVCCode();
                  ,
                )
              ],
            ),
            Padding(
              padding: const EdgeInsets.fromLTRB(0, 22, 0, 0),
              child: ElevatedButton(
                  onPressed: () async 
                    var msg;
                    if (pwd.text == "") msg = "请填写密码!";
                    if (account.text == "") msg = "请填写账号!";
                    if (code.text == "") msg = "请输入验证码!";

                    if (msg != null) 
                      Fluttertoast.showToast(
                          msg: msg,
                          toastLength: Toast.LENGTH_SHORT,
                          gravity: ToastGravity.TOP,
                          timeInSecForiosWeb: 1,
                          fontSize: 16.0
                      );
                      return;
                    

                    bool isLogin = await http.login(account.text, pwd.text, code.text);
                    if (isLogin) 
                      // 登录成功,进入主页面
                      runApp(const RoomList());
                    
                  ,
                  child: const Text("登录")
              ),
            )
          ],
        ),
      )
    );
  


END

以上是关于Flutter引入图形验证码,并保留SessionId的主要内容,如果未能解决你的问题,请参考以下文章

Flutter引入图形验证码,并保留SessionId

常用开发组件

常用开发组件

MVC中生成图形验证码并验证(详细)

MVC中生成图形验证码并验证(详细)

MVC中生成图形验证码并验证(详细)