基本顺序
1.微信小程序的appId
2.微信小程序的secret
3.ip在微信公众号里备案
4.添加业务域名会访问项目中的MP_verify_fQ6FF0R8GZHgK5Kl.txt (程序实现这个文件)
5.wx.login()获取微信给的临时code 5分钟有效期
6.code2ession() 接口,使用code 和appid 和secret 换取用户的唯一标识openid和会话秘钥session_key
1.微信小程序的appId(去往小程序)
设置=====>开发设置 ====>appId
2.微信小程序的secret
设置=====>开发设置=====>secret
3.ip在公众号中备案
基本设置====>IP白名单 可以设置多个ip 使用回车分隔
4.添加业务域名
点击公众号头像===>功能设置====>业务域名
设置自己的域名然后下载文件
这里使用代码替代
域名设置 你的域名 xxx.com\mini 你的项目必须是启动 ,微信会去验证使用get方式
/**
* @date: 2018/11/2 11:33
* @author: YINLELE
* @description:用于给微信验证用的
*/
@Controller
@RequestMapping("/mini")
public class MiniWxController {
Logger logger = LoggerFactory.getLogger(MiniWxController.class);
/*用于微信验证MVP文件*/
@RequestMapping("MP_verify_{path}.txt")
public void index(@PathVariable String path, HttpServletResponse response){
try {
PrintWriter writer = response.getWriter();
writer.print(path);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
5.wx.login()获取微信给的临时code 5分钟有效期
1.使用wx.login()获取微信用户的code(点击即可)
这里面介绍了小程序登录以及加解密的原理和实现
2.这是获取session_key用于解密用户的加密信息的(点击即可)
小程序代码 wxml
<view class="login-container">
<view class="login" wx:if="{{ !logged }}">
<view class="app-info">
<image class="app-logo" src="{{logo}}" />
<text class="app-name">{{title}}</text>
</view>
<view class='form-item'>
<input class='username' value='{{phone}}' bindinput='getPhone' placeholder='请输入手机号'>
</input>
</view>
<button class="weui-btn" type="primary" open-type="getUserInfo" bindgetuserinfo="login">确认授权</button>
</view>
<view class="logged" wx:else>
<image class="logged-icon" src="../../images/iconfont-weixin.png" />
<view class="logged-text">近期你已经授权登陆过{{title}}</view>
<view class="logged-text">自动登录中</view>
</view>
</view>
小程序的js 我使用的是请求是封装好的 我又获取了用户的手机号 所以多个属性 一般解密用户信息在后台 解析
const app = getApp();
var httpUtil = require("../../../utils/httpUtil.js");
var api = require("../../../config/api.js");
Page({
data: {
phone: "",
fail: "网络繁忙"
},
onLoad: function() {
},
getPhone: function(e) {
this.setData({
phone: e.detail.value
});
console.log(this.data.phone);
},
login: function(e) {
var that = this;
wx.login({
success: function(r) {
var code = r.code; //登录凭证
console.log(code)
if (code) {
//2、调用获取用户信息接口
wx.getUserInfo({
success: function(res) {
console.log(JSON.stringify("获取的用户信息:===>" + that.data.phone));
var data = {
"code": r.code,//用户code
"encryptedData": res.encryptedData,//用户信息不过被加密 需要使用session_key解密
"iv": res.iv,//参与解密用户信息的向量
"phone": that.data.phone//手机号
};
//3.请求自己的服务器,解密用户信息 获取unionId等加密信息
httpUtil.http_post(api.LoginCodeUrl, data).then((res) => {
console.log("用户信息====>" + JSON.stringify(res));
if (res.status == 80200) {
app.globalData.userInfo = res.data;
wx.setStorageSync('userInfo', res.data);
wx.switchTab({
url: '/pages/index/index',
})
} else {
wx.showToast({
title: that.data.fail,
icon: "loading",
duration: 2000
})
}
});
},
fail: function() {
console.log('获取用户信息失败')
}
})
} else {
console.log('获取用户登录态失败!' + r.errMsg)
}
},
fail: function() {
console.log('登陆失败')
}
})
}
})
小程序的wxss
.login-container {
height: 100%;
padding: 10px 30px;
background: #fff;
}
.app-info {
position:relative;
padding:20px;
text-align:center;
}
.app-info:after {
content: " ";
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 1px;
border-bottom: 1px solid #E5E5E5;
color: #E5E5E5;
-webkit-transform-origin: 0 100%;
transform-origin: 0 100%;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
.app-info .app-logo {
display: block;
width: 64px;
height: 64px;
margin: 10px auto;
border-radius: 4px;
}
.app-info .app-name {
font-weight: bold;
font-size: 18px;
color: #000;
}
.alert {
margin: 20px 0 30px;
}
.alert .alert-title {
font-weight: 400;
font-size: 16px;
color: #000;
margin-bottom: 10px;
}
.alert-desc {
display: block;
list-style: disc inside none;
}
.alert .alert-text {
display: list-item;
text-align: -webkit-match-parent;
font-size: 14px;
color: #999;
}
.logged {
margin-top: 100px;
}
.logged .logged-icon {
display: block;
width: 64px;
height: 64px;
margin: 20px auto;
}
.logged .logged-text {
font-size: 14px;
color: #000;
text-align: center;
margin: 10px 0;
}
.form-item{
position: relative;
background: #fff;
height: 96rpx;
border-bottom: 1px solid #d9d9d9;
}
.form-item .username, .form-item .password, .form-item .code{
position: absolute;
top: 26rpx;
left: 0;
display: block;
width: 100%;
height: 44rpx;
background: #fff;
color: #333;
font-size: 30rpx;
}
6.后台获取小程序传过来的值进行解密
import java.util.Map;
/**
* @date: 2018/10/26 15:51
* @author: YINLELE
* @description: 用于小程序登录
*/
@RestController
@RequestMapping("/mini/login")
public class MiniLoginController extends BaseController {
Logger logger =LoggerFactory.getLogger(MiniLoginController.class);
@Autowired
private UserService userService;
@ResponseBody
@PostMapping("/wxUserInfo")
public ResponseBean wxLoginToken(@RequestBody WxEncryption wxEncryption) throws Exception {
logger.info("wxUserInfo={}",wxEncryption);
//准备url,获取用户的session_key和openId信息
String urlSM=WxSPConfig.CODE2_SESSION_URL.replace("APPID",WxSPConfig.APP_ID_SMALL_PROGRAM).replace("SECRET",WxSPConfig.SECRET_SMALL_PROGRAM).replace("JSCODE",wxEncryption.getCode());
logger.info("urlSM={}",urlSM);
//请求微信接口获取信息
RestTemplate template = new RestTemplate();
ResponseEntity<String> responseSM = template.getForEntity(urlSM, String.class);
logger.info("responseSM={}",responseSM);
//转换成map集合
String bodySM = responseSM.getBody();
WxUserInfoVO wxUserInfoVO = FastJsonUtil.getObject(bodySM, WxUserInfoVO.class);
//获取解密后的数据
String jsonData = AesCbcUtil.decrypt(wxEncryption.getEncryptedData(), wxUserInfoVO.getSession_key(),wxEncryption.getIv(), "UTF-8");
logger.info("wxUserInfoVO={}",wxUserInfoVO);
WxUserInfoVO userInfoVO = FastJsonUtil.getObject(jsonData, WxUserInfoVO.class);
userInfoVO.setSession_key(wxUserInfoVO.getSession_key());
userInfoVO.setWxUserInfo(jsonData);
//根据openid查询
UserEntity userEntity=userService.findByOpenId(userInfoVO.getOpenId());
if(null==userEntity){
userInfoVO = userService.saveUserByWx(userInfoVO, wxEncryption);
}else{
userInfoVO = userService.updateUserByWx(userInfoVO, wxEncryption);
}
userInfoVO.setWxUserInfo(null);
userInfoVO.setSession_key(null);
return ResponseUtil.success(userInfoVO);
}
}
使用的实体类
package com.aui.stock.controller.mini.vo;
/**
* @date: 2018/11/6 23:46
* @author: YINLELE
* @description: 用户存储用户信息
*/
public class WxUserInfoVO {
/*微信用户在系统的的唯一ID*/
private Long sn;
/*用户唯一的标识*/
private String openId;
/*微信用户的昵称*/
private String nickName;
/*微信用户的性别*/
private String gender;
/*微信用户的城市*/
private String city;
/*微信用户的省*/
private String province;
/*微信用户的国家*/
private String country;
/*微信用户的头像*/
private String avatarUrl;
/*用户手机号*/
private String phone;
/*用户的语言*/
private String language;
/*用户秘钥*/
private String session_key;
/*用户信息json*/
private String wxUserInfo;
public String getWxUserInfo() {
return wxUserInfo;
}
public void setWxUserInfo(String wxUserInfo) {
this.wxUserInfo = wxUserInfo;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public Long getSn() {
return sn;
}
public void setSn(Long sn) {
this.sn = sn;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getAvatarUrl() {
return avatarUrl;
}
public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getSession_key() {
return session_key;
}
public void setSession_key(String session_key) {
this.session_key = session_key;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("WxUserInfoVO{");
sb.append("sn=").append(sn);
sb.append(", nickName='").append(nickName).append('\'');
sb.append(", gender='").append(gender).append('\'');
sb.append(", city='").append(city).append('\'');
sb.append(", province='").append(province).append('\'');
sb.append(", country='").append(country).append('\'');
sb.append(", avatarUrl='").append(avatarUrl).append('\'');
sb.append(", phone='").append(phone).append('\'');
sb.append(", language='").append(language).append('\'');
sb.append(", session_key='").append(session_key).append('\'');
sb.append('}');
return sb.toString();
}
}
package com.aui.stock.controller.mini.to;
/**
* @date: 2018/11/6 22:27
* @author: YINLELE
* @description: 用于接受微信登录的参数
*/
public class WxEncryption {
/*用户登录的code 有效期5分钟*/
private String code;
/*用户的加密信息*/
private String encryptedData;
/*加密算法的初始向量*/
private String iv;
/*用户手机号*/
private String phone;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getEncryptedData() {
return encryptedData;
}
public void setEncryptedData(String encryptedData) {
this.encryptedData = encryptedData;
}
public String getIv() {
return iv;
}
public void setIv(String iv) {
this.iv = iv;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("WxEncryption{");
sb.append("code='").append(code).append('\'');
sb.append(", encryptedData='").append(encryptedData).append('\'');
sb.append(", iv='").append(iv).append('\'');
sb.append('}');
return sb.toString();
}
}
解密的工具类
package com.aui.stock.util;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;
/**
* @date: 2018/11/6 22:49
* @author: YINLELE
* @description:
* AES-128-CBC 加密方式
* 注:
* AES-128-CBC可以自己定义“密钥”和“偏移量“。
* AES-128是jdk自动生成的“密钥”。
*/
public class AesCbcUtil {
static {
//BouncyCastle是一个开源的加解密解决方案,主页在http://www.bouncycastle.org/
Security.addProvider(new BouncyCastleProvider());
}
/**
* AES解密
*
* @param data //密文,被加密的数据
* @param key //秘钥
* @param iv //偏移量
* @param encodingFormat //解密后的结果需要进行的编码
* @return
* @throws Exception
*/
public static String decrypt(String data, String key, String iv, String encodingFormat) throws Exception {
//被加密的数据
byte[] dataByte = Base64.decodeBase64(data);
//加密秘钥
byte[] keyByte = Base64.decodeBase64(key);
//偏移量
byte[] ivByte = Base64.decodeBase64(iv);
try {
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, encodingFormat);
return result;
}
return null;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidParameterSpecException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
}
解密所需要的maven坐标
<!--用于小程序解密-->
<dependency>
<groupId>org.codehaus.xfire</groupId>
<artifactId>xfire-core</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
</dependency>
<!--小程序解密-->