java对接微信小程序详细流程(登录&获取用户信息)
需求说明:
用户通过小程序登录,进入到平台系统,进行各功能操作;
解决方案:
首先通过对接小程序,用户通过小程序登录及授权获取用户信息,后端调用接口获取微信用户信息,进行保存到数据库,然后返回token给前端(实际在这里相当于用户的一个注册及登录),前端使用该token访问所有接口;
相关代码:
首先我们需要用到 http工具类 方便后续的接口调用: 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 { // 创建Httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创建Http Post请求 HttpPost httpPost = new HttpPost(url); // 创建参数列表 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()); // 执行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 '手机号码', `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; //这个入参其实里面包含了用户的信息 下面的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"); //校验签名 小程序发送的签名signature与服务器端生成的签名signature2 = sha1(rawData + sessionKey) String signature2 = DigestUtils.sha1Hex(loginRequest.getRawData() + sessionKey); if (!loginRequest.getSignature().equals(signature2)) { return ResponseResult.errorResult(HttpCodeEnum.FAIL, "签名校验失败"); } WechatUser insertOrUpdateDO = buildWechatUserAuthInfoDO(loginRequest, sessionKey, openId); // 根据code保存openId和sessionKey JSONObject sessionObj = new JSONObject(); sessionObj.put("openId", openId); sessionObj.put("sessionKey", sessionKey); // 根据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); } //调用接口 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); // 发送post请求读取调用微信接口获取openid用户唯一标识 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) { // 被加密的数据 byte[] dataByte = Base64.decode(encryptedData); // 加密秘钥 byte[] keyByte = Base64.decode(sessionkey); // 偏移量 byte[] ivByte = Base64.decode(iv); try { // 如果密钥不足16位,那么就补足.这个if中的内容很重要 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 这里可以忽略 根据自己当前业务系统选取生成方式 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); } }
注意:
微信小程序更新后:
#### 前端调用接口 参数一次性给齐 这样就可以一次调用 获取所有;
总结
到此这篇关于java对接微信小程序的文章就介绍到这了,更多相关java对接微信小程序内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
SpringBoot之配置logging日志及在控制台中输出过程
这篇文章主要介绍了SpringBoot之配置logging日志及在控制台中输出过程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2023-06-06详解Java中String,StringBuffer和StringBuilder的使用
这篇文章主要为大家详细介绍了Java中String,StringBuffer和StringBuilder三者的区别以及使用,文中的少了讲解详细,感兴趣的可以了解一下2022-07-07Spring Boot引入swagger-ui 后swagger-ui.html无法访问404的问题
这篇文章主要介绍了Spring Boot引入swagger-ui 后swagger-ui.html无法访问404的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-09-09
最新评论