springboot+taro实现小程序微信登录并获得手机号授权功能

共 19466字,需浏览 39分钟

 ·

2020-12-29 15:07

点击上方 java项目开发选择 设为星标

优质文章,及时送达

--

案例功能效果图

获取手机号码登录页面

微信登录授权页面

手机&微信都登录成功页面

环境介绍

前端:taro

后端:springboot

jdk:1.8及以上

数据库:mysql


完整源码获取方式



源码获取方式

扫码关注回复【wxdl】获取完整源码


如果你在运行这个代码的过程中有遇到问题,请加小编微信xxf960513,我拉你进对应微信学习群!!帮助你快速掌握这个功能代码!




核心代码介绍

pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0modelVersion>  <parent>    <groupId>org.springframework.bootgroupId>    <artifactId>spring-boot-starter-parentartifactId>    <version>2.4.1version>    <relativePath/>   parent>  <groupId>com.examplegroupId>  <artifactId>wechatartifactId>  <version>0.0.1-SNAPSHOTversion>  <name>wechatname>  <description>Demo project for Spring Bootdescription>
<properties> <skipTests>trueskipTests> <java.version>1.8java.version> <maven-jar-plugin.version>2.6maven-jar-plugin.version> properties>
<dependencies> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starterartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-webartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-testartifactId> <scope>testscope> dependency> <dependency> <groupId>io.springfoxgroupId> <artifactId>springfox-swagger2artifactId> <version>2.9.2version> dependency> <dependency> <groupId>io.swaggergroupId> <artifactId>swagger-modelsartifactId> <version>1.5.21version> dependency> <dependency> <groupId>com.github.xiaoymingroupId> <artifactId>swagger-bootstrap-uiartifactId> <version>1.9.3version> dependency> <dependency> <groupId>org.apache.httpcomponentsgroupId> <artifactId>httpclientartifactId> dependency> <dependency> <groupId>org.codehaus.xfiregroupId> <artifactId>xfire-coreartifactId> <version>1.2.6version> dependency> <dependency> <groupId>org.bouncycastlegroupId> <artifactId>bcprov-jdk16artifactId> <version>1.46version> dependency> <dependency> <groupId>com.alibabagroupId> <artifactId>fastjsonartifactId> <version>1.2.62version> dependency> dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-maven-pluginartifactId> plugin> plugins>  build>project>

WeChatConfig.java

