微信小程序后端开发流程根据官网总结为两个步骤

1、前端调用 wx.login 返回了code,然后调用wx.getUserInfo获取到用户的昵称 头像 2、服务端根据code去微信获取openid, 接口地址:developers.weixin.qq.com/miniprogram…

微信小程序后端接口开发

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?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"; String URL_GET_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; String URL_GET_IMAGE = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s"; /**
* 给公众号发送信息。参考https://mp.weixin.qq.com/advanced/tmplmsg?action=faq&token=708366329&lang=zh_CN
*/
String URL_SEND_TO_CHANNEL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s";
String URL_SEND_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s"; /**
* 发送模板消息。参考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?access_token=%s"; String URL_QR_CODE_UNLIMTED = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s"; String URL_QR_CODE = "https://api.weixin.qq.com/wxa/getwxacode?access_token=%s"; /**
* 获取标签下粉丝列表
*/
String URL_ALL_FANS_OPENID = "https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token=%s";
/**
* 获取公众号已创建的标签
*/
String URL_ALL_TAGS = "https://api.weixin.qq.com/cgi-bin/tags/get?access_token=%s"; }
复制代码

使用到的实体类

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 和其他参数进行解密获取用户手机号

通过解密获取授权登录的手机号,然后根据自己的业务逻辑处理即可,这样我们就可以根据授权的手机号进行授权登录

作者:CoderZS
链接:https://juejin.im/post/5dcaa988f265da4d2125e0bf
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

微信小程序后端开发流程的更多相关文章

  1. 微信小程序注册开发流程

    开篇: 微信小程序 很多刚学的同学都不太清楚如何去申请这个小程序的appid 现在呢我就一步步的告诉大家这个流程: 首先第1步,百度搜索:微信公众平台-点击右上角的立即注册 第2步:可以看到有4大注册 ...

  2. 微信小程序后端开发(Java语言)笔记

    前言: 因为是第一次真正接触后端开发,从编码到部署服务器到上线,所以做个笔记,也供和我一样的开发小白一些参考. 一.前期工作:开发环境与工具: 1. 编程语言:Java    #笔者还没学PHP,只想 ...

  3. 关于开发微信小程序后端linux使用xampp配置https

    关于开发微信小程序后端linux使用xampp配置https 背景 由于最近开发微信小程序,前后端交互需要使用https协议,故需要配置https服务 服务器环境 服务器系统 ubuntu 环境 xa ...

  4. 微信小程序从开发到上线流程

    一.微信小程序从开发到上线流程 注册小程序 1.登录微信公众平台 https://mp.weixin.qq.com 2.在微信公众平台>立即注册>小程序中注册微信小程序 3.在邮箱中激活并 ...

  5. 微信小程序云开发-从0打造云音乐全栈小程序

    第1章 首门小程序“云开发”课程,你值得学习本章主要介绍什么是小程序云开发以及学习云开发的重要性,并介绍项目的整体架构,真机演示项目功能,详细介绍整体课程安排.课程适用人群以及需要掌握的前置知识.通过 ...

  6. 小程序语音红包开发中 汉字转拼音的问题 微信小程序红包开发遇到的坑

    公司最近在开发微信小程序的红包功能,语音红包需要用到文字转拼音的功能. 之前介绍过怎么将中文的汉字转为拼音的,具体看下面这篇文章. 微信语音红包小程序开发如何提高精准度 红包小程序语音识别精准度 微信 ...

  7. 第一章 “我要点爆”微信小程序云开发之项目建立与我的页面功能实现

    第一章 “我要点爆”微信小程序云开发之项目建立与我的页面功能实现 开发环境搭建 使用自己的AppID新建小程序项目,后端服务选择小程序·云开发,点击新建,完成项目新建. 新建成功后跳转到开发者工具界面 ...

  8. 校园表白墙、微信表白墙、校园墙 微信小程序 JAVA 开发记录与分享

    目录 最新版表白墙博客地址 1.微信小程序前台展示 2.功能介绍 3.后台管理 4.后端语言采用 JAVA 开发 5.体验此微信小程序 扫描下方二维码 6.如何联系我或需要源码进行联系 最新版表白墙博 ...

  9. 微信小程序登入流程

    微信小程序登入流程 一.首先前端先传code去后端 wx.login({ success(res) { if (res.code) { //发起网络请求 wx.request({ url: app.g ...

随机推荐

  1. Linux内核单链表

    主要说明Linux内核中单链表操作的关键思想,需要注意的地方 1. 假设 为了说明关键思想,对数据结构进行了精简 2. 数据结构定义 struct ListNode { int val; ListNo ...

  2. Java入门基础(变量、操作符与表达式)

    Java入门基础 1. 第一个程序 2.变量(命名.运算.整数/小数/字符串.布尔类型) 3.操作符与表达式(算术/逻辑/关系/赋值/自增/类型转换操作符) HelloWorld! public cl ...

  3. webdriver高级应用 -更改一个对象界面的属性值

    #-*- coding=utf-8 -*- #更改一个对象界面的属性值 from selenium import webdriver import unittest def addAttribute( ...

  4. GitHub注册与Git安装

    注册GitHub GitHub官网 ☞☞☞☞[戳我] 一.打开,如下图所示, 按照要求进行注册. 安装Git Git官网下载☞☞☞☞ [戳我] 一.打开,根据自己的电脑系统选择不同的版本,这里主要是以 ...

  5. NetworkX包

    官方教程 NetworkX是一个创建,操作,研究复杂网络的结构,动态,功能的python包. #创建一个network import networkx as nx G = nx.Graph() #no ...

  6. WSGI接口

    web server gateway interface:将http请求,响应格式封装起来,让我们可以专心的用python编写web业务. WSGI接口定义的非常简单,它只要求开发者实现一个函数,就可 ...

  7. Codeforces_512_B

    http://codeforces.com/problemset/problem/512/B dp题,因为状态很多,所以用map保存,注意代码中的那个二层循环不能内外换,因为map会自动排序. #in ...

  8. ELK:收集Docker容器日志

    简介 之前写过一篇博客 ELK:日志收集分析平台,介绍了在Centos7系统上部署配置使用ELK的方法,随着容器化时代的到来,容器化部署成为一种很方便的部署方式,收集容器日志也成为刚需.本篇文档从 容 ...

  9. 纯JavaScript实现页面行为的录制

    在网上有个开源的rrweb项目,该项目采用TypeScript编写(不了解该语言的可参考之前的<TypeScript躬行记>),分为三大部分:rrweb-snapshot.rrweb和rr ...

  10. linux入门系列11--Centos7网络服务管理

    通过前面文章的学习已经掌握了Linux系统配置管理的知识,本文讲解Centos7网络配置知识. Linux要对外提供服务,需要保证网络通信正常,因此需要正确配置网络参数.本文将讲解如何使用Networ ...