Java 微信登陆逻辑记录

170 阅读2分钟

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

// /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的时候抛异常

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反序列化,返回这样的类

public class WxSessionModel {

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

application-local.yml

 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加密用户信息

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;
}

解密后,返回这样的类

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