typora-root-url: D:\编程项目\typora文档\图片目录
SSM框架下的微信登录
首先看官方给出的登录过程,然后按步骤一个个来
1.获取CODE
首先用微信提供的方法获取code,使用wx.login()方法,还顺便封装了一个微信请求的发起方法
这里封装了俩个方法,在调用登录的时候异步执行
//获取code换session
getCode(){
var that=this;
//回调函数
return new Promise(function(resolve, reject){
wx.login({
success(res) {
console.log('code:'+res.code);
//把获取的code存到本地data以便发送到服务器
that.data.loginData.code = res.code;
resolve();
}
})
})
},
//微信请求封装
getRequest(url,data){
var that=this;
return new Promise(function(resolve,reject){
wx.request({
url: 'http://localhost/BookKeeping/'+url,
//请求数据
data: data,
method: 'POST',
//token验证时携带的数据(暂时不需要)
header: {
Authorization: that.data.token
},
success: function(res){
resolve(res)
},
fail: function() {
reject()
},
complete: function() {
//complete
}
})
})
},
//这样调用就可以把code发出去
userLogin(e){
console.log(e);
var that=this;
this.getCode()
.then(()=>{
console.log(this.data.loginData);
return this.getRequest("api/login",this.data.loginData.code)
})
}
下面就交给服务器处理吧
2.服务器处理发送过来的Code
服务器处理时,我们需要一个工具,向微信服务器发送我们的请求数据
请求的数据内容如下

