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

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. 吴sir讲Python之 ——Pycharm的教程使用(二)

    欢迎您进入老吴的博客,如有联系请加QQ群:1055524279 Pycharm使用: 第一步:打开的界面: 选择代码路径和Python解释器版本 设置Pycharm菜单字体的大小: 设置编辑器里面字体 ...

  2. 关于使用详解ASP.NET State Service

    ASP.NET State Service服务如果启动可以解决这个问题,它会生成一个aspnet_state.exe进程,这个就是Session信息的进程.只要这个进程在,就算是重启了IIS,站点的S ...

  3. 机器学习总结-bias–variance tradeoff

    bias–variance tradeoff 通过机器学习,我们可以从历史数据学到一个\(f\),使得对新的数据\(x\),可以利用学到的\(f\)得到输出值\(f(x)\).设我们不知道的真实的\( ...

  4. HDU6183 Color it (线段树动态开点)

    题意: 一个1e6*1e6的棋盘,有两个操作:给(x,y)加上颜色c,或查找(1,y1)到(x,y2)内的颜色种类数量,最多有50种颜色 思路: 建立50颗线段树,对每个颜色的线段树,维护每个y坐标上 ...

  5. Spring事务中的隔离级别

    TransactionDefinition接口中定义了五个表示隔离级别的常量: TransactionDefinition.ISOLATION_DEFAULT:使用后端数据库默认的隔离界别,MySQL ...

  6. redis说明及部署

    一.reids 概述 redis全称REmote DIctionary Server.一个基于KV的持久化分布式数据库.所编写的语言为C.与另一个分布式缓存Memcached有几分相似 但是redis ...

  7. cdh集群hive升级,数据不丢失

    1.下载hive-1.2.1安装包 http://archive.apache.org/dist/hive/hive-1.2.1/apache-hive-1.2.1-bin.tar.gz 2.将安装包 ...

  8. Angular RxJs:针对异步数据流编程工具

    一. RxJs:针对异步数据流编程工具 1. 创建subject类对象(发送方) 2. subject.subscribe(观察者); (注册观察者对象observer,可以注册多个相当于回调函数取数 ...

  9. Nginx-2.初学者使用

    原文 Nginx有一个master进程和几个worker进程.master进程用来读取和评估配置文件,以及维护worker进程.worker进程用来处理实际的请求.Nginx使用事件模型和基于操作系统 ...

  10. C# 根据天、周、月汇总统计生成统计报表

    先看核心代码: public List<DataEntity> SearchShopSalesReport(DateTimeOffset? dateFrom, DateTimeOffset ...