java對接微信小程序詳細(xì)流程(登錄&獲取用戶信息)
需求說明:
用戶通過小程序登錄,進(jìn)入到平臺系統(tǒng),進(jìn)行各功能操作;
解決方案:
首先通過對接小程序,用戶通過小程序登錄及授權(quán)獲取用戶信息,后端調(diào)用接口獲取微信用戶信息,進(jìn)行保存到數(shù)據(jù)庫,然后返回token給前端(實(shí)際在這里相當(dāng)于用戶的一個注冊及登錄),前端使用該token訪問所有接口;
相關(guān)代碼:
首先我們需要用到 http工具類 方便后續(xù)的接口調(diào)用:
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HttpClientUtils {
final static int TIMEOUT = 1000;
final static int TIMEOUT_MSEC = 5 * 1000;
public static String doPost(String url, Map<String, String> paramMap) throws IOException {
// 創(chuàng)建Httpclient對象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 創(chuàng)建Http Post請求
HttpPost httpPost = new HttpPost(url);
// 創(chuàng)建參數(shù)列表
if (paramMap != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (Map.Entry<String, String> param : paramMap.entrySet()) {
paramList.add(new BasicNameValuePair(param.getKey(), param.getValue()));
}
// 模擬表單
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
httpPost.setEntity(entity);
}
httpPost.setConfig(builderRequestConfig());
// 執(zhí)行http請求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (Exception e) {
throw e;
} finally {
try {
response.close();
} catch (IOException e) {
throw e;
}
}
return resultString;
}
private static RequestConfig builderRequestConfig() {
return RequestConfig.custom()
.setConnectTimeout(TIMEOUT_MSEC)
.setConnectionRequestTimeout(TIMEOUT_MSEC)
.setSocketTimeout(TIMEOUT_MSEC).build();
}
}小程序用戶表
CREATE TABLE `wechat_user` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `nickname` varchar(100) DEFAULT NULL COMMENT '用戶昵稱', `avatar_url` varchar(500) DEFAULT NULL COMMENT '用戶頭像', `gender` int(11) DEFAULT NULL COMMENT '性別 0-未知、1-男性、2-女性', `country` varchar(100) DEFAULT NULL COMMENT '所在國家', `province` varchar(100) DEFAULT NULL COMMENT '省份', `city` varchar(100) DEFAULT NULL COMMENT '城市', `mobile` varchar(100) DEFAULT NULL COMMENT '手機(jī)號碼', `open_id` varchar(100) NOT NULL COMMENT '小程序openId', `union_id` varchar(100) DEFAULT '' COMMENT '小程序unionId', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '插入時間', `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間', PRIMARY KEY (`id`), KEY `idx_open_id` (`open_id`), KEY `idx_union_id` (`union_id`), KEY `idx_mobile` (`mobile`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='小程序用戶表';
dto
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class WechatLoginRequest {
//登錄時獲取的 code,可通過wx.login獲取
@NotNull(message = "code不能為空")
@ApiModelProperty(value = "微信code", required = true)
private String code;
//這個入?yún)⑵鋵?shí)里面包含了用戶的信息 下面的impl層 就是解析這個json獲取用戶信息
@ApiModelProperty(value = "用戶非敏感字段")
private String rawData;
@ApiModelProperty(value = "簽名")
private String signature;
@ApiModelProperty(value = "用戶敏感字段")
private String encryptedData;
@ApiModelProperty(value = "解密向量")
private String iv;
}主要代碼:
controller
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/wechat")
@Api(tags = {"微信小程序"}, value = "/wechat")
@Slf4j
public class LoginController {
@Resource
private WechatService wechatService;
@ApiOperation(value = "登入接口", httpMethod = "POST")
@PostMapping("/login")
public ResponseResult login(@Validated @RequestBody WechatLoginRequest loginRequest) throws Exception {
return wechatService.getUserInfoMap(loginRequest);
}
}mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mmc.aircraftsystemserver.api.wechet.pojo.WechatUser;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface WechatMapper extends BaseMapper<WechatUser> {
}impl
package com.mmc.aircraftsystemserver.api.wechet.service.impl;
import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class WechatServiceImpl extends ServiceImpl<WechatMapper, WechatUser> implements WechatService {
//小程序 appId
@Value("${wechat.appid}")
private String APPID;
//小程序 appSecret
@Value("${wechat.secret}")
private String SECRET;
//${wechat.grantType} = authorization_code
@Value("${wechat.grantType}")
private String GRANT_TYPE;
// ${wechat.url} = https://api.weixin.qq.com/sns/jscode2session
@Value("${wechat.url}")
private String REQUEST_URL;
public ResponseResult getUserInfoMap(WechatLoginRequest loginRequest) throws Exception {
JSONObject sessionKeyOpenId = getSessionKeyOrOpenId(loginRequest.getCode());
// 獲取openId && sessionKey
String openId = sessionKeyOpenId.getString("openid");
String sessionKey = sessionKeyOpenId.getString("session_key");
//校驗(yàn)簽名 小程序發(fā)送的簽名signature與服務(wù)器端生成的簽名signature2 = sha1(rawData + sessionKey)
String signature2 = DigestUtils.sha1Hex(loginRequest.getRawData() + sessionKey);
if (!loginRequest.getSignature().equals(signature2)) {
return ResponseResult.errorResult(HttpCodeEnum.FAIL, "簽名校驗(yàn)失敗");
}
WechatUser insertOrUpdateDO = buildWechatUserAuthInfoDO(loginRequest, sessionKey, openId);
// 根據(jù)code保存openId和sessionKey
JSONObject sessionObj = new JSONObject();
sessionObj.put("openId", openId);
sessionObj.put("sessionKey", sessionKey);
// 根據(jù)openid查詢用戶
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("open_id",openId);
WechatUser user = getOne(wrapper);
if (user == null) {
// 用戶不存在,insert用戶
save(insertOrUpdateDO);
} else {
// 已存在,更新用戶的信息
UpdateWrapper<WechatUser> updateWrapper = new UpdateWrapper();
updateWrapper.eq("openId",openId)
.set("nickname",insertOrUpdateDO.getNickname())
.set("avatar_url",insertOrUpdateDO.getAvatarUrl())
.set("gender",insertOrUpdateDO.getGender())
.set("country",insertOrUpdateDO.getCountry())
.set("province",insertOrUpdateDO.getProvince())
.set("city",insertOrUpdateDO.getCity())
.set("mobile",insertOrUpdateDO.getMobile());
update(updateWrapper);
}
ResponseResult token = createToken(insertOrUpdateDO);
return ResponseResult.okResult(token);
}
//調(diào)用接口
private JSONObject getSessionKeyOrOpenId(String code) throws Exception {
Map<String, String> requestUrlParam = new HashMap<>();
requestUrlParam.put("appid", APPID);
requestUrlParam.put("secret", SECRET);
requestUrlParam.put("js_code", code);
requestUrlParam.put("grant_type", GRANT_TYPE);
// 發(fā)送post請求讀取調(diào)用微信接口獲取openid用戶唯一標(biāo)識
String result = HttpClientUtils.doPost(REQUEST_URL, requestUrlParam);
return JSON.parseObject(result);
}
private WechatUser buildWechatUserAuthInfoDO(WechatLoginRequest loginRequest, String sessionKey, String openId){
WechatUser wechatUserDO = new WechatUser();
wechatUserDO.setOpenId(openId);
if (loginRequest.getRawData() != null) {
RawDataDO rawDataDO = JSON.parseObject(loginRequest.getRawData(), RawDataDO.class);
wechatUserDO.setNickname(rawDataDO.getNickName());
wechatUserDO.setAvatarUrl(rawDataDO.getAvatarUrl());
wechatUserDO.setGender(rawDataDO.getGender());
wechatUserDO.setCity(rawDataDO.getCity());
wechatUserDO.setCountry(rawDataDO.getCountry());
wechatUserDO.setProvince(rawDataDO.getProvince());
}
// 解密加密信息,獲取unionID
if (loginRequest.getEncryptedData() != null){
JSONObject encryptedData = getEncryptedData(loginRequest.getEncryptedData(), sessionKey, loginRequest.getIv());
if (encryptedData != null){
String unionId = encryptedData.getString("unionId");
wechatUserDO.setUnionId(unionId);
}
}
return wechatUserDO;
}
private JSONObject getEncryptedData(String encryptedData, String sessionkey, String iv) {
// 被加密的數(shù)據(jù)
byte[] dataByte = Base64.decode(encryptedData);
// 加密秘鑰
byte[] keyByte = Base64.decode(sessionkey);
// 偏移量
byte[] ivByte = Base64.decode(iv);
try {
// 如果密鑰不足16位,那么就補(bǔ)足.這個if中的內(nèi)容很重要
int base = 16;
if (keyByte.length % base != 0) {
int groups = keyByte.length / base + 1;
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 JSONObject.parseObject(result);
}
} catch (Exception e) {
log.error("解密加密信息報錯", e.getMessage());
}
return null;
}
//生成token 這里可以忽略 根據(jù)自己當(dāng)前業(yè)務(wù)系統(tǒng)選取生成方式
public ResponseResult createToken(WechatUser wechatUser) {
String openid = wechatUser.getOpenId();
String token = MD5Util.getMD5Str(openid + System.currentTimeMillis());
String flyingSessionId = MD5Util.getMD5Str("HAHA" + wechatUser.getOpenId());
wechatUser.getStringRedisTemplate().opsForValue().set(token, wechatUser.getNickname());
//外部登錄生成token
String key = token + flyingSessionId;
Map<String, String> redisData = new HashMap<>();
redisData.put("HAHA-TOKEN", token);
redisData.put("HAHA-SESSIONID", flyingSessionId);
redisData.put("uid", wechatUser.getId() + "");
redisData.put("openid", wechatUser.getOpenId());
redisData.put("nickname", wechatUser.getNickname());
wechatUser.getStringRedisTemplate().opsForHash().putAll(key, redisData);
wechatUser.getStringRedisTemplate().expire(key, 86400, TimeUnit.SECONDS);
return ResponseResult.okResult(redisData);
}
}注意:
微信小程序更新后:

#### 前端調(diào)用接口 參數(shù)一次性給齊 這樣就可以一次調(diào)用 獲取所有;
總結(jié)
到此這篇關(guān)于java對接微信小程序的文章就介紹到這了,更多相關(guān)java對接微信小程序內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot查詢數(shù)據(jù)庫導(dǎo)出報表文件方式
這篇文章主要介紹了SpringBoot查詢數(shù)據(jù)庫導(dǎo)出報表文件方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04
SpringBoot @SpringBootTest加速單元測試的小訣竅
這篇文章主要介紹了SpringBoot @SpringBootTest加速單元測試的小訣竅,具有很好的參考價值,對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
實(shí)例解決Java異常之OutOfMemoryError的問題
在本篇文章中,我們給大家分享了關(guān)于解決Java異常之OutOfMemoryError的問題的方法,有此需要的朋友們學(xué)習(xí)下。2019-02-02
Spring Boot JDBC 連接數(shù)據(jù)庫示例
本篇文章主要介紹了Spring Boot JDBC 連接數(shù)據(jù)庫示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-02-02
Spring AOP手動實(shí)現(xiàn)簡單動態(tài)代理的代碼
今天小編就為大家分享一篇關(guān)于Spring AOP手動實(shí)現(xiàn)簡單動態(tài)代理的代碼,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03
IntelliJ?IDEA?2024.2?發(fā)布新功能介紹Spring?Data?JPA即時查詢、自動補(bǔ)全cro
在2024.2?Ultimate版本中,對?Spring?Data?JPA?的支持做了增強(qiáng),新功能允許您在不運(yùn)行應(yīng)用程序和分析日志文件的情況下查看方法將生成的查詢,下面就來一起看看這個版本中推出的幾個強(qiáng)大新特性2024-08-08
@ComponentScan在spring中無效的原因分析及解決方案
這篇文章主要介紹了@ComponentScan在spring中無效的原因分析及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11

