需求说明:

用户通过小程序登录,进入到平台系统,进行各功能操作;

解决方案:

首先通过对接小程序,用户通过小程序登录及授权获取用户信息,后端调用接口获取微信用户信息,进行保存到数据库,然后返回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;@Datapublic 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")@Slf4jpublic class LoginController {@Resourceprivate 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;@Mapperpublic 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@Servicepublic 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 && sessionKeyString 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和sessionKeyJSONObject 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());} // 解密加密信息,获取unionIDif (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());//外部登录生成tokenString 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);}}

注意:

微信小程序更新后:

#### 前端调用接口 参数一次性给齐 这样就可以一次调用 获取所有;