Java 微信登陆逻辑记录

Java 微信登陆逻辑记录

六月 24, 2019

Java 微信登陆

code、encryptedData、iv:
需要前端通过wx.login获取code,wx.getUserInfo获取encryptedData用户数据加密、iv偏移向量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// /game-catch/ ... /com.xxxx.appserver/WeixinController.java
public UserDto login(String code, String encryptedData, String iv) throws Exception {

// code、encryptedData、iv
// 需要前端通过wx.login获取code,wx.getUserInfo获取encryptedData用户数据加密、iv偏移向量
WxSessionModel wxSession = wxClientApi.getSessionKey(WxSessionModel.class, code);
WxUserModel wxUser = wxClientApi.getUserInfo(WxUserModel.class, wxSession.getSession_key(), encryptedData, iv);
UserDto user = proxy.queryUserByUid(wxUser.getOpenId());
if (user == null) {
user = registUser(wxUser);
} else {
user.setName(wxUser.getNickName());
user.setAvatarUrl(wxUser.getAvatarUrl());
user.setSex(wxUser.getGender());
reactor.notify("updateUserHandler", Event.wrap(user));
}
//缓存用户
proxy.saveUserByUid(user);
log.info("用户登录:" + user);
return user;
}

getSessionKey(Class resultClass, String code)

请求地址:api.weixin.qq.com/sns/jscode2session

根据前端传的code请求微信接口获取session

checkCode(HttpResponse response),对请求微信接口的结果做异常处理,有errcode!=0和,httpcode!=200的时候抛异常

1
2
3
4
5
6
7
8
9
10
11
12
public <T> T getSessionKey(Class<T> resultClass, String code) throws Exception {

// makeHttpsUrl方法根据传入的字符串去application-local.yml获取配置信息,如下yml配置
HttpRequest request = new HttpRequest(makeHttpsUrl("sessionPath"));
request.addParameter("appid", getProperties("AppID"));
request.addParameter("secret", getProperties("AppSecret"));
request.addParameter("js_code", code);
request.addParameter("grant_type", "authorization_code");
HttpResponse response = clientManager.execute(request);
checkCode(response);
return JsonUtility.toObject(response.getContentString(), resultClass);
}

最终json反序列化,返回这样的类

1
2
3
4
5
6
7
8
public class WxSessionModel {

private Integer errcode; // 返回码
private String errmsg; // 返回错误信息
private String openid; // 用户唯一标识
private String session_key; // 会话密钥
private String unionid; // 用户在开放平台的唯一标识符
}

application-local.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
http:
enabled: true
time-out-socket: 5000
time-out-connection: 2000
time-out-request-connection: 5000
retry-number: 2
max-total: 500
client-properties:
weixin:
host: api.weixin.qq.com
max-pre-route: 100
url-map:
sessionPath: /sns/jscode2session
TokenPath: /cgi-bin/token
ScoreStoragePath: /wxa/set_user_storage
AppID: xxxxxxxxxxx
AppSecret: xxxxxx

getUserInfo(Class resultClass, String sessionKey, String encryptedData, String iv)

解密前端传入的encryptedData加密用户信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public <T> T getUserInfo(Class<T> resultClass, String sessionKey, String encryptedData, String iv) {
// 被加密的数据
byte[] dataByte = Base64.decode(encryptedData);
// 加密秘钥
byte[] keyByte = Base64.decode(sessionKey);
// 偏移量
byte[] ivByte = Base64.decode(iv);

try {
// 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
int base = 16;
if (keyByte.length % base != 0) {
int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte) 0);
System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
keyByte = temp;
}
// 初始化
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
parameters.init(new IvParameterSpec(ivByte));
cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
byte[] resultByte = cipher.doFinal(dataByte);
if (null != resultByte && resultByte.length > 0) {
String result = new String(resultByte, "UTF-8");
return JsonUtility.toObject(result, resultClass);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

解密后,返回这样的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class WxUserModel {
private String nickName; // 用户昵称
private String openId; // openId
private String unionId; // unionId
private String avatarUrl; // 头像地址
private Integer gender = Constant.Gender.Nomal.ordinal(); // 性别
}

// 公共枚举取,详情查看Constant
/**
* 性别
*/
public enum Gender {
Nomal, //未知
Man, //男
Woman //女
}

总结

主要是没有做过获取微信用户信息这一块,后面的操作就是业务逻辑,存在我们自己的数据库了,唯一标识就是wx的openId