小程式登入(java後臺)
阿新 • • 發佈:2018-11-22
基本順序
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>
<!--小程式解密-->