springboot+taro实现小程序微信登录并获得手机号授权功能
点击上方 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
*
*/
"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()
{
// 信任所有
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/"
};
推荐案例
温暖提示
请长按识别二维码
想学习更多的java功能案例请关注
Java项目开发
如果你觉得这个案例以及我们的分享思路不错,对你有帮助,请分享给身边更多需要学习的朋友。别忘了《留言+点在看》给作者一个鼓励哦!
评论