本鱼拟成立工作室承接项目开发/软件定制/云设施开发运维/办公设备技术支持等,如您有相关需求,欢迎来询 | ::博客文章推荐::

12306订票客户端 FOR .NET 演示项目 【7】登录

: DOT.NET 木魚 18322℃ 15评论
每天我都在烂尾和不烂尾间徘徊。每次我都想着累死累死累死,我这么彷徨这么无助这么凄凉,连路灯都用昏黄的灯光为我默泪,为什么我还要在这边继续着有人看或没人看的事,写能怎么样,断更又能怎么样。只是强迫症的独特天赋在鞭策着我,于是每次都会极不情愿地拿起键盘,强大的羞耻心在时刻地提醒着我,如果真的断更了,下一次上厕所你一定会忘记带纸,你就只能凄凉着等着有人路过大方地施舍给你一张草纸,因为你连人民币都没有,你从来不会在大号的时候带着钱包。说到这里,下次记得放一卷手纸在卫生间的角落里。。。

上一章写到已经成功获得了验证码并且输入。那么输入后能干嘛?当然先登录了  54.gif 

7.1 先完成一些辅助函数和登录界面

创建登录界面应该是极为简单的事情,就不多说了。我们这里设计个登录界面如下。

ticket_12306_demo_7_1

然后写上了俩调用函数。

partial class Login : Form
{
    ServiceContext _context;
    
    public Login(ServiceContext context)
    {
        _context = context;
        InitializeComponent();
    }
 
    private void btnOk_Click(object sender, EventArgs e)
    {
 
    }
}
tsLogin.Click += (s, e) => new Login(_context).ShowDialog(this);

……好吧,我突然想起来好像登录函数还没写,咱继续回去写吧。

7.2 登录函数

上一章研究验证码的时候我们曾经保存了一个包,当时提到了下次还要用。现在我们翻出来那个包,再看一下,找找与登录相关的请求。

ticket_12306_demo_7_2

在验证码校验请求之后紧跟着两个请求,第一个包含了用户名密码的验证码,这货脑门上就写着“我是登录请求”了。至于第二个……不知道干嘛的,看起来就是POST一个空数据到对应的网页,然后对方302了一下。

从完整可靠性起见,我们还是完整复制流程吧……且慢,让我们先看一下正确登录和错误登录都是啥信息。

首先来看用户密码输错的时候。

{
    "validateMessagesShowId": "_validatorMessage",
    "status": true,
    "httpstatus": 200,
    "data": { },
    "messages": [ "密码输入错误。如果输错次数超过4次,用户将被锁定。" ],
    "validateMessages": { }
}

再看正确的情况。

{
    "validateMessagesShowId": "_validatorMessage",
    "status": true,
    "httpstatus": 200,
    "data": {
        "otherMsg": "",
        "loginCheck": "Y"
    },
    "messages": [ ],
    "validateMessages": { }
}

……好吧明白了。那我们就创建一个类来保存返回值吧。

class LoginAsyncResult
{
    public string LoginCheck { get; set; }
    public string OtherMsg { get; set; }
 
    /// <summary>
    /// 获得登录是否成功
    /// </summary>
    public bool IsSuceess {get { return LoginCheck == "Y"; } }
}

然后我们在Session中写登录函数。代码位置随便放,就是酱紫随便  72.gif 

/// <summary>
/// 登录。如果返回异常则说明登录失败
/// </summary>
/// <param name="verifyPoints"></param>
/// <returns></returns>
public async Task<ExceptionLoginAsync(string usernamestring passwordList<PointverifyPoints)
{
    IsLogined = false;
    LoginInfo = new LoginInfo()
    {
        UserName = username,
        Password = password
    };
    var loginData = new Dictionary<stringstring>()
    {
        ["loginUserDTO.user_name"] = LoginInfo.UserName,
        ["userDTO.password"] = LoginInfo.Password,
        ["randCode"] = verifyPoints.Select(s => s.X + "," + s.Y).JoinAsString(",")
    };
    var loginCheck = NetClient.Create<WebResponseResult<LoginAsyncResult>>(
                                        HttpMethod.Post,
                                    "https://kyfw.12306.cn/otn/login/loginAysnSuggest",
                                    "https://kyfw.12306.cn/otn/login/init",
                                    loginData
        );
    await loginCheck.SendTask();
    if (!loginCheck.IsValid())
    {
        return loginCheck.Exception ?? new Exception("未能提交请求");
    }
 
    if (!loginCheck.Result.Data.IsSuceess)
    {
        return new Exception(loginCheck.Result.GetErrorMessage());
    }
 
    //登录成功
    var postLogin = NetClient.Create<string>(
                                                HttpMethod.Post,
                                            "https://kyfw.12306.cn/otn/login/userLogin",
                                            "https://kyfw.12306.cn/otn/login/init"
        );
    await postLogin.SendTask()//这里的返回值我们不care ....
 
    //登录好了。等等。。我们好像想拿到显示的中文名?
    //所以多加一个请求吧。
    var realNameCtx = NetClient.Create<string>(
                                                HttpMethod.Get,
                                            "https://kyfw.12306.cn/otn/index/initMy12306",
                                            "https://kyfw.12306.cn/otn/login/init"
        );
    await realNameCtx.SendTask();
 
    IsLogined = true;
 
    return null;
}

等等。我突然想到在这里也许还要同时获得显示的中文名以便于后续显示。

我们看看这个中文名可以从哪里获得。貌似“我的12306”这个页面不错。

ticket_12306_demo_7_3

user_name 很明显就是名字了,虽然经过了编码。但是这个会难道我大C#吗,开玩笑。

//登录好了。等等。。我们好像想拿到显示的中文名?
//所以多加一个请求吧。
var realNameCtx = NetClient.Create<string>(
                                            HttpMethod.Get,
                                        "https://kyfw.12306.cn/otn/index/initMy12306",
                                        "https://kyfw.12306.cn/otn/login/init"
    );
await realNameCtx.SendTask();
 
if (realNameCtx.IsValid())
{
    //匹配出名字信息
    var realMatch = Regex.Match(realNameCtx.Result@"user_name\s*=\s*['""]([^'""]+)['""]"RegexOptions.Singleline);
    if (realMatch.Success)
    {
        LoginInfo.DisplayName = realMatch.GetGroupValue(1).DecodeFromJsExpression();
    }
}
//这里失败了我们就随便起个名字,嗯。
if (LoginInfo.DisplayName.IsNullOrEmpty())
    LoginInfo.DisplayName = "路人甲";

然后我们在登录对话框中将逻辑写上。

private async void btnOk_Click(object senderEventArgs e)
{
    var username = txtUserName.Text;
    var password = txtPassword.Text;
 
    if (username.IsNullOrEmpty() || password.IsNullOrEmpty())
    {
        MessageBox.Show("输入用户名和密码哦!""提示"MessageBoxButtons.OKMessageBoxIcon.Information);
        return;
    }
 
    var vcdlg = new RequireVcDlg(_contextVerifyCodeType.Login);
    if (vcdlg.ShowDialog(this) != DialogResult.OK)
        return;
 
    var result = await _context.Session.LoginAsync(usernamepasswordvcdlg.Points);
    if (result == null)
    {
        Close();
        return;
    }
    MessageBox.Show("登录失败:" + result.Message"提示"MessageBoxButtons.OKMessageBoxIcon.Asterisk);
}

嗯,来试试看  74.gif 

ticket_12306_demo_7_4

额…… 72.gif  登录成功了??

我还以为会登录失败提示网络繁忙呢。

好吧。既然登录成功那我这里就可以少写一点了。

管他呢。

提示:其实这里登录成功是一个比较奇怪的事儿,因为按照12306逻辑这里会校验一个请求。但为什么会不校验也成功……表示表示很明白为啥,后面再说吧。

7.3 注销登录

把注销代码加上。我懒得写网络请求了,所以注销么,直接销毁Session得了。

/// <summary>
/// 注销登录
/// </summary>
public void Logout()
{
    LoginInfo = null;
    NetClient=new NetClient();
    IsLogined = false;
}

 

喜欢 (23)
发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(15)个小伙伴在吐槽
  1. 打算最近实践这个系列,不知道文章中的方法在两年后的今天是否还有效

    涤生2017-09-25 15:47 回复
  2. ["loginUserDTO.user_name"] = LoginInfo.UserName,出错

    lzpgfy2016-12-27 21:45 回复
  3. 為什麼會一直返回"系统繁忙,请稍后重试!"?

    D.X2016-12-27 14:51 回复
    • 我试过也是一样的提示,看参数都已经提交了

      JZ.Chen2016-12-29 23:31 回复
      • 需要先Get Referer网址

        Gloway.Liu2017-01-09 23:04 回复
        • 果然可以了icon_twisted.gif,另外想請教下為何在瀏覽器裏抓包看不到這個請求?還有登錄提交時不是應該要帶cookie嗎,為何沒有也能登錄成功?

          D.X2017-01-22 10:24 回复
  4. 我弄了一个算法,因为图片是固定大小的(293*190),那么把图片进行切分为8块,并去掉文字部分也就是30个像素,然后判定用户点击的在哪个区域内,根据区域取该块的中心位置作为验证码的x,y坐标。这样会有问题吗?

    大大2016-01-04 17:48 回复
    • 做法看不出问题,只要提交数据能通过验证就行了

      木魚2016-01-05 11:11 回复
  5. 请问下,登录一直返回 当前访问用户过多,请稍候重试是什么原因呢?

    大大2016-01-04 17:45 回复
  6. 还是c#的大神,我是刚刚入门不久,写了MVC的网站和WinFrom的应用,也是写成系统型的,很多的地方都不是特别的明白,望鱼哥多多指教啊!

    honolulu2015-12-05 11:23 回复
  7. 求更新啊。。。学习了

    Cheng2015-12-01 16:09 回复
    • 眼下比较忙。。后面看时间会更新的。

      木魚2015-12-01 17:14 回复
  8. :idea:很赞呀,学习了。。。

    dege2015-08-26 16:40 回复
  9. 66666六

    那个同学2015-08-19 10:14 回复
  10. 不要断更啊 大神

    jyyzll2015-08-18 21:32 回复