package com.example.wechat.util;
/** * Copyright @ 2020 Zonlyn. All rights reserved.
* @Description: 该类的功能描述** @version: v1.0.0* @author: ducl* @date: 2020年12月16日 上午9:12:21 ** Modification History:* Date Author Version Description*---------------------------------------------------------** 2020年12月16日 ducl v1.0.0 修改原因*/public class WeChatConfig { //微信 app_id public static final String APP_ID ="your APP_ID";//"wx5a383d14e917d3ae"; //app_secret  public static final String APP_SECRET = "0569d415cc撒打多大9cd6a834763"; //获取 session_key地址 public static final String USERINFO_URL = "https://api.weixin.qq.com/sns/jscode2session";}

WeChatController.java

package com.example.wechat.ctrler;
import java.io.IOException;import java.io.UnsupportedEncodingException;import java.security.AlgorithmParameters;import java.security.InvalidAlgorithmParameterException;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.security.NoSuchProviderException;import java.security.Security;import java.security.spec.InvalidParameterSpecException;import java.util.Arrays;
import javax.crypto.BadPaddingException;import javax.crypto.Cipher;import javax.crypto.IllegalBlockSizeException;import javax.crypto.NoSuchPaddingException;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;
import org.bouncycastle.jce.provider.BouncyCastleProvider;import org.codehaus.xfire.util.Base64;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Controller;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.CrossOrigin;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSONObject;import com.example.wechat.bean.WeChatUserInfo;import com.example.wechat.util.HttpUtil;import com.example.wechat.util.WeChatConfig;import com.example.wechat.util.rest.CodeMsg;import com.example.wechat.util.rest.Result;
import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;
/** * Copyright @ 2020 Zonlyn. All rights reserved.
* @Description: 该类的功能描述** @version: v1.0.0* @author: ducl* @date: 2020年12月16日 上午9:03:16 ** Modification History:* Date Author Version Description*---------------------------------------------------------** 2020年12月16日 ducl v1.0.0 修改原因*/@Controller@RequestMapping("/wechat")@CrossOrigin@Api(value="微信小程序服务",tags={"微信小程序服务"})public class WeChatController { private Logger logger =LoggerFactory.getLogger(WeChatController.class); @RequestMapping(value = "/dec/userinfo", method = RequestMethod.POST) @ResponseBody @ApiOperation(value="解析登录成功用户信息") public Object login(@RequestBody WeChatUserInfo infoData, HttpServletRequest request, HttpServletResponse response) throws IOException { logger.info("请求的数据:[{}]" + infoData); //JSONObject dataObj = JSONObject.parseObject(infoData); String code = infoData.getCode(); String encryptedData = infoData.getEncrypted_data(); String iv = infoData.getIv(); boolean paramError = StringUtils.isEmpty(code) || StringUtils.isEmpty(encryptedData) || StringUtils.isEmpty(iv); if(paramError) { return Result.error(CodeMsg.PARAMERROR); } String sessionkey = getSessionKey(code); JSONObject userInfo = this.getUserInfo(encryptedData, sessionkey, iv); logger.info("解析出的用户信息:[{}]",userInfo); return Result.success(userInfo); } private String getSessionKey(String code) { logger.info("我的APP_ID:[{}]",WeChatConfig.APP_ID); String url = WeChatConfig.USERINFO_URL+"?appid=" + WeChatConfig.APP_ID + "&secret=" + WeChatConfig.APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code"; String reusult = HttpUtil.sendGet(url, true); JSONObject oppidObj = JSONObject.parseObject(reusult); //String openid = (String) oppidObj.get("openid"); String session_key = (String) oppidObj.get("session_key"); return session_key; } /** * 获取信息 */ private JSONObject getUserInfo(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 + (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 JSONObject.parseObject(result); } } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidParameterSpecException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { e.printStackTrace(); } return null; }}

HttpUtil.java

åpackage com.example.wechat.util;
import java.security.cert.CertificateException;import java.security.cert.X509Certificate;import java.util.ArrayList;import java.util.List;import java.util.Map;import javax.net.ssl.SSLContext;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.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.client.methods.HttpRequestBase;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.conn.ssl.SSLContextBuilder;import org.apache.http.conn.ssl.TrustStrategy;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClientBuilder;import org.apache.http.message.BasicNameValuePair;import org.apache.http.util.EntityUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * @description httpclient 工具 * @author Administrator * */@SuppressWarnings("deprecation")public class HttpUtil { private static Logger logger = LoggerFactory.getLogger(HttpUtil.class); private static final int Format_KeyValue = 0; private static final int Format_Json = 1; private static final int SOCKET_TIMEOUT = 60 * 1000; private static final int CONN_TIMEOUT = 60 * 1000;
/** * 创建HttpClient客户端 * * @param isHttps * @return */ private static CloseableHttpClient createClient(boolean isHttps) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); if (isHttps) { try { SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { // 信任所有 @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); httpClientBuilder.setSSLSocketFactory(sslsf); } catch (Exception e) { e.printStackTrace(); logger.error("创建HTTPS客户端异常"); } } return httpClientBuilder.build(); }
/** * 支持 http、https协议 * 调用Get接口 * @param url 接口地址 * @param isHttps [true:https协议,false:http协议] * @return */ public static String sendGet(String url, boolean isHttps) { CloseableHttpClient httpClient = createClient(isHttps); if (url == null || "".equals(url)) { logger.error("接口地址为空"); return null; } HttpGet request = null; try { request = new HttpGet(url); if (httpClient == null) { logger.error("HttpClient实例为空"); return null; } setTimeOut(request); CloseableHttpResponse response = httpClient.execute(request); if (response.getStatusLine().getStatusCode() == 200) { return EntityUtils.toString(response.getEntity()); } } catch (Exception e) { logger.error("访问接口失败,接口地址为:" + url); } finally { if (request != null) request.releaseConnection(); } return null; }
/** * 调用Post接口 * * @param url 接口地址 * @param params 参数 * @param type 参数类型,0:键值对,1:json数据 * @param isHttps 参数类型 false:http协议,true:https协议 * @return */ public static String sendPost(String url, String parameters, int type, boolean isHttps) { CloseableHttpClient httpClient = createClient(isHttps); if (url == null || "".equals(url)) { logger.error("接口地址为空"); return null; } HttpPost request = null; try { request = new HttpPost(url); if (httpClient == null) { logger.error("HttpClient实例为空"); return null; } setTimeOut(request); StringEntity entity = new StringEntity(parameters, "UTF-8"); if (type == Format_KeyValue) {// request.addHeader("Content-Type", "application/x-www-form-urlencoded"); entity.setContentType("application/x-www-form-urlencoded"); } else if (type == Format_Json) {// request.addHeader("Content-Type", "application/json"); entity.setContentType("application/json"); } else { logger.error("不支持的参数格式"); return null; } request.setEntity(entity); CloseableHttpResponse response = httpClient.execute(request); if (response.getStatusLine().getStatusCode() == 200) { return EntityUtils.toString(response.getEntity()); } } catch (Exception e) { logger.error("访问接口失败,接口地址为:" + url); throw new RuntimeException(e.getMessage()); } finally { if (request != null) request.releaseConnection(); } return null; }
/** * 调用Post接口,参数为键值对方式 * * @param url 接口地址 * @param params 键值对参数 * @param isHttps 参数类型 false:http协议,true:https协议 * @return */ public static String sendPostByKeyValue(String url, Map params, boolean isHttps) { CloseableHttpClient httpClient = createClient(isHttps); if (url == null || "".equals(url)) { logger.error("接口地址为空"); return null; } HttpPost request = null; try { request = new HttpPost(url); if (httpClient == null) { logger.error("HttpClient实例为空"); return null; } setTimeOut(request); List nvps = new ArrayList(); for (Map.Entry entry : params.entrySet()) { nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } request.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8")); CloseableHttpResponse response = httpClient.execute(request); if (response.getStatusLine().getStatusCode() == 200) { return EntityUtils.toString(response.getEntity()); } } catch (Exception e) { logger.error("访问接口失败,接口地址为:" + url); } finally { if (request != null) request.releaseConnection(); } return null; }
/** * 调用Post接口,参数为JSON格式 * * @param url 接口地址 * @param params json数据 * @param isHttps 参数类型 false:http协议,true:https协议 * @return */ public static String sendPostByJson(String url, String params,boolean isHttps) { return sendPost(url, params, Format_Json, isHttps); } /** * 设置 请求超时 * @author ducl * @param methodType void * @date 2020年7月1日上午8:55:44 */ private static void setTimeOut(HttpRequestBase methodType){ RequestConfig config = RequestConfig.custom() .setConnectTimeout(SOCKET_TIMEOUT) .setConnectionRequestTimeout(CONN_TIMEOUT) .setSocketTimeout(SOCKET_TIMEOUT).build(); methodType.setConfig(config); } }

home.js

/** @format */import { ApiConfig } from "../utils/config";
export default { wechatPhone: { name: "wechatPhone", method: "POST", desc: "拿到微信手机号码", path: ApiConfig.baseUrl + "/yxyz/wechat/dec/userinfo" }};

index.jsx

import Taro from "@tarojs/taro";import React from "react";import { View, Text, Image, Button } from "@tarojs/components";import { AtList, AtListItem, AtIcon } from "taro-ui";import { getData } from "../../utils/request";import home from "../../api/home";import "./index.scss";import { checkStaticImg } from "../../utils";import { connect } from "react-redux";import { commitUserInfo } from "../../store/actions";
const mapStateToProps = state => { return { userInfo: state.my.userInfo };};
const mapDispatchToProps = { commitUserInfo};
@connect(mapStateToProps, mapDispatchToProps)class My extends React.Component { state = { code: "" };
componentDidMount = async () => { const that = this; Taro.login({ success: function(res) { console.log(res, "code"); if (res.code) { that.setState({ code: res.code }); } } }); };
getUserInfo = res => { console.log(res, "res");
if (res.detail && res.detail.userInfo) { this.props.commitUserInfo({ ...this.props.userInfo, ...res.detail.userInfo }); } };
getPhoneNumber = res => { console.log(res, "res");
if (res.detail) { let params = { code: this.state.code, encrypted_data: res.detail.encryptedData, iv: res.detail.iv }; console.log(this.userInfo); getData(params, home.wechatPhone).then(res => { console.log(res, "mydata"); if (res.data.phoneNumber) { let userInfo = { ...this.props.userInfo, phoneNumber: res.data.phoneNumber }; this.props.commitUserInfo(userInfo); } }); } console.log(res, "res"); }; render() { let { userInfo } = this.props; return ( <View className="my-wrap"> {userInfo.nickName ? ( <View class="top-wrap"> <View class="top-wrap-con"> <Image class="logo" src={userInfo.avatarUrl}>Image> <View class="top-con"> <Text class="title">{userInfo.nickName}Text> {/* <Text class="title">性别:{userInfo.gender === 1 ? "男" : "女"}Text> <Text class="title"> 地址:{userInfo.province}/{userInfo.city} Text> */} {userInfo.phoneNumber ? <Text class="phone">{userInfo.phoneNumber}Text> : null} View> <AtIcon prefixClassName="iconfont" value="edit-outline" size="16" color={"#999"} className={`iconfont icon-edit-outline`} >AtIcon> View> {/* {userInfo.phoneNumber ? null : ( <Button open-type="getPhoneNumber" onGetPhoneNumber={this.getPhoneNumber} class="phone-btn"> 手机号 Button> )} */} View> ) : ( <View class="top-wrap"> <Button open-type="getUserInfo" onGetUserInfo={this.getUserInfo} class="no-login"> <Image class="img" src={checkStaticImg("login.png")}>Image> <View class="login-label"> <Text class="login-label-title">点击登录Text> {userInfo.phoneNumber ? <Text class="phone">{userInfo.phoneNumber}Text> : null} View> Button> View> )} {userInfo.phoneNumber ? null : ( <Button open-type="getPhoneNumber" onGetPhoneNumber={this.getPhoneNumber} class="phone-btn"> 点我获取手机号 Button> )} <View class="sep">View> <AtList className="list"> <Button openType="contact" className="btn-item"> <AtListItem title="联系客服" thumb={checkStaticImg("contact.png")} arrow="right" className="list-item" /> Button> <Button openType="feedback" className="btn-item"> <AtListItem title="意见反馈" thumb={checkStaticImg("feedback.png")} arrow="right" className="list-item" /> Button> {/* <Button openType="contact" className="btn-item"> <Image src={checkStaticImg("contact.png")} className="btn-img">Image> <Text className="btn-text">联系客服Text> Button> */} AtList> {/* <View className="btn-wrap"> <Button openType="feedback" className="btn-item"> <Image src={checkStaticImg("feedback.png")} className="btn-img">Image> <Text className="btn-text">意见反馈Text> Button> <Button openType="contact" className="btn-item"> <Image src={checkStaticImg("contact.png")} className="btn-img">Image> <Text className="btn-text">联系客服Text> Button> View> */} View> ); }}export default My;

request.js

/** @format */// axios 默认配置export const ApiConfig = {  baseUrl: "http://XXX.XXX.XXX.XXX:8000/"};
- END -

推荐案例

温暖提示

为了方便大家更好的学习,本公众号经常分享一些完整的单个功能案例代码给大家去练习,如果本公众号没有你要学习的功能案例,你可以联系小编(微信:xxf960513)提供你的小需求给我,我安排我们这边的开发团队免费帮你完成你的案例。
注意:只能提单个功能的需求不能要求功能太多,比如要求用什么技术,有几个页面,页面要求怎么样?


请长按识别二维码

想学习更多的java功能案例请关注

Java项目开发

如果你觉得这个案例以及我们的分享思路不错,对你有帮助,请分享给身边更多需要学习的朋友。别忘了《留言+点在看》给作者一个鼓励哦!

浏览 55
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报