微信小程序端发起登录请求,携带的参数主要有:
code:loginRes.code,//临时登录凭证
rawData:infoRes.rawData,//用户非敏感信息
signature:infoRes.signature,//签名
encrypteData:infoRes.encryptedData,//用户敏感信息
iv:infoRes.iv//解密算法的向量
public JSONObject getSessionKeyOrOpenId(String code){
//微信端登录code
String wxCode = code;
String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
Map requestUrlParam = new HashMap( );
requestUrlParam.put( "appid","***" );//小程序appId
requestUrlParam.put( "secret","***" );////小程序secret
requestUrlParam.put( "js_code",wxCode );//小程序端返回的code
requestUrlParam.put( "grant_type","authorization_code" );//默认参数
//发送post请求读取调用微信接口获取openid用户唯一标识
JSONObject jsonObject = JSON.parseObject(HttpClientUtil.httpPost(requestUrl,requestUrlParam));
return jsonObject;
}
由小程序那传输过来的 code,通过 HttpClientUtil 工具,通过个人的appid和secret,从微信服务器获取 openid 和 sessionkey 敏感信息,再sessionKey结合encryptedData和iv,通过解密算法 getUserInfo() 获取用户的私人信息;
public JSONObject getUserInfo(String encryptedData,String sessionKey,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 JSON.parseObject(result);
}
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchPaddingException e) {
} catch (InvalidParameterSpecException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
} catch (UnsupportedEncodingException e) {
} catch (InvalidKeyException e) {
} catch (InvalidAlgorithmParameterException e) {
} catch (NoSuchProviderException e) {
}
return null;
}
结合redis实现自动登录
自动登录的思路:
自动登录的关键是skey,在用户初次登录时,会通过uuid生成唯一key,我们命名为skey,我们同时将skey和openid存入redis,(并设置有效期),以及将skey传到小程序端,每次使用小程序时,会将本地的skey和redis服务器的skey进行辨别,如果不一致,就将重新进入初次登录的流程,以此类推;
为什么要使用redis?
其实完全可以使用mysql,但是考虑到redis的速度以及key-value的优越性,这样存储skey:openid会方便点;
//uuid生成唯一key
String skey = UUID.randomUUID().toString();
//根据openid查询skey是否存在
String skey_redis = (String) redisTemplate.opsForValue().get( openid );
if(StringUtils.isNotBlank( skey_redis )){
//存在 删除 skey 重新生成skey 将skey返回
redisTemplate.delete( skey_redis );
}
// 缓存一份新的
JSONObject sessionObj = new JSONObject( );
sessionObj.put( "openId",openid );
sessionObj.put( "sessionKey",sessionKey );
redisTemplate.opsForValue().set( skey,sessionObj.toJSONString() ,20,TimeUnit.MINUTES);
redisTemplate.opsForValue().set( openid,skey ,20,TimeUnit.MINUTES);
//把新的sessionKey和oppenid返回给小程序
map.put( "skey",skey );
map.put( "result","1" );