微信web开发只需要code值,但是小程序需要一个code值,一个encryptData,一个iv
首先先看图
箭头部分为微信给我们的,就是前端需要传过来的。
步骤为:
重点在6、7、8三个环节。
AES解密三个参数:
概念性的东西就这些,下面看代码
首先前端需要传给我们的东西就是三个一个code值,一个encryptData,一个iv,
先写方法
package com.everest.academy.util; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.encoders.Base64; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.net.URLConnection; import java.security.AlgorithmParameters; import java.security.Security; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * @ClassName XcxUtils * @Description 微信小程序方法 * @Author 田野 * @Data 22:14 * @Version 1.0 **/ @Slf4j public class XcxUtils { /** * 获取微信小程序 session_key 和 openid * * @author zhy * @param code * 调用微信登陆返回的Code * @return */ public static JSONObject getSessionKeyOropenid(String code, String appid, String secret) { String requestUrl = "https://api.weixin.qq.com/sns/jscode2session"; // 请求地址 MaprequestUrlParam = new HashMap (); requestUrlParam.put("appid", appid); // 开发者设置中的appId requestUrlParam.put("secret", secret); // 开发者设置中的appSecret requestUrlParam.put("js_code", code); // 小程序调用wx.login返回的code requestUrlParam.put("grant_type", "authorization_code"); // 默认参数 // 发送post请求读取调用微信 https://api.weixin.qq.com/sns/jscode2session // 接口获取openid用户唯一标识 JSONObject jsonObject = JSON.parseObject(sendPost(requestUrl, requestUrlParam)); System.out.println(jsonObject); return jsonObject; } /** * 解密用户敏感数据获取用户信息 * * @author zhy * @param sessionKey * 数据进行加密签名的密钥 * @param encryptedData * 包括敏感数据在内的完整用户信息的加密数据 * @param iv * 加密算法的初始向量 * @return */ public static JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) { encryptedData=encryptedData.replace(" ", "+"); sessionKey=sessionKey.replace(" ", "+"); iv=iv.replace(" ", "+"); // 被加密的数据 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 + (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, "UTF-8"); return JSON.parseObject(result); } } catch (Exception e) { log.error(e.getMessage(), e); } return null; } /** * 向指定 URL 发送POST方法的请求 * * @param url * 发送请求的 URL * @param * * @return 所代表远程资源的响应结果 */ public static String sendPost(String url, Map paramMap) { PrintWriter out = null; BufferedReader in = null; String result = ""; String param = ""; Iterator it = paramMap.keySet().iterator(); while (it.hasNext()) { String key = it.next(); param += key + "=" + paramMap.get(key) + "&"; } try { URL realUrl = new URL(url); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("Accept-Charset", "utf-8"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(param); // flush输出流的缓冲 out.flush(); // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { log.error(e.getMessage(), e); } // 使用finally块来关闭输出流、输入流 finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } return result; } }
注解写的很明白的,应该没什么看不懂。
之后就是service层
package com.everest.academy.service.impl; import com.alibaba.fastjson.JSONObject; import com.everest.academy.business.dto.LoginUserDTO; import com.everest.academy.business.dto.WechatTokenDto; import com.everest.academy.framework.exception.ResourceIsNullException; import com.everest.academy.framework.pojo.User; import com.everest.academy.persistence.mapper.UserMapper; import com.everest.academy.service.WechatService; import com.everest.academy.util.WechatUtil; import com.everest.academy.util.XcxUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Date; /** * @ClassName WechatServiceImpl * @Description 微信sgervice * @Author 田野 * @Data 20:25 * @Version 1.0 **/ @Slf4j @Service public class WechatServiceImpl implements WechatService { @Autowired UserMapper userMapper; @Override public LoginUserDTO getUserByCode(String code, String encryptedData, String iv)throws Exception{ log.info("传进来的值"+encryptedData+"另一个"+iv); JSONObject shopAddress = null; if (StringUtils.isNotEmpty(code)) { String appid = "填自己的"; String secret = "填自己的"; shopAddress = XcxUtils.getSessionKeyOropenid(code, appid, secret); } assert shopAddress != null; String openId = shopAddress.getString("openid"); String sessionKey = shopAddress.getString("session_key"); log.info("session_key为:"+sessionKey); JSONObject user1 = XcxUtils.getUserInfo(encryptedData, sessionKey, iv); //user这里根据用户openId查询是否有这个用户。 User user=userMapper.findByOpenId(openId); LoginUserDTO loginUserDTO=new LoginUserDTO(); if (user!=null){ log.info("用户的状态"+user.getState()); if (user.getState()==1){ //有的话,直接就进入 ,直接将信息返回给前端 loginUserDTO.setId(user.getId()); loginUserDTO.setOpenId(openId); return loginUserDTO; } throw new Exception("无法登录,账号被冻结"); } //没有的话,创建该学生的信息,然后再传给部分数据给前端 User newUser=new User(); newUser.setOpenId(openId); assert user1 != null; newUser.setAddress(user1.getString("city")); newUser.setHeadImgUrl(user1.getString("avatarUrl")); newUser.setName(user1.getString("nickName")); newUser.setState(1); newUser.setBeans(0); newUser.setBinding(0); newUser.setCreate_at(new Date().getTime()); newUser.setCreate_by("系统创建"); userMapper.insert(newUser); User user2=userMapper.findByOpenId(openId); LoginUserDTO loginUserDTO1=new LoginUserDTO(); loginUserDTO1.setId(user2.getId()); log.info("id的值"+newUser.getId()); loginUserDTO1.setOpenId(newUser.getOpenId()); loginUserDTO1.setBinding(newUser.getState()); return loginUserDTO1; } }
也写的挺清楚的,通过openId判断是否有这个用户
controller
package com.everest.academy.controller; import com.everest.academy.business.vo.ResponseVo; import com.everest.academy.framework.exception.ResourceIsNullException; import com.everest.academy.service.WechatService; import com.everest.academy.util.ResultUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.Map; /** * @ClassName WechatController * @Description 微信登录验证 * @Author 田野 * @Data 15:56 * @Version 1.0 **/ @Slf4j @Api(tags = "WechatController",description = "微信开发API") @RestController @RequestMapping("/a/home") @Validated public class WechatController { @Autowired WechatService wechatService; @ApiOperation(value = "微信登录验证",notes = "通过获取的codeId值登录") @PostMapping("/{code}") public ResponseVo WechatLogin( @PathVariable("code") String code, @RequestParam("encryptedData") String encryptedData,@RequestParam("iv") String iv)throws Exception{ log.info("传进来的未"+encryptedData +"iv为" + iv); return ResultUtil.success("微信登录成功",wechatService.getUserByCode(code,encryptedData,iv)); } }
就有一点问题
测试的时候,每次传参都读不到+号,每次都把我的+号弄掉。
所以这里运用了一个replace,完美解决
微信网页开发,通过codeId得到access_token,通过access_token和openid获取用户基本信息
package com.everest.academy.util; import com.everest.academy.business.dto.LoginUserDTO; import com.everest.academy.business.dto.WechatTokenDto; import com.google.gson.Gson; import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import java.util.HashMap; import java.util.Map; /** * @ClassName WechatUtil * @Description TODO * @Author 田野 * @Data 19:25 * @Version 1.0 **/ @Slf4j public class WechatUtil { public final static String APPID = "自己的"; public final static String APPSECRET = "自己的"; /** * 获取请求用户信息的access_token * @param code * @return */ public static WechatTokenDto getUserInfoAccessToken(String code) { JsonObject object = null; WechatTokenDto wechatTokenDto=new WechatTokenDto(); Mapdata = new HashMap(); try { String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code", APPID, APPSECRET, code); log.info("request accessToken from url: {}", url); CloseableHttpClient httpClient =HttpClients.createDefault(); HttpGet httpGet = new HttpGet(url); HttpResponse httpResponse = httpClient.execute(httpGet); HttpEntity httpEntity = httpResponse.getEntity(); String tokens = EntityUtils.toString(httpEntity, "utf-8"); Gson token_gson = new Gson(); object = token_gson.fromJson(tokens, JsonObject.class); log.info("request accessToken success. [result={}]", object); wechatTokenDto.setOpenid(object.get("openid").toString().replaceAll("\"", "")); wechatTokenDto.setAccess_token(object.get("access_token").toString().replaceAll("\"", "")); wechatTokenDto.setRefresh_token(object.get("refresh_token").toString().replaceAll("\"", "")); wechatTokenDto.setScope(object.get("scope").toString().replaceAll("\"", "")); wechatTokenDto.setExpires_in(Integer.parseInt(object.get("expires_in").toString().replaceAll("\"",""))); // data.put("openid", object.get("openid").toString().replaceAll("\"", "")); // data.put("access_token", object.get("access_token").toString().replaceAll("\"", "")); // data.put("expires_in",object.get("expires_in").toString().replaceAll("\"","")); // data.put("refresh_token",object.get("refresh_token").toString().replaceAll("\"", "")); // data.put("scope",object.get("scope").toString().replaceAll("\"", "")); } catch (Exception ex) { log.error("fail to request wechat access token. [error={}]", ex); } return wechatTokenDto; } /** * 通过access_token和openid获取用户基本信息 * @param accessToken * @param openId * @return */ public static LoginUserDTO getUserInfo(String accessToken, String openId) { LoginUserDTO loginUserDTO=new LoginUserDTO(); String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN"; log.info("request user info from url: {}", url); JsonObject userInfo = null; try { CloseableHttpClient httpClient =HttpClients.createDefault(); HttpGet httpGet = new HttpGet(url); HttpResponse httpResponse = httpClient.execute(httpGet); HttpEntity httpEntity = httpResponse.getEntity(); String response = EntityUtils.toString(httpEntity, "utf-8"); Gson token_gson = new Gson(); userInfo = token_gson.fromJson(response, JsonObject.class); log.info("get userinfo success. [result={}]", userInfo); loginUserDTO.setOpenId(userInfo.get("openid").toString().replaceAll("\"", "")); loginUserDTO.setCountry(userInfo.get("country").toString().replaceAll("\"", "")); loginUserDTO.setProvince(userInfo.get("province").toString().replaceAll("\"", "")); loginUserDTO.setCity(userInfo.get("city").toString().replaceAll("\"", "")); loginUserDTO.setHeadImgUrl(userInfo.get("headimgurl").toString().replaceAll("\"", "")); loginUserDTO.setSex(Integer.valueOf(userInfo.get("sex").toString().replaceAll("\"", ""))); loginUserDTO.setNickname(userInfo.get("nickname").toString().replaceAll("\"", "")); // data.put("openid", userInfo.get("openid").toString().replaceAll("\"", "")); // data.put("nickname", userInfo.get("nickname").toString().replaceAll("\"", "")); // data.put("city", userInfo.get("city").toString().replaceAll("\"", "")); // data.put("province", userInfo.get("province").toString().replaceAll("\"", "")); // data.put("country", userInfo.get("country").toString().replaceAll("\"", "")); // data.put("headimgurl", userInfo.get("headimgurl").toString().replaceAll("\"", "")); } catch (Exception ex) { log.error("fail to request wechat user info. [error={}]", ex); } return loginUserDTO; } }
@Override public LoginUserDTO wechat(String code)throws ResourceIsNullException{ //调用封装的微信方法,通过code值得到wechatTokenDto WechatTokenDto wechatTokenDto=WechatUtil.getUserInfoAccessToken(code); String accessToken = wechatTokenDto.getAccess_token();//得到accessToken String openId = wechatTokenDto.getOpenid();//得到openId //user这里根据用户openId查询是否有这个用户。 User user=userMapper.findByOpenId(openId); LoginUserDTO loginUserDTO=WechatUtil.getUserInfo(accessToken,openId); log.info("用户信息"+user); if(user!=null){ //有的话,直接就进入 ,直接将信息返回给前端 // loginUserDTO.setOpenId(openId); loginUserDTO.setId(user.getId()); loginUserDTO.setBinding(user.getState()); return loginUserDTO; }else { //没有的话,创建该学生的信息,然后再传给部分数据给前端 User newUser=new User(); newUser.setOpenId(loginUserDTO.getOpenId()); newUser.setAddress(loginUserDTO.getCity()); newUser.setHeadImgUrl(loginUserDTO.getHeadImgUrl()); newUser.setName(loginUserDTO.getNickname()); log.info("用户昵称"+loginUserDTO.getNickname()); newUser.setState(1); newUser.setBeans(0); newUser.setBinding(0); newUser.setCreate_at(new Date().getTime()); newUser.setCreate_by("系统创建"); userMapper.insert(newUser); LoginUserDTO loginUserDTO1=new LoginUserDTO(); loginUserDTO1.setId(newUser.getId()); log.info("id的值"+newUser.getId()); loginUserDTO1.setOpenId(newUser.getOpenId()); loginUserDTO1.setBinding(newUser.getState()); return loginUserDTO1; } }
基本就是这个样子,后续看下能不能写的更加详细。