http请求数据无论是GET或者POST都可能会被抓包获取到数据。为了避免用户的敏感数据被窃取(比如密码),需要对数据进行加密处理。

一、相关名词解析

RSA:非对称加密。
会产生公钥和私钥,公钥在客户端,私钥在服务端。公钥用于加密,私钥用于解密。
优势在于 不需要共享私钥,避免了私钥泄露的风险。
劣势在于 加密效率低,数据量大时耗时也大。

AES:对称加密。
客服端和服务器端都使用同一个秘钥来进行加密和解密。
优势在于 加密效率高
缺点在于 秘钥需要共享给客户端,具有泄露的风险

MD5:MD5信息摘要算法
只能加密,不能解密。

Base64编码:对字节数组转换成字符串的一种编码方式。

二、基本逻辑

整体流程:客户端获得数据–>对数据进行加密–>上传加密数据–>服务器获得数据–>对数据进行解密
需要考虑:
1、密码不能明文传输,即需要对明文进行加密。
2、参数不能被中途截取修改,即需要对参数进行验证。

三、对明文进行加密—Cipher

javax.crypto.Cipher 类中提供了对明文的加密和解密功能。

Cipher可以使用RSA加密方式,即非对称加密,所以需要生成公钥与私钥。生成的公钥放到客户端中用于加密,私钥放在服务器上用于解密。
生成公钥和私钥 示例代码:
提示:如果出现找不到Base64.encode方法时,你可能需要引入com.sun.jersey.jersey-core.jar包(提取码:4lvj )

/**
* 生成公和私钥
* */
public static void generater() {try {java.security.KeyPairGenerator keygen = java.security.KeyPairGenerator.getInstance("RSA");SecureRandom secrand = new SecureRandom();secrand.setSeed("ye23".getBytes()); // 初始化随机产生器keygen.initialize(1024, secrand);KeyPair keys = keygen.genKeyPair();PublicKey pubkey = keys.getPublic();PrivateKey prikey = keys.getPrivate();String pubkeySrt = new String(Base64.encode(pubkey.getEncoded()));String prikeySrt = new String(Base64.encode(prikey.getEncoded()));LocalLogUtils.d("pubKey = " + pubkeySrt,true); //公钥LocalLogUtils.d("priKey = " + prikeySrt,true); //私钥} catch (java.lang.Exception e) {System.out.println("生成密钥对失败");e.printStackTrace();}
}

客户端明文加密 示例代码:

private static final String ALGORITHM = "RSA"; //所使用算法
private static final String PublicKey = Info.loginPublicKey; //公钥public static String encrypt(String content) {try {PublicKey pubkey = getPublicKeyFromX509(ALGORITHM, PublicKey);Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");//生成Cipher对象cipher.init(Cipher.ENCRYPT_MODE, pubkey);//操作模式为加密(Cipher.ENCRYPT_MODE),pubkey为公钥byte plaintext[] = content.getBytes("UTF-8");byte[] output = cipher.doFinal(plaintext);//得到加密后的字节数组String s = new String(android.util.Base64.encode(output, android.util.Base64.DEFAULT));return s;} catch (Exception e) {e.printStackTrace();return null;}
}private static PublicKey getPublicKeyFromX509(String algorithm, String bysKey)throws NoSuchAlgorithmException, Exception {byte[] decodedKey = android.util.Base64.decode(bysKey, android.util.Base64.DEFAULT);X509EncodedKeySpec x509 = new X509EncodedKeySpec(decodedKey);KeyFactory keyFactory = KeyFactory.getInstance(algorithm);return keyFactory.generatePublic(x509);
}

服务端解密 示例代码:

private static final String PrivateKey = Info.loginPrivateKey; //私钥
public static String reEncrypt(String encryptStr) {try {PrivateKey privateKey = getPrivateKey(PrivateKey);Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");cipher.init(Cipher.DECRYPT_MODE, privateKey);byte[] output =  cipher.doFinal(android.util.Base64.decode(encryptStr, android.util.Base64.DEFAULT));return new String(output);} catch (Exception e) {e.printStackTrace();return null;}
}private static PrivateKey getPrivateKey(String priKey) throws NoSuchAlgorithmException,InvalidKeySpecException, NoSuchProviderException {byte[] keyBytes = android.util.Base64.decode(priKey, android.util.Base64.DEFAULT);PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance("RSA");PrivateKey privateKey = keyFactory.generatePrivate(keySpec);return privateKey;
}

四、对参数进行验证–请求参数的签名sign

避免请求参数被修改,保证了请求数据的一致性。需要客户端和服务端约定一个签名生成算法。
客户端在请求接口之前调用签名算法,根据参数生成sign值。然后把sign和请求参数一并传给服务器。
服务器收到到参数和签名之后,使用同样的签名算法和参数 生成sign,并比对两个sign是否一致。如果一致就说明参数未被修改。
签名算法示例代码:

/**
* 使用MD5加密
* 对键进行升值排序
* 把生成的md5统一转成大写
*/
public static String md5Decode32(Map<String, String> data, String time) {String key=Info.md5secretkey; // key,和后台保持一致String content = "";for (String keyname:data.keySet()) {content=content+keyname+ data.get(keyname);}content=content+time+ key; //注意:time一定要传进来,不可以在方法中直接生成。不然时间是对不上的byte[] hash;try {hash = MessageDigest.getInstance("MD5").digest(content.getBytes("UTF-8"));} catch (NoSuchAlgorithmException e) {throw new RuntimeException("NoSuchAlgorithmException",e);} catch (UnsupportedEncodingException e) {throw new RuntimeException("UnsupportedEncodingException", e);}//对生成的16字节数组进行补零操作StringBuilder hex = new StringBuilder(hash.length * 2);for (byte b : hash) {if ((b & 0xFF) < 0x10){hex.append("0");}hex.append(Integer.toHexString(b & 0xFF));}return hex.toString().toUpperCase();//返回纯大写的MD5
}

五、登录

登录可以分为账密登录和自动登录。
第一次账密登录成功之后,后台可以生成并返回一个token。后续的一段时间内,可以直接通过token自动登录。免去输入账号密码的麻烦。
账号登录示例代码(客户端):

@OnClick(R2.id.btn_sign_in)
void onClickSignIn(){if(checkForm()){ //检查账密格式是否正确//使用Cipher对密码进行加密String encryptPassword;encryptPassword = CipherUtil.encrypt(password);//生成MD5 sign,避免请求参数被恶意修改。保证了请求数据的一致性Map<String, String> MD5_Parameter_Map = new TreeMap<String, String>(); //TreeMap为有序集合,默认是升序MD5_Parameter_Map.put("userName", name);MD5_Parameter_Map.put("userPassword", encryptPassword);String time = System.currentTimeMillis()+"";String sign = MD5Sign.md5Decode32(MD5_Parameter_Map,time);//上传数据RestClient.builder().url(AppInfo.API_LOGIN).params("userName",name).params("userPassword",encryptPassword).params("signTime",time) //要把时间也传上后台,不能直接在后台生成时间,不然生成的sign不一致.params("sign",sign).success(new ISuccess() {@Overridepublic void onSuccess(String response) {}}).failure(new IFailure() {@Overridepublic void onFailure(String msg) {}}).error(new IError() {@Overridepublic void onError(int code, String msg) {}}).build().post();}
}

账号登录示例代码(服务端):

@RequestMapping("/login")
public ApiResult login(String userName,String userPassword,String signTime,String sign) {int code = 0;String status = "unknown error";UserDao data = new UserDao();try {//生成sign对请求数据的一致性进行校验Map<String, String> MD5_Parameter_Map = new TreeMap<String, String>();MD5_Parameter_Map.put("userName", userName);MD5_Parameter_Map.put("userPassword", userPassword);String checkSign = MD5SignUtil.md5Decode32(MD5_Parameter_Map,signTime);//校验不通过直接返回错误if(!checkSign.equals(sign)){ApiResult apiResult = new ApiResult();apiResult.setCode(ApiCallback.SignIsError);apiResult.setStatus(status);return apiResult;}//判断sign是否超过有效时间long time= System.currentTimeMillis();long signTimeToLong = Long.parseLong(signTime);long interval = Math.abs(time-signTimeToLong);if(interval > validityTime){ApiResult apiResult = new ApiResult();apiResult.setCode(ApiCallback.SignIsTimeout);apiResult.setStatus("Sign is timeout");return apiResult;}//调用Cipher对密码进行解密userPassword = CipherUtil.reEncrypt(userPassword);UserController.getInstance().init(userRepository);if(userName.isEmpty()){ //userName不能为空code = ApiCallback.InputUserIsNull;status = "User name can not be null";}else if(userPassword.isEmpty()){ //userPassword也不能为空code = ApiCallback.InputPasswordIsNull;status = "Password can not be null";} else if(!UserController.getInstance().isUserNameExist(userName)){//判断名字是否存在code = ApiCallback.UserIsNotExist;status = "User name is not exist.";}else if(!UserController.getInstance().getPassword(userName).equals(userPassword)){//判断密码是否正确code = ApiCallback.EntryPasswordError;status = "Input password is error.";}else {//一切正常,更新数据库登录信息,并返回token等信息data = UserController.getInstance().doLogin(userName);code = ApiCallback.Success;status = "Register success";}}catch (Exception e){//报错了,返回错误信息status=String.valueOf(e);}ApiResult apiResult = new ApiResult();apiResult.setCode(code);apiResult.setStatus(status);apiResult.setData(data);//返回数据return apiResult;
}

上一篇:游戏社区App (二):网络请求框架的封装
下一篇:游戏社区App (四):底部导航栏

游戏社区App (三):客户端与服务端的加密处理 和 登录相关推荐

  1. 学游戏开发,从客户端还是服务端开始?

    ##前言 近几年,游戏开发行业风生水起,入行的个个都赚个盆满钵满,这种现状反过来又吸引着更多人源源不断地进行这个行业. 那么,对于刚刚转行到游戏开发或者有意转行的朋友来说,应该从哪里开始学起呢? 这个 ...

  2. 浅谈客户端与服务端的加密通讯(HTTPS/AES/RSA/RequestBodyAdviceAdapter/ResponseBodyAdvice)

    目录 前言 HTTPS与SSL证书 AES对称加密 RSA非对称加密 AES + RSA 组合加密 服务端请求参数解密拦截器RequestBodyAdviceAdapter 服务端返回参数加密拦截器R ...

  3. 精品手游小精灵大作战源码 安卓+IOS+H5三端Cocos Creator 客户端 JAVA服务端

    小精灵大作战 数据库说明 数据库使用MySQL,推荐管理软件Navicat For MySQL. 创建数据库命名为pet_battle,字符集选用utf8 -- UTF-8 Unicode,排序规则选 ...

  4. 基于ET框架致敬LOL的Moba游戏源码,包含完整的客户端与服务端交互

    运行环境 编辑器:Unity 2020.3.12 LTS 客户端:.Net Framework 4.7.2 IDE:JetBrain Rider 2020 服务端:.Net Core 3.1 已实现功 ...

  5. 为什么 TCP 三次握手期间,客户端和服务端的初始化序列号要求不一样?

    大家好,我是小林. 为什么 TCP 三次握手期间,客户端和服务端的初始化序列号要求不一样的呢? 接下来,我一步一步给大家讲明白,我觉得应该有不少人会有类似的问题,所以今天在肝一篇! 正文 为什么 TC ...

  6. QT中使用C++ socket通信(了解socket通信、socket的三次握手和四次挥手、socket函数说明、客户端与服务端的代码实例)

    一.TCP/IP协议四个抽象层: 二.socket位置 socket就在应用程序的传输层和应用层之间,传输层的底一层的服务提供给socket抽象层,socket抽象层再提供给应用层. 三.socket ...

  7. mysql服务器是否支持tcp/ip连接,(3)MySQL客户端与服务端的TCP/IP及socket连接方式-Go语言中文社区...

    MySQL客户端与服务端的TCP/IP及socket连接方式 客户端与服务器模型 客户端与服务端模型 TCP/IP方式连接 解释说明 TCP/IP套接字方式是MySQL在任何平台下都提供的连接方式,也 ...

  8. 客户端渲染 服务端渲染_这就是赢得客户端渲染的原因

    客户端渲染 服务端渲染 A decade ago, nearly everyone was rendering their web applications on the server using t ...

  9. 【学习笔记】在windows下进行基于TCP的本地客户端和服务端socket通信

    文章目录 socket介绍 java中使用socket 基于tcp的socket通信 使用ServerSocket类创建一个web服务器:(java) windows下的基于tcp的socket编程( ...

最新文章

  1. 2021年大数据Hadoop(二十九):​​​​​​​关于YARN常用参数设置
  2. python字符编码是什么_python3字符编码是什么?怎么用?
  3. Xamarin 2017.10.9更新
  4. [异能程序猿]第一章 酒后事发(第一更)
  5. python中国大学排名爬虫写明详细步骤-Python爬虫——定向爬取“中国大学排名网”...
  6. 利用OpenCV的VideoCapture类实现视频读操作
  7. 信息熵和交叉熵的细节理解
  8. docker新建Linux虚拟机,RHEL/CentOS 7下创建你的第一个Docker容器
  9. Java 守护线程概述
  10. linux git reposi,关于linux:GitLab:无法从远程存储库读取
  11. iOS之深入解析静态库和动态库
  12. IBM计划未来四年在美聘用2000名退伍军人
  13. 童继龙:论ERP顾问的创新分享与专业精神
  14. java 多线程操作List,已经做了同步synchronized,还会有ConcurrentModificationException,知道为什么吗?...
  15. html5 粽子飘落,飘落的丁香花阅读*
  16. 解决Nextcloud新建用户默认语言是英文且默认地区是美国的方法
  17. sgu 309 Real Fun
  18. Debian Linux及kali程序安装卸载方式
  19. WIN10安装与升级的方法
  20. 计算机科学与技术研究生推荐读物,2017年中山大学0812计算机科学与技术考研参考书目推荐...

热门文章

  1. Ubuntu系统耳机没声音
  2. 宠物医院称可给怀孕宠物剖腹产 医生一对一护理
  3. 喜欢听音乐CD的请进:[技术贴]介绍APE+CUE格式的音乐文件
  4. Milvus 群星闪耀时|又一个小目标达成 :社区正式突破 15,000 星!
  5. java计算机毕业设计智友少儿编程学习平台源码+mysql数据库+系统+部署+lw文档
  6. python 自动化微信小程序_干货 | 微信小程序自动化测试最佳实践(附 Python 源码)...
  7. 聚观早报 | 元旦机票预订量增长145%;小米集团副总裁崔宝秋离职
  8. Debian / Ubuntu下轻松切换GDM, LightDM , KDM
  9. 如何在App Store用流量下载超过150M的软件?
  10. 计算机方向键是哪个键,你可能从来没碰过的键,电脑键盘方向键上面的3个按键有什么用?...