请求的地址是
GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
那后端该怎么发送这个请求呢?
在这之前,需要导入maven依赖,以支持发起请求对应的包
<!--Https请求依赖-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.3</version>
</dependency>
在原先的ssm框架下,新建一个包,用来存放我们的工具类
这次使用到的是发送http请求的工具,HttpUtil.java
package com.BookKeeping.util;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class HttpUtil {
public static String doGet(String uri) {
//1:创建一个HttpClient的实例
CloseableHttpClient httpclient = HttpClients.createDefault();
//2:创建一个get请求实例
HttpGet httpGet = new HttpGet(uri);
//请求的响应:
CloseableHttpResponse response1 = null;
try {
//3:使用HttpClient的实例执行get请求
response1 = httpclient.execute(httpGet);
//http请求的状态:404 500 200
//System.out.println(response1.getStatusLine());
int statusCode = response1.getStatusLine().getStatusCode();
if (statusCode == 200) {
//请求成功:
HttpEntity entity1 = response1.getEntity();
String result = EntityUtils.toString(entity1, "utf-8");
return result;
} else {
//请求失败
System.out.println("请求失败......:"+statusCode);
}
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public JSONObject domain(String type, String code){
JSONObject jo = JSONObject.parseObject("{'message':'Error',}");
if(type.equals("getSession_key")){
System.out.println("获取session和openid");
String result=doGet("https://api.weixin.qq.com/sns/jscode2session?appid=自己的appid&secret=自己的secret&js_code="+code+"&grant_type=authorization_code");
jo = JSONObject.parseObject(result);
return jo;
}else if(type.equals("getUserData")){
System.out.println("获取用户信息");
}
return jo;
}
}
上面封装的domain方法可以按照传入的方法和code,返回一个请求结果(因为微信返回结果是一个json格式)
这里把返回结果直接转json了
请求结果示例:

3.控制器层处理
直接贴代码
@RequestMapping(value = "/login",method = RequestMethod.POST)
@ResponseBody //使返回的数据生成json格式
public Result login(@RequestBody String code){
Result rs=new Result(); //自定义的结果返回值(详细查看上一篇)
Login result=new Login();//登录数据的实体类,存放登录信息等
System.out.println(code);
//获取微信session和生成自定义token
HttpUtil hrs=new HttpUtil();
//获取session_key和openid
JSONObject session_key=hrs.domain("getSession_key",code);
String session=session_key.getString("session_key");
String openid=session_key.getString("openid");
result.setSession(session);
//把参数放入
rs.setData(result);
return rs;
}
Login的实体类,注意要右键Generate自动生成get和set和tostring,详见上篇
package com.BookKeeping.entity;
public class Login {
private String encryptedData;
private String iv;
private String code;
private String token;
private String session;
}
到这里,我们可以在在微信开发者工具测试这个接口了:

注意这里的session要存起来,微信官方说是有20分钟有效期,后面要使用这个来解密数据
4.获取用户的加密数据
要获取用户加密数据,需要以下数据:

小程序前端调用wx.getuserinfo获得,其中的 加密数据 encrytedData 和加密向量iv需要存起来,给服务器解密
对应的js代码为:
//获取用户数据
getUserInfo() {
var that=this; //否则wx方法内无法访问this
return new Promise(function(resolve, reject){
wx.getUserInfo({
success: function(e) {
console.log(e)
//存储相关数据
app.globalData.userInfo = e.userInfo
that.data.loginData.iv=e.iv;
that.data.loginData.encryptedData=e.encryptedData;
resolve();
}
})
})
},
调用登录的函数改写为下面:
//用户登录操作
userLogin(e){
console.log(e);
var that=this;
this.getCode()
.then(()=>{
return this.getUserInfo()
})
.then(()=>{
console.log(this.data.loginData);
return this.getRequest("api/login",this.data.loginData.code)
})
.then((res)=>{
console.log(res)
that.data.token=res.data.data.token;
that.data.loginData.code=res.data.data.session;
return this.getRequest("api/getUserData",this.data.loginData)
}).then((res)=>{
that.data.userInfo=res.data.data;
that.data.hasUserInfo=true;
})
},
这下,后端就会收到前端发来的三个数据:
- 加密数据
- 加密向量
- session_key
有了这些,我们就开始解密操作
5.解密数据,回发
在这之前,需要导入maven依赖,以支持解密对应的包
<!--微信AES-128-CBC解密依赖-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
</dependency>
<dependency>
<groupId>org.codehaus.xfire</groupId>
<artifactId>xfire-core</artifactId>
<version>1.2.6</version>
</dependency>
同上,导入一个加解密的工具类Aes.java
package com.BookKeeping.common;
import com.alibaba.fastjson.JSONObject;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.Security;
/**
* AES加解密算法
*/
public class Aes {
public static String KEY_ALGORITHM = "AES";
//数据填充方式
String algorithmStr = "AES/CBC/PKCS7Padding";
//避免重复new生成多个BouncyCastleProvider对象,因为GC回收不了,会造成内存溢出
//只在第一次调用decrypt()方法时才new 对象
public static boolean initialized = false;
/**
* AES加密
*/
public byte[] encrypt(byte[] originalContent, byte[] encryptKey, byte[] ivByte) {
initialize();
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec skeySpec = new SecretKeySpec(encryptKey, "AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(ivByte));
byte[] encrypted = cipher.doFinal(originalContent);
return encrypted;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* AES解密
* 填充模式AES/CBC/PKCS7Padding
* 解密模式128
*/
public String decrypt(String encryptedData, String sessionKey, String ivs) {
initialize();
//转换为byte类型以解密
byte[] content= Base64.decode(encryptedData);
byte[] aesKey= Base64.decode(sessionKey);
byte[] ivBytes= Base64.decode(ivs);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
Key sKeySpec = new SecretKeySpec(aesKey, "AES");
cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivBytes));// 初始化
byte[] result = cipher.doFinal(content);
return new String(result);
} catch (Exception e) {
System.out.println("解密失败");
throw new RuntimeException(e);
}
//return "Error";
}
/**BouncyCastle作为安全提供,防止加密解密时候因为jdk内置的不支持改模式运行报错。**/
public static void initialize() {
if (initialized)
return;
Security.addProvider(new BouncyCastleProvider());
initialized = true;
}
// 生成iv
public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
params.init(new IvParameterSpec(iv));
return params;
}
public JSONObject domain(String encryptedData, String sessionKey, String ivs){
JSONObject jo = JSONObject.parseObject(decrypt(encryptedData, sessionKey, ivs));
return jo;
}
}
同上,domain为解密的具体实现,返回的同样被转换为json格式
接下来控制层这样写
@RequestMapping(value = "/getUserData",method = RequestMethod.POST)
public Result getUserData(@RequestBody Login login, @RequestHeader("Authorization") String token){
System.out.println("获取用户数据");
Result rs=new Result();
//调用服务层处理
User us=loginService.getUserData(login.getEncryptedData(),login.getCode(),login.getIv());
System.out.println(us.toString());
rs.setData(us);
return rs;
}
服务层
@Override
public User getUserData(String EncryptedData,String session,String ivs) {
Aes aes=new Aes();
User us=new User(); //具体实体类参考上一篇
//使用session_key获取用户数据
JSONObject userData=aes.domain(EncryptedData,session,ivs);
us.setAvatarUrl(userData.getString("avatarUrl"));
us.setGender(userData.getInteger("gender"));
us.setNickName(userData.getString("nickName"));
us.setOpenId(userData.getString("openId"));
return us;
}
至此,解密工作完成,可以看到这接口返回如下数据

来源:CSDN
作者:ZeroS5A
链接:https://blog.csdn.net/u011478348/article/details/104122197