ZUI易入门Android之安全

Post请求

  • Post请求与Get请求的区别
    • Get请求的参数是直接放在url后面的,而Post请求是放在请求体中的
    • Get请求参数的长度会根据浏览器的不同实现有一定限制,而Post请求参数长度没有限制
    • Get请求方便测试,直接输入地址即可,而Post请求不方便测试,需要借助代码或者工具进行发送
    • Get请求和Post请求没有本质的区别,只是定义上的区别,比如如果你非要将get方式的参数放在请求体中,那么服务器也是可以接收处理的,同样,你也可以将post方式的参数拼在url后面
  • Post请求的使用场景
    • 使用Post请求上传键值对
    • 使用Post请求上传json串
    • 使用Post请求上传单个文件
    • 使用Post请求上传多个文件
  • 使用Post方法上传键值对,代码如下:

    public void asyncPost(String url,HashMap<String, String> params,TextHttpResponseHandler responseHandler){
        RequestParams requestParams = new RequestParams(params);
        asyncHttpClient.post(url, requestParams, responseHandler);
    }
    /**
     * post提交key-value
     * @param url2
     */
    private void postKeyValue(String url) {
        HashMap<String, String> params = new HashMap<String, String>();
        params.put("username", "俊哥");
        params.put("password", "111");
        HttpHelper.get().asyncPost(url, params, new TextHttpResponseHandler() {
            @Override
            public void onSuccess(int arg0, Header[] arg1, String text) {
                tv_result.setText(text);
            }
            @Override
            public void onFailure(int arg0, Header[] arg1, String arg2, Throwable arg3) {
    
            }
        });
    }
    
  • 使用Post方式上传文件,代码如下:

    public void asyncUploadFile(String url, String key, File file,TextHttpResponseHandler responseHandler) {
        RequestParams requestParams = new RequestParams();
        try {
            requestParams.put(key, file);
            asyncHttpClient.post(url,requestParams , responseHandler);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
    private void uploadFile() {
        try {
            File file = new File(Environment.getExternalStorageDirectory()+"/dog.jpg");
            HttpHelper.get().asyncUploadFile(upload, "file", file, new TextHttpResponseHandler() {
                @Override
                public void onSuccess(int arg0, Header[] arg1, String text) {
                    tv_result.setText(text);
                }
                @Override
                public void onFailure(int arg0, Header[] arg1, String arg2, Throwable arg3) {
    
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
  • Post请求相关注意事项:
    • 传递中文参数问题,Get请求同样需要注意,需要对url进行编码,对应的类有URLEncoder和URLDecoder,否则会乱码,当然很多第三方http库已经处理该问题
  • 常见Content-Type定义:
    • 普通文本:text/plain
    • 表单键值对:application/x-www-form-urlencoded
    • 文件数据:application/octet-stream
    • json数据:application/json
    • xml数据:text/xml

Android中常见编码Base64和URL编码

  • URL编码:http协议中请求的url不支持中文和特殊字符(如&?),所以需要对url进行编码和解码,编码使用的是URLEncoder,解码使用的是URLDecoder;

    //进行url编码
    URLEncoder.encode(url)
    //进行url解码
    URLDecoder.decode(encodeUrl)
    
  • Base64:可以将二进制数据对象转为字符串,主要用于在http传输中将一些比较大的比如二进制数据,编码为字符串,适合放入url进行传递,而且具有非明文行。应用场景如下:

    • 将文件进行base64编码为字符串后再上传,代码如下:

      private void base64Image() {
          Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          bitmap.compress(CompressFormat.PNG, 100, baos);
      
          byte[] encode = Base64.encode(baos.toByteArray(), Base64.DEFAULT);
          tv_result.setText(new String(encode));
      }
      
    • 将对象进行base64编码为字符串后,可以进行本地缓存,代码如下:

      private void base64Object() {
          Object obj = new Object();
          ByteArrayOutputStream bo = new ByteArrayOutputStream();
          ObjectOutputStream oo;
          try {
              oo = new ObjectOutputStream(bo);
              oo.writeObject(obj);
          } catch (IOException e) {
              e.printStackTrace();
          }
      
          byte[] encode = Base64.encode(bo.toByteArray(), Base64.DEFAULT);
          tv_result.setText(new String(encode));
      }
      

Android中的数字摘要,加密和解密

  • 数字摘要:是指通过算法将长数据变为短数据,通常用来标识数据的唯一性,是否被修改,常用的加密算法有md5和sha1,如安卓app的签名也是用这2种算法计算的;

    • md5由于具有不可逆性,也被用来作为密码加密,并且通常情况下为了让加密过程变的不可预测,我们会进行加盐操作,代码如下:

      /**
       * 使用md5方式进行加密
       * @return
       */
      public static String digest(String content){
          StringBuilder builder = new StringBuilder();
          try {
              MessageDigest msgDitest = MessageDigest.getInstance("MD5");
              msgDitest.update(content.getBytes());
              byte[] digests = msgDitest.digest();
              //将每个字节转为16进制
              for (int i=0;i<digests.length;i++){
                  builder.append(Integer.toHexString(digests[i] & 0xff +8));//+8为加盐操作
              }
          } catch (NoSuchAlgorithmException e) {
              e.printStackTrace();
          }
          return  builder.toString();
      }
      
    • sha1也不可逆,比md5长度更长,所以更安全,但是加密的效率比md5要慢一些,如文件的秒传功能,以及相同的v4包冲突都是根据文件的sha1值进行比对的。
  • 加密和解密,一般分为对称加密和非对称加密

    • 对称加密:
      • 密钥可以自己指定,只有一把密钥,如果密钥暴露,文件就会被暴露,
      • 常见对称加密算法有DES算法(Data Encryption Standard)和AES算法(Advanced Encryption Standard),
      • 特点是加密速度很快,但是缺点是安全性较低,因为只要密钥暴漏,数据就可以被解密了。
    • 非对称加密:有两把钥匙(密钥对),公钥私钥,公钥的话给别人,私钥自己保存
      • 常见非对称加密算法是RSA
      • 2把密钥通常是通过程序生成,不能自己指定
      • 特点是加密速度慢些,但是安全系数很高
      • 加密和解密的规则是:公钥加密只能私钥解密,私钥加密只能公钥解密
      • 应用场景举例:在集成支付宝支付sdk时,需要生成私钥和公钥,公钥需要设置到支付宝网站的管理后台,在程序中调用支付接口的时候,使用我们自己的私钥进行加密,这样支付宝由于有公钥可以解密,其他人即时劫持了数据,但是没有公钥,也无法解密。
  • 代码实践,使用hmAndroidUtils中的工具类进行操作

    • DES加密和解密的代码如下:

      String data = "我是xxx";
      String desKey = "青龙偃月刀";// 密钥,口号
      boolean isDesEncrypt = false;
      private void useDes() {
          try {
              if(isDesEncrypt){
                  //解密
                  text.setText(Des.decrypt(text.getText().toString(), desKey));
              }else {
                  //加密
                  text.setText(Des.encrypt(data, desKey));
              }
              isDesEncrypt = !isDesEncrypt;
      
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      
    • RSA加密和解密的代码如下:

      //1.生成密钥对,设计口号
      try {
          Map<String, Object> genKeyPair = RSACrypt.genKeyPair();
          //2.获取公钥
          publicKey = RSACrypt.getPublicKey(genKeyPair);
          //3.获取私钥
          privateKey = RSACrypt.getPrivateKey(genKeyPair);
      } catch (Exception e) {
          e.printStackTrace();
      }
      private boolean isRSAEncrypt = false;
      protected void useRSA() {
          try {
              if(isRSAEncrypt){
                  //公钥解密
                  String str = text.getText().toString();
                  byte[] bs = RSACrypt.decryptByPublicKey(RSACrypt.decode(str), publicKey);
                  text.setText(new String(bs));
              }else {
                  //私钥加密
                  byte[] bs = RSACrypt.encryptByPrivateKey(data.getBytes(), privateKey);
                  text.setText(RSACrypt.encode(bs));
              }
              isRSAEncrypt = !isRSAEncrypt;
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      

Android中集成第三方支付

支付宝的集成流程

  • 首先,申请移动支付权限
    • 首先登录【支付宝开放平台】http://open.alipay.com/platform/home.htm,添加应用,申请移动支付权限
    • 申请开通支付,是需要公司文件的,个人是不允许开始支付的
    • 申请成功后,使用支付宝提供的工具生成RSA公钥和私钥,公钥需要设置到管理后台,具体操作请登录官网
  • 第二步,拼接符合支付宝规范的请求参数,主要有以下几点:

    • 先拼接订单信息,,如下:

      // 签约合作者身份ID
      String orderInfo = "partner=" + "\"" + PARTNER + "\"";
      // 签约卖家支付宝账号
      orderInfo += "&seller_id=" + "\"" + SELLER + "\"";
      // 商户网站唯一订单号
      orderInfo += "&out_trade_no=" + "\"" + getOutTradeNo() + "\"";
      ...
      
    • 然后并对订单信息使用私钥进行RSA加密,代码如下:

      //3.对订单信息使用私钥进行RSA加密
      String sign = sign(orderInfo);
      
    • 最后组织请求参数,如下:

      String payInfo = orderInfo + "&sign=\"" + sign + "\"&" + getSignType();
      
    • 但是,上面的3个步骤,由于涉及到私钥加密,如果写在本地容易暴露私钥,所以一般会让服务器提供一个提交确认订单的接口,客户端将订单相关参数传递给服务器,服务器负责拼接支付宝的请求参数,然后返回给客户端,所以上面的步骤其实不用我们来做。
  • 第三步,调用支付api,传入请求参数进行支付请求:

    // 构造PayTask 对象
    PayTask alipay = new PayTask(PayDemoActivity.this);
    // 调用支付接口,获取支付结果
    String result = alipay.pay(payInfo, true);
    
  • 最后,接收支付结果,根据支付状态进行UI提示:

    String resultInfo = payResult.getResult();// 同步返回需要验证的信息
    String resultStatus = payResult.getResultStatus();
    // 判断resultStatus 为“9000”则代表支付成功,具体状态码代表含义可参考接口文档
    if (TextUtils.equals(resultStatus, "9000")) {
        Toast.makeText(PayDemoActivity.this, "支付成功", Toast.LENGTH_SHORT).show();
    } else {
        // 判断resultStatus 为非"9000"则代表可能支付失败
        // "8000"代表支付结果因为支付渠道原因或者系统原因还在等待支付结果确认,最终交易是否成功以服务端异步通知为准(小概率状态)
        if (TextUtils.equals(resultStatus, "8000")) {
            Toast.makeText(PayDemoActivity.this, "支付结果确认中", Toast.LENGTH_SHORT).show();
    
        } else {
            // 其他值就可以判断为支付失败,包括用户主动取消支付,或者系统返回的错误
            Toast.makeText(PayDemoActivity.this, "支付失败", Toast.LENGTH_SHORT).show();
    
        }
    }
    

微信支付的集成流程

整体步骤可以说,和支付宝一致的

  • 首先,获取符合微信支付规范的请求参数,demo中是通过一个url获取的,这告诉我们,应该让服务器提供一个接口,来对订单信息进行加密然后返回。代码如下:

    String url = "http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=android";
    byte[] buf = Util.httpGet(url);
    
  • 第二步,对请求参数进行封装:

    PayReq req = new PayReq();
    //req.appId = "wxf8b4f85f3a794e77";  // 测试用appId
    req.appId           = json.getString("appid");
    req.partnerId       = json.getString("partnerid");
    req.prepayId        = json.getString("prepayid");
    req.nonceStr        = json.getString("noncestr");
    req.timeStamp       = json.getString("timestamp");
    req.packageValue    = json.getString("package");
    req.sign            = json.getString("sign");
    req.extData         = "app data"; // optional
    
  • 第三步,调用支付api,传入请求参数:

    // 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信
    api.sendReq(req);
    
  • 最后,接收支付结果,根据状态码进行UI提示,此处在demo中是在WXPayEntryActivity中进行操作的。

    public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler{
        private static final String TAG = "MicroMsg.SDKSample.WXPayEntryActivity";
        private IWXAPI api;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.pay_result);
            api = WXAPIFactory.createWXAPI(this, Constants.APP_ID);
            api.handleIntent(getIntent(), this);
        }
        @Override
        protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
            setIntent(intent);
            api.handleIntent(intent, this);
        }
        @Override
        public void onReq(BaseReq req) {
        }
        @Override
        public void onResp(BaseResp resp) {
            if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle(R.string.app_tip);
                builder.setMessage(getString(R.string.pay_result_callback_msg, String.valueOf(resp.errCode)));
                builder.show();
            }
        }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值