微信小程序后端开发流程根据官网总结为两个步骤
1、前端调用 wx.login 返回了code,然后调用wx.getUserInfo获取到用户的昵称 头像
2、服务端根据code去微信获取openid, 接口地址: https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html%EF%BC%9B%E5%90%8C%E6%97%B6%EF%BC%8C%E6%9B%B4%E6%96%B0%E7%94%A8%E6%88%B7%E6%98%B5%E7%A7%B0%E5%A4%B4%E5%83%8F%E7%AD%89%E8%B5%84%E6%96%99
微信小程序后端接口开发
controller层
public class OauthController { @Autowired private WeChatService weChatService; /** * 微信授权用js_code换取openId * @param code * @return */ @GetMapping("/code2Session") public BaseResponse code2Session(String code) { log.info("code2Session,code={}", code); if (StringUtil.isEmpty(code)) { return BaseResponse.buildFail("参数异常"); } Code2SessionResponse res = weChatService.code2Session(code); log.info("code2Session,res={}", res); if (!res.isSuccess()) { return BaseResponse.buildFail(res.getErrCode(), res.getErrMsg()); } return BaseResponse.buildSuccess(res); } /** * 解密获取手机号 * @param request * @param response * @param param * @return */ public BaseResponse decryptGetPhone(HttpServletRequest request, HttpServletResponse response, @RequestBody OauthParam param) { if (!StringUtil.isEmpty(param.getOpenId())) {//微信授权登录 String sessionKey = weChatService.getSessionKey(param.getOpenId()); if (StringUtil.isEmpty(sessionKey)) { return BaseResponse.buildFail("会话不存在"); } Sha1Utils sha = new Sha1Utils(); // 获取用户信息 log.debug("微信登陆 sessionKey = {}", sessionKey); String userInfoStr = sha.decryptWXAppletInfo(sessionKey, param.getEncryptedData(), param.getIv()); if (StringUtil.isEmpty(userInfoStr)) { return BaseResponse.buildFail("无法获取用户信息"); } JSONObject json = JSONObject.parseObject(userInfoStr); //绑定微信的手机号 String tel = json.getString("purePhoneNumber"); Assert.isTrue(!StringUtils.isEmpty(tel), "无法获取用户手机号"); BaseResponse baseResponse=new BaseResponse(); baseResponse.setResultInfo(tel); baseResponse.setState(0); return baseResponse; } } }
接口
public interface WeChatService { /** * 用code换取openid * * @param code * @return */ Code2SessionResponse code2Session(String code); /** * 获取凭证 * * @return */ String getAccessToken(); /** * 获取凭证 * * @param isForce * @return */ String getAccessToken(boolean isForce); String getSessionKey(String openId); }
实现类
public class WeChatServiceImpl implements WeChatService { //获取配置文件数据 @Value("${wechat.miniprogram.id}") private String appId; @Value("${wechat.miniprogram.secret}") private String appSecret; @Reference private SysUserService sysUserService; @Override public Code2SessionResponse code2Session(String code) { String rawResponse = HttpClientUtil .get(String.format(WechatConstant.URL_CODE2SESSION, appId, appSecret, code)); log.info("rawResponse====={}", rawResponse); Code2SessionResponse response = JSON.parseObject(rawResponse, Code2SessionResponse.class); if (response.isSuccess()) { cacheSessionKey(response); } return response; } private void cacheSessionKey(Code2SessionResponse response) { RedisCache redisCache = RedisCache.getInstance(); String key = RedisCacheKeys.getWxSessionKeyKey(response.getOpenId()); redisCache.setCache(key, 2147483647, response.getSessionKey()); } @Override public String getAccessToken() { return getAccessToken(false); } @Override public String getAccessToken(boolean isForce) { RedisCache redisCache = RedisCache.getInstance(); String accessToken = null; if (!isForce) { accessToken = redisCache.getCache(RedisCacheKeys.getWxAccessTokenKey(appId)); } if (StringUtil.isNotEmpty(accessToken)) { return accessToken; } String rawResponse = HttpClientUtil .get(String.format(WechatConstant.URL_GET_ACCESS_TOKEN, appId, appSecret)); AccessTokenResponse response = JSON.parseObject(rawResponse, AccessTokenResponse.class); log.info("getAccessToken:response={}", response); if (response.isSuccess()) { redisCache.setCache(RedisCacheKeys.getWxAccessTokenKey(appId), 7000, response.getAcessToken()); return response.getAcessToken(); } return null; } @Override public String getSessionKey(String openId) { RedisCache redisCache = RedisCache.getInstance(); String key = RedisCacheKeys.getWxSessionKeyKey(openId); String sessionKey = redisCache.getCache(key); return sessionKey; } }
用到的解密工具类
public class Sha1Utils { public static String decryptWXAppletInfo(String sessionKey, String encryptedData, String iv) { String result = null; try { byte[] encrypData = Base64.decodeBase64(encryptedData); byte[] ivData = Base64.decodeBase64(iv); byte[] sessionKeyB = Base64.decodeBase64(sessionKey); AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec keySpec = new SecretKeySpec(sessionKeyB, "AES"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); byte[] doFinal = cipher.doFinal(encrypData); result = new String(doFinal); return result; } catch (Exception e) { //e.printStackTrace(); log.error("decryptWXAppletInfo error",e); } return null; } }
网络请求工具类
public class HttpClientUtil { // utf-8字符编码 public static final String CHARSET_UTF_8 = "utf-8"; // HTTP内容类型。 public static final String CONTENT_TYPE_TEXT_HTML = "text/xml"; // HTTP内容类型。相当于form表单的形式,提交数据 public static final String CONTENT_TYPE_FORM_URL = "application/x-www-form-urlencoded"; // HTTP内容类型。相当于form表单的形式,提交数据 public static final String CONTENT_TYPE_JSON_URL = "application/json;charset=utf-8"; // 连接管理器 private static PoolingHttpClientConnectionManager pool; // 请求配置 private static volatile RequestConfig requestConfig; private static CloseableHttpClient getNewHttpClient() { CloseableHttpClient httpClient = HttpClients.custom() // 设置连接池管理 .setConnectionManager(pool) // 设置请求配置 .setDefaultRequestConfig(getRequestConfig()) // 设置重试次数 .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)).build(); return httpClient; } /** * 发送 post请求 * * @param httpUrl * 地址 */ public static String post(String httpUrl) { // 创建httpPost HttpPost httpPost = new HttpPost(httpUrl); return request(httpPost); } public static byte[] postRaw(String httpUrl) { // 创建httpPost HttpPost httpPost = new HttpPost(httpUrl); return requestRaw(httpPost); } /** * 发送 get请求 * * @param httpUrl */ public static String get(String httpUrl) { // 创建get请求 HttpGet httpGet = new HttpGet(httpUrl); return request(httpGet); } /** * 发送 post请求(带文件) * * @param httpUrl * 地址 * @param maps * 参数 * @param fileLists * 附件 */ public static String post(String httpUrl, Map<String, String> maps, List<File> fileLists, String fileName) { HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost MultipartEntityBuilder meBuilder = MultipartEntityBuilder.create(); if (maps != null) { for (String key : maps.keySet()) { meBuilder.addPart(key, new StringBody(maps.get(key), ContentType.TEXT_PLAIN)); } } if (fileLists != null) { for (File file : fileLists) { FileBody fileBody = new FileBody(file); meBuilder.addPart(fileName, fileBody); } } HttpEntity reqEntity = meBuilder.build(); httpPost.setEntity(reqEntity); return request(httpPost); } public static String post(String httpUrl, Map<String, String> maps, List<File> fileLists) { return post(httpUrl, maps, fileLists, "file"); } public static String post(String httpUrl, List<File> fileLists) { return post(httpUrl, Collections.emptyMap(), fileLists, "file"); } /** * 发送 post请求 * * @param httpUrl * 地址 * @param params * 参数(格式:key1=value1&key2=value2) * */ public static String post(String httpUrl, String params) { HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost try { // 设置参数 if (params != null && params.trim().length() > 0) { StringEntity stringEntity = new StringEntity(params, "UTF-8"); stringEntity.setContentType(CONTENT_TYPE_FORM_URL); httpPost.setEntity(stringEntity); } } catch (Exception e) { e.printStackTrace(); } return request(httpPost); } /** * 发送 post请求 * * @param maps * 参数 */ public static String post(String httpUrl, Map<String, String> maps) { String param = convertStringParamter(maps); return post(httpUrl, param); } /** * 发送 post请求 发送json数据 * * @param httpUrl * 地址 * @param content * * */ public static String post(String httpUrl, String content, String contentType) { // HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost // try { // // 设置参数 // if (StringUtils.isNotEmpty(content)) { // StringEntity stringEntity = new StringEntity(content, "UTF-8"); // stringEntity.setContentType(contentType); // httpPost.setEntity(stringEntity); // } // } catch (Exception e) { // e.printStackTrace(); // } // return request(httpPost); return new String(postRaw(httpUrl, content, contentType), StandardCharsets.UTF_8); } public static byte[] postRaw(String httpUrl, String content, String contentType) { HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost try { // 设置参数 if (StringUtils.isNotEmpty(content)) { StringEntity stringEntity = new StringEntity(content, "UTF-8"); stringEntity.setContentType(contentType); httpPost.setEntity(stringEntity); } } catch (Exception e) { e.printStackTrace(); } return requestRaw(httpPost); } /** * 发送 post请求 发送json数据 * * @param httpUrl * 地址 * @param paramsJson * 参数(格式 json) * */ public static String postJson(String httpUrl, String paramsJson) { return post(httpUrl, paramsJson, CONTENT_TYPE_JSON_URL); } public static byte[] postJsonRaw(String httpUrl, String paramsJson) { return postRaw(httpUrl, paramsJson, CONTENT_TYPE_JSON_URL); } /** * 发送 post请求 发送xml数据 * * @param url 地址 * @param paramsXml 参数(格式 Xml) * */ public static String postXml(String url, String paramsXml) { return post(url, paramsXml, CONTENT_TYPE_TEXT_HTML); } /** * 将map集合的键值对转化成:key1=value1&key2=value2 的形式 * * @param parameterMap * 需要转化的键值对集合 * @return 字符串 */ public static String convertStringParamter(Map parameterMap) { StringBuilder parameterBuffer = new StringBuilder(); if (parameterMap != null) { Iterator iterator = parameterMap.keySet().iterator(); String key = null; String value = null; while (iterator.hasNext()) { key = (String) iterator.next(); if (parameterMap.get(key) != null) { value = (String) parameterMap.get(key); } else { value = ""; } parameterBuffer.append(key).append("=").append(value); if (iterator.hasNext()) { parameterBuffer.append("&"); } } } return parameterBuffer.toString(); } /** * 发送请求 * * @param request * @return */ public static byte[] requestRaw(HttpRequestBase request) { CloseableHttpClient httpClient; CloseableHttpResponse response = null; // 响应内容 // String responseContent = null; byte[] rawResponse = null; try { // 创建默认的httpClient实例. httpClient = getNewHttpClient(); // 配置请求信息 request.setConfig(requestConfig); // 执行请求 response = httpClient.execute(request); // 得到响应实例 HttpEntity entity = response.getEntity(); // 可以获得响应头 // Header[] headers = response.getHeaders(HttpHeaders.CONTENT_TYPE); // for (Header header : headers) { // System.out.println(header.getName()); // } // 得到响应类型 // System.out.println(ContentType.getOrDefault(response.getEntity()).getMimeType()); // 判断响应状态 if (response.getStatusLine().getStatusCode() >= 300) { throw new Exception("HTTP Request is not success, Response code is " + response.getStatusLine().getStatusCode()); } if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) { rawResponse = EntityUtils.toByteArray(entity); // responseContent = EntityUtils.toString(entity, CHARSET_UTF_8); EntityUtils.consume(entity); } } catch (Exception e) { e.printStackTrace(); } finally { try { // 释放资源 if (response != null) { response.close(); } } catch (IOException e) { e.printStackTrace(); } } return rawResponse; } private static String request(HttpRequestBase req) { return new String(requestRaw(req), StandardCharsets.UTF_8); } private static RequestConfig getRequestConfig() { if (requestConfig == null) { synchronized (HttpClientUtil.class) { if (requestConfig == null) { try { //System.out.println("初始化HttpClientTest~~~开始"); SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( builder.build()); // 配置同时支持 HTTP 和 HTPPS Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder .<ConnectionSocketFactory> create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", sslsf).build(); // 初始化连接管理器 pool = new PoolingHttpClientConnectionManager(socketFactoryRegistry); // 将最大连接数增加到200,实际项目最好从配置文件中读取这个值 pool.setMaxTotal(200); // 设置最大路由 pool.setDefaultMaxPerRoute(2); // 根据默认超时限制初始化requestConfig int socketTimeout = 10000; int connectTimeout = 10000; int connectionRequestTimeout = 10000; requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(connectionRequestTimeout) .setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout) .build(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } // 设置请求超时时间 requestConfig = RequestConfig.custom().setSocketTimeout(50000) .setConnectTimeout(50000).setConnectionRequestTimeout(50000).build(); } } } return requestConfig; } }
常量
public interface WechatConstant { Integer OK_STATUS = 0; String URL_CODE2SESSION = "https://api.weixin.qq.com/sns/jscode2session"; String URL_GET_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token"; String URL_GET_IMAGE = "http://file.api.weixin.qq.com/cgi-bin/media/get"; /** * 给公众号发送信息。参考https://mp.weixin.qq.com/advanced/tmplmsg"https://api.weixin.qq.com/cgi-bin/message/template/send"; String URL_SEND_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; /** * 发送模板消息。参考https://developers.weixin.qq.com/miniprogram/dev/api-backend/sendMiniTemplateMessage.html */ String URL_SEND_TEMPLATE_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send"; String URL_QR_CODE_UNLIMTED = "https://api.weixin.qq.com/wxa/getwxacodeunlimit"; String URL_QR_CODE = "https://api.weixin.qq.com/wxa/getwxacode"; /** * 获取标签下粉丝列表 */ String URL_ALL_FANS_OPENID = "https://api.weixin.qq.com/cgi-bin/user/tag/get"; /** * 获取公众号已创建的标签 */ String URL_ALL_TAGS = "https://api.weixin.qq.com/cgi-bin/tags/get"; }
使用到的实体类
public class Code2SessionResponse implements Serializable { public static Integer RESPONSE_OK = 0; @JSONField(name = "openid") private String openId; @JSONField(name = "session_key") private String sessionKey; @JSONField(name = "unionid") private String unionId; @JSONField(name = "errcode") private Integer errCode; @JSONField(name = "errmsg") private String errMsg; public boolean isSuccess() { return this.errCode == null || RESPONSE_OK.equals(this.errCode); } }
总结:微信小程序的后端开发主要就是对用户进行授权 , 1、前端调用 wx.login 返回了code,然后调用wx.getUserInfo获取到用户的昵称 头像 2.首先通过微信授权用js_code换取openId,来获取openId,前端传微信的参数 code字段 3.然后解密获取手机号 前端需要传openId encryptedData iv 等字段来获取用户的的授权手机号
这些信息都获取后 接着就是调用后端的登陆接口,登陆接口如果只有授权登录就是我们将接口参数为下图最后三个字段为前端必填字段
主要步骤是根据前端的openId获取sessionKey 然后根据sessionKey 和其他参数进行解密获取用户手机号
通过解密获取授权登录的手机号,然后根据自己的业务逻辑处理即可,这样我们就可以根据授权的手机号进行授权登录
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]