《Android使用OKhttp3实现登录注册功能+springboot搭建后端》这篇教程介绍了登录注册的流程及基本实现。但是有一些小问题。就是后端处理的时候,URL将用户名密码使用明文加密。这样抓包就很容易获取隐私数据。这篇完善这个问题。例如这个网站,我们进行抓包获取到请求,这里的密码是加密的。
加密算法分为对称加密和非对称加密。这里我们就简单介绍如何使用加密算法来实现这个功能(主要强调的是流程)。
一、Android前端
这里使用AES对称加密算法
1、创建AESHelper.java
import java.io.UnsupportedEncodingException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
///** AES对称加密解密类 **/
public class AESHelper {
// /** 算法/模式/填充 **/
private static final String CipherMode = "AES/ECB/PKCS5Padding";
///** 创建密钥 **/
private static SecretKeySpec createKey(String password) {
byte[] data = null;
if (password == null) {
password = "";
}
StringBuffer sb = new StringBuffer(32);
sb.append(password);
while (sb.length() < 32) {
sb.append("0");
}
if (sb.length() > 32) {
sb.setLength(32);
}
try {
data = sb.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return new SecretKeySpec(data, "AES");
}
// /** 加密字节数据 **/
public static byte[] encrypt(byte[] content, String password) {
try {
SecretKeySpec key = createKey(password);
System.out.println(key);
Cipher cipher = Cipher.getInstance(CipherMode);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(content);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
///** 加密(结果为16进制字符串) **/
public static String encrypt(String content, String password) {
byte[] data = null;
try {
data = content.getBytes("UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
data = encrypt(data, password);
String result = byte2hex(data);
return result;
}
// /** 解密字节数组 **/
public static byte[] decrypt(byte[] content, String password) {
try {
SecretKeySpec key = createKey(password);
Cipher cipher = Cipher.getInstance(CipherMode);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(content);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
///** 解密16进制的字符串为字符串 **/
public static String decrypt(String content, String password) {
byte[] data = null;
try {
data = hex2byte(content);
} catch (Exception e) {
e.printStackTrace();
}
data = decrypt(data, password);
if (data == null)
return null;
String result = null;
try {
result = new String(data, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
// /** 字节数组转成16进制字符串 **/
public static String byte2hex(byte[] b) { // 一个字节的数,
StringBuffer sb = new StringBuffer(b.length * 2);
String tmp = "";
for (int n = 0; n < b.length; n++) {
// 整数转成十六进制表示
tmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (tmp.length() == 1) {
sb.append("0");
}
sb.append(tmp);
}
return sb.toString().toUpperCase(); // 转成大写
}
// /** 将hex字符串转换成字节数组 **/
private static byte[] hex2byte(String inputString) {
if (inputString == null || inputString.length() < 2) {
return new byte[0];
}
inputString = inputString.toLowerCase();
int l = inputString.length() / 2;
byte[] result = new byte[l];
for (int i = 0; i < l; ++i) {
String tmp = inputString.substring(2 * i, 2 * i + 2);
result[i] = (byte) (Integer.parseInt(tmp, 16) & 0xFF);
}
return result;
}
}
2、修改MainActivity.java
这里修改login方案的算法
public void login(View view) {
loginUser = new LoginUser();
loginUser.setUserName(username.getText().toString());
loginUser.setUserPassword(password.getText().toString());
new Thread(new Runnable() {
@Override
public void run() {
MediaType JSON = MediaType.parse("application/json;charset=utf-8");
JSONObject jsonObject = new JSONObject();
OkHttpClient httpClient = new OkHttpClient();
try {
jsonObject.put("userName",loginUser.getUserName());
jsonObject.put("userPassword",loginUser.getUserPassword());
} catch (JSONException e) {
e.printStackTrace();
}
//7.29 对数组进行加密解密,后端解密,前端加密,秘钥为"key",这里的key自行设定与后端对应即可;
String encrypt = AESHelper.encrypt(jsonObject.toString(), "key");
// RequestBody requestBody = RequestBody.create(JSON, String.valueOf(jsonObject));
RequestBody requestBody = RequestBody.create(JSON, encrypt);
Log.d("前端测试发送的jsonObject:",jsonObject.toString());
Log.d("encrypt:",encrypt);
Log.d("前端测试发送的requestBody:",requestBody.toString());
// String url = "http://ip:8001/server/user/login";
String url = "http://ip:8001/server/user/loginaes";
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
Log.d("前端测试发送的request:",request.toString());
Call call = httpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d("whq登录","失败了");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String MyResult = response.body().string();
/* Gson gson = new GsonBuilder().create();
ResultGson resultGson = gson.fromJson(MyResult, ResultGson.class);
Log.d("whq登录",resultGson.getData().get("result")+"---------resultGson---------");
Log.d("whq登录",resultGson.toString()+"---------resultGson---------");*/
/* Log.d("whq登录","----------------------------");
Map<String, Object> data = new HashMap<String, Object>();
data.put("result","success login");
ResultGson json = new ResultGson(true, 20000, "成功", data);
Log.d("原始json",json.toString());
Gson gson1 = new GsonBuilder().create();
String toJson = gson1.toJson(json);
Log.d("转化之后的json",toJson);*/
Log.d("whq登录",response+"---------response---------");
Log.d("whq登录",response.message()+"---------message---------");
Log.d("whq登录",response.body().toString()+"------------------");
Log.d("whq登录",MyResult+"-----------MyResult-------");
Log.d("whq登录",MyResult.contains("success login")+"-----------MyResult.length()-------");
}
});
}
}).start();
}
注意这里修改了三处地方:
//7.29 对数组进行加密解密,后端解密,前端加密,秘钥为key;
String encrypt = AESHelper.encrypt(jsonObject.toString(), "key");
//RequestBody requestBody = RequestBody.create(JSON, String.valueOf(jsonObject));
RequestBody requestBody = RequestBody.create(JSON, encrypt);
//String url = "http://ip:8001/server/user/login";
String url = "http://ip:8001/server/user/loginaes";
将json加密成字符串,这里的key自行设定,需要与后端对应解密。使用新的URL请求地址。这样前端就修改完成,就是这么简单。
二、服务器后端
1、创建AESHelper.java类
这个类与Android端的工具类一样,复制过来就行。
1、修改UserController.java
@PostMapping("loginaes")
public R loginAES(@RequestBody String Aes){
String decrypt = AESHelper.decrypt(Aes,"key");
System.out.println("decrypt"+decrypt);
Gson gson = new GsonBuilder().create();
loginUser loginUser = gson.fromJson(decrypt, loginUser.class);
System.out.println("loginUser"+loginUser.toString());
System.out.println("loginUser"+loginUser);
String token = userService.login(loginUser);
if (token == "wrong Password"){
return R.error().data("result",token);
}else{
return R.ok().data("result",token);
}
}
这里重新修改接口,定义一个loginaes的接口,将参数先解密,这里的key要对应起来,由于是字符串,我们需要将字符串转为json,这里使用GSon工具,具体介绍看这篇文章《Android解析json-Gson使用》,这里还要有一个loginUser的实体类
loginUser.java
@Data
public class loginUser {
private String userName;
private String userPassword;
}
其他的地方不需要改动。
三、启动项目
使用maven将项目打包部署在服务器上,参考之前的教程,安装APP进行调试
抓包:
这样就解决了明文传送。当然还可以使用其他的非对称加密算法,生成双钥就可以了(比如RSA算法)。也可以继续改进加密的功能,这里只是粗略的介绍一下思想。