java對(duì)接微信小程序詳細(xì)流程(登錄&獲取用戶信息)
需求說明:
用戶通過小程序登錄,進(jìn)入到平臺(tái)系統(tǒng),進(jìn)行各功能操作;
解決方案:
首先通過對(duì)接小程序,用戶通過小程序登錄及授權(quán)獲取用戶信息,后端調(diào)用接口獲取微信用戶信息,進(jìn)行保存到數(shù)據(jù)庫(kù),然后返回token給前端(實(shí)際在這里相當(dāng)于用戶的一個(gè)注冊(cè)及登錄),前端使用該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對(duì)象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 創(chuàng)建Http Post請(qǐng)求 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請(qǐng)求 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 '所在國(guó)家', `province` varchar(100) DEFAULT NULL COMMENT '省份', `city` varchar(100) DEFAULT NULL COMMENT '城市', `mobile` varchar(100) DEFAULT NULL COMMENT '手機(jī)號(hào)碼', `open_id` varchar(100) NOT NULL COMMENT '小程序openId', `union_id` varchar(100) DEFAULT '' COMMENT '小程序unionId', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '插入時(shí)間', `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時(shí)間', 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 { //登錄時(shí)獲取的 code,可通過wx.login獲取 @NotNull(message = "code不能為空") @ApiModelProperty(value = "微信code", required = true) private String code; //這個(gè)入?yún)⑵鋵?shí)里面包含了用戶的信息 下面的impl層 就是解析這個(gè)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請(qǐng)求讀取調(diào)用微信接口獲取openid用戶唯一標(biāo)識(shí) 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ǔ)足.這個(gè)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("解密加密信息報(bào)錯(cuò)", 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對(duì)接微信小程序的文章就介紹到這了,更多相關(guān)java對(duì)接微信小程序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot查詢數(shù)據(jù)庫(kù)導(dǎo)出報(bào)表文件方式
這篇文章主要介紹了SpringBoot查詢數(shù)據(jù)庫(kù)導(dǎo)出報(bào)表文件方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04SpringBoot @SpringBootTest加速單元測(cè)試的小訣竅
這篇文章主要介紹了SpringBoot @SpringBootTest加速單元測(cè)試的小訣竅,具有很好的參考價(jià)值,對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11實(shí)例解決Java異常之OutOfMemoryError的問題
在本篇文章中,我們給大家分享了關(guān)于解決Java異常之OutOfMemoryError的問題的方法,有此需要的朋友們學(xué)習(xí)下。2019-02-02Spring Boot JDBC 連接數(shù)據(jù)庫(kù)示例
本篇文章主要介紹了Spring Boot JDBC 連接數(shù)據(jù)庫(kù)示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02Spring AOP手動(dòng)實(shí)現(xiàn)簡(jiǎn)單動(dòng)態(tài)代理的代碼
今天小編就為大家分享一篇關(guān)于Spring AOP手動(dòng)實(shí)現(xiàn)簡(jiǎn)單動(dòng)態(tài)代理的代碼,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03IntelliJ?IDEA?2024.2?發(fā)布新功能介紹Spring?Data?JPA即時(shí)查詢、自動(dòng)補(bǔ)全cro
在2024.2?Ultimate版本中,對(duì)?Spring?Data?JPA?的支持做了增強(qiáng),新功能允許您在不運(yùn)行應(yīng)用程序和分析日志文件的情況下查看方法將生成的查詢,下面就來一起看看這個(gè)版本中推出的幾個(gè)強(qiáng)大新特性2024-08-08@ComponentScan在spring中無效的原因分析及解決方案
這篇文章主要介紹了@ComponentScan在spring中無效的原因分析及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11