Android利用Cookie实现码源登录效果

上一篇讲了界面的实现,详情请戳Android利用Cookie实现码源登录效果(一) - 界面实现,本篇将对功能的实现进行分析。

具体实现


功能实现
  • 使用HttpClient进行post请求
    在进行post请求的时候,我们先看看网页版登录是如何发送数据的。

    打开登录页面,点击键盘上的F12,在Sources中找到login,找到发送ajax请求进行登录的部分,加入断点:

    点击登录,然后在Network中看到有个发送到http://www.codefrom.com/login/ajax 的网络请求,点开它,我们看看Header里面有什么数据:

    可以看到,有三部分数据:Request URLRequest HeaderForm Data,他们的含义我想大家应该都很清楚。那么我们就尝试在LoginActivity中模拟一下这个操作。

    首先,我们写一个方法,封装我们的登录请求:

    public String sendPost(String url, String username, String password) {
        // 根据url获得HttpPost对象
        HttpPost httpRequest = new HttpPost(url);
        // 取得默认的HttpClient
        DefaultHttpClient httpclient = new DefaultHttpClient();
        String strResult = null;
        // NameValuePair实现请求参数的封装
        List<NameValuePair> params = new ArrayList<NameValuePair>();
        params.add(new BasicNameValuePair("_tk", "codefrom"));
        params.add(new BasicNameValuePair("name", username));
        params.add(new BasicNameValuePair("pass", password));
        httpRequest.addHeader("Accept", "application/json, text/javascript, */*; q=0.01");
        httpRequest.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        httpRequest.addHeader("Origin", "http://www.codefrom.com");
        httpRequest.addHeader("Referer", "http://www.codefrom.com/login");
        httpRequest.addHeader("X-Requested-With", "XMLHttpRequest");
        try {
            // 添加请求参数到请求对象
            httpRequest.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
            // 获得响应对象
            HttpResponse httpResponse = httpclient.execute(httpRequest);
            // 判断是否请求成功
            if (httpResponse.getStatusLine().getStatusCode() == 200) {
                // 获得响应返回Json格式数据
                strResult = EntityUtils.toString(httpResponse.getEntity());
                return strResult;
            } else {
                strResult = "错误响应:" + httpResponse.getStatusLine().toString();
            }
        } catch (ClientProtocolException e) {
            strResult = "错误响应:" + e.getMessage().toString();
            e.printStackTrace();
            return strResult;
        } catch (IOException e) {
            strResult = "错误响应:" + e.getMessage().toString();
            e.printStackTrace();
            return strResult;
        } catch (Exception e) {
            strResult = "错误响应:" + e.getMessage().toString();
            e.printStackTrace();
            return strResult;
        }
        return strResult;
    }
    

    (注:当前版本网络请求只能异步操作)
    然后,在UserLoginTaskdoInBackground中,当睡眠结束后,我们对登录方法进行调用:

    String res = sendPost("http://www.codefrom.com/login/ajax", mEmail, mPassword);
    Log.d("CodeFromLogin", res);
    

    我们运行项目,看看日志中打印:

    没错,将Unicode转成中文之后,得到“操作成功”的返回信息!
    需要注意一点,一开始,我没有添加httpRequest.addHeader这部分的内容,得到的结果如下:

    感兴趣的同学可以尝试一下,服务器返回提示我们操作失败,缺少ajax头部信息!

  • 保存来自服务器的Cookie
    能够成功发送数据给服务器,并且服务器能响应结果,那么,我们能否获取更多内容呢?当然可以!HttpClient提供了下面的方法供我们获取Cookie:

    // 取得Cookie
    CookieStore mCookieStore = httpclient.getCookieStore();
    List<Cookie> cookies = mCookieStore.getCookies();
    

    那么,Android又是如何对Cookie进行管理的呢?

    // 设置cookie
    public static void synCookies(Context context, String url) {  
        CookieSyncManager.createInstance(context);  
        CookieManager cookieManager = CookieManager.getInstance();  
        cookieManager.setCookie(url, "uid=1243432");              
        CookieSyncManager.getInstance().sync();  
    }
    
    // 清除cookie
    private void removeCookie(Context context) {
        CookieSyncManager.createInstance(context);  
        CookieManager cookieManager = CookieManager.getInstance(); 
        cookieManager.removeAllCookie();
        CookieSyncManager.getInstance().sync();  
    }
    

    接下来,我们就可以对sendPost方法进行一些改造了:

    public CookieManager cookieManager = null;
    public static String cookies;
    public String sendPost(String url, String username, String password) {
      CookieSyncManager.createInstance(LoginActivity.this);
      // 每次登录操作的时候先清除cookie
      removeAllCookie();
      // 根据url获得HttpPost对象
      HttpPost httpRequest = new HttpPost(url);
      // 取得默认的HttpClient
      DefaultHttpClient httpclient = new DefaultHttpClient();
      String strResult = null;
      // NameValuePair实现请求参数的封装
      List<NameValuePair> params = new ArrayList<NameValuePair>();
      params.add(new BasicNameValuePair("_tk", "codefrom"));
      params.add(new BasicNameValuePair("name", username));
      params.add(new BasicNameValuePair("pass", password));
      httpRequest.addHeader("Accept", "application/json, text/javascript, */*; q=0.01");
      httpRequest.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
      httpRequest.addHeader("Origin", "http://www.codefrom.com");
      httpRequest.addHeader("Referer", "http://www.codefrom.com/login");
      httpRequest.addHeader("X-Requested-With", "XMLHttpRequest");
      try {
          // 添加请求参数到请求对象
          httpRequest.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
          // 获得响应对象
          HttpResponse httpResponse = httpclient.execute(httpRequest);
          // 判断是否请求成功
          if (httpResponse.getStatusLine().getStatusCode() == 200) {
              // 获得响应返回Json格式数据
              strResult = EntityUtils.toString(httpResponse.getEntity());
              // 取得Cookie
              CookieStore mCookieStore = httpclient.getCookieStore();
              List<Cookie> cookies = mCookieStore.getCookies();
              if (cookies.isEmpty()) {
                  System.out.println("Cookies为空");
              } else {
                  for (int i = 0; i < cookies.size(); i++) {
                      // 保存cookie
                      Cookie cookie = cookies.get(i);
                      Log.d("Cookie", cookies.get(i).getName() + "=" + cookies.get(i).getValue());
                      cookieManager = CookieManager.getInstance();
                      String cookieString = cookie.getName() + "=" + cookie.getValue() + "; domain=" + cookie.getDomain();
                      cookieManager.setCookie("http://www.codefrom.com/", cookieString);
                  }
              }
              return strResult;
          } else {
              strResult = "错误响应:" + httpResponse.getStatusLine().toString();
          }
      } catch (ClientProtocolException e) {
          strResult = "错误响应:" + e.getMessage().toString();
          e.printStackTrace();
          return strResult;
      } catch (IOException e) {
          strResult = "错误响应:" + e.getMessage().toString();
          e.printStackTrace();
          return strResult;
      } catch (Exception e) {
          strResult = "错误响应:" + e.getMessage().toString();
          e.printStackTrace();
          return strResult;
      }
      return strResult;
    }
    
    private void removeAllCookie() {
      cookieManager = CookieManager.getInstance();
      cookieManager.removeAllCookie();
      CookieSyncManager.getInstance().sync();  
    }
    

    运行改造之后的代码,我们先进行登录,然后点击按钮打开码源首页,可以看到,首页导航是可以看到登录信息的!

  • 使用本地广播更新界面
    完成登录之后,我们需要修改主界面的控件文字,比如我们想显示用户名等等。理论上来讲,使用startActivity将数据通过Intent从登录界面传递给主界面的方法是可行的。但是,我们需要考虑在实际中,如果多个地方校验到用户没有登录,都需要弹出登录界面,然后登录完了之后,我们需要返回的界面不一定都是主界面(如果PM要求返回主界面,当我没说),那我们该怎么办呢?
    这时,我们可以通过本地广播来实现:在主界面(或者其他需要得到登录信息的界面),动态注册一个本地广播,通过广播获取到的Intent来进行数据分析,部分代码如下:

    localBroadcastManager = LocalBroadcastManager.getInstance(MainActivity.this);
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("com.codefrom.broadcastreceiver.LOGIN_BROADCAST");//建议把它写一个公共的变量,这里方便阅读就不写了。
    loginBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle bundle = intent.getExtras();
    
            boolean result = bundle.getBoolean("result");
            String user = bundle.getString("username");
            if (result) {
                username.setText(user);
                Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
                loginbtn.setText("退出登录");
                isLogin = true;
            } else {
                Toast.makeText(MainActivity.this, "登录失败", Toast.LENGTH_SHORT).show();
                username.setText("未登录");
                loginbtn.setText("登录");
                isLogin = false;
            }
        }
    };
    localBroadcastManager.registerReceiver(loginBroadcastReceiver, intentFilter);
    

    同时,在登录界面,登录成功之后,我们可以发送一个广播告知其他界面登录结果,由于登录在子线程中进行,我们就需要借助Handler进行广播发送:

    Handler handler = new Handler() {
    public void handleMessage(android.os.Message msg) {
        LoginInfo loginInfo = (LoginInfo) msg.obj;
        Toast.makeText(LoginActivity.this, loginInfo.getStatus() + " - " + loginInfo.getMsg() + " - " + Uri.decode(loginInfo.getMsg()) , Toast.LENGTH_SHORT).show();
        if("1".equals(loginInfo.getStatus())) {
            Intent intent = new Intent("com.codefrom.broadcastreceiver.LOGIN_BROADCAST");
            Bundle bundle = new Bundle();
            bundle.putBoolean("result", true);
            bundle.putString("username", "单车武士");
            intent.putExtras(bundle);
            localBroadcastManager.sendBroadcast(intent);
        }
    };
    };
    

    其实,如果观察LoginActivity,在成功以后,它做的操作是调用Activity的finish(),也就是直接关闭登录界面了,我猜系统应该也是不推荐直接使用startActivity进行显示跳转的。
    由于服务器并没有给我们返回更多用户的信息,我们先返回一个写死的username过去……当然,在网页中是根据cookie来获取用户信息的,这方面的东西在Android上面的实现我们今后再探讨。

以上就是我们实现的模拟登录码源效果,如果有什么不对的地方或者更好的建议,欢迎回复交流。


附:Demo下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值