springboot+taro實現(xiàn)小程序微信登錄并獲得手機(jī)號授權(quán)功能
點擊上方?java項目開發(fā),選擇?設(shè)為星標(biāo)
優(yōu)質(zhì)文章,及時送達(dá)
案例功能效果圖
獲取手機(jī)號碼登錄頁面

微信登錄授權(quán)頁面

手機(jī)&微信都登錄成功頁面

環(huán)境介紹

前端:taro
后端:springboot
jdk:1.8及以上
數(shù)據(jù)庫:mysql
完整源碼獲取方式
源碼獲取方式
掃碼關(guān)注回復(fù)【wxdl】獲取完整源碼
如果你在運行這個代碼的過程中有遇到問題,請加小編微信xxf960513,我拉你進(jìn)對應(yīng)微信學(xué)習(xí)群!!幫助你快速掌握這個功能代碼!
核心代碼介紹
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_idpublic 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="微信小程序服務(wù)",tags={"微信小程序服務(wù)"})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("請求的數(shù)據(jù):[{}]" + 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){// 被加密的數(shù)據(jù)byte[] dataByte = Base64.decode(encryptedData);// 加密秘鑰byte[] keyByte = Base64.decode(sessionkey);// 偏移量byte[] ivByte = Base64.decode(iv);try {// 如果密鑰不足16位,那么就補(bǔ)足. 這個if 中的內(nèi)容很重要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;/*** 創(chuàng)建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("創(chuàng)建HTTPS客戶端異常");}}return httpClientBuilder.build();}/*** 支持 http、https協(xié)議* 調(diào)用Get接口* @param url 接口地址* @param isHttps [true:https協(xié)議,false:http協(xié)議]* @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;}/*** 調(diào)用Post接口** @param url 接口地址* @param params 參數(shù)* @param type 參數(shù)類型,0:鍵值對,1:json數(shù)據(jù)* @param isHttps 參數(shù)類型 false:http協(xié)議,true:https協(xié)議* @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("不支持的參數(shù)格式");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;}/*** 調(diào)用Post接口,參數(shù)為鍵值對方式** @param url 接口地址* @param params 鍵值對參數(shù)* @param isHttps 參數(shù)類型 false:http協(xié)議,true:https協(xié)議* @return*/public static String sendPostByKeyValue(String url, Mapparams, 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);Listnvps = new ArrayList (); for (Map.Entryentry : 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;}/*** 調(diào)用Post接口,參數(shù)為JSON格式** @param url 接口地址* @param params json數(shù)據(jù)* @param isHttps 參數(shù)類型 false:http協(xié)議,true:https協(xié)議* @return*/public static String sendPostByJson(String url, String params,boolean isHttps){return sendPost(url, params, Format_Json, isHttps);}/*** 設(shè)置 請求超時* @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: "拿到微信手機(jī)號碼",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><AtIconprefixClassName="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">手機(jī)號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">點我獲取手機(jī)號Button>)}<View class="sep">View><AtList className="list"><Button openType="contact" className="btn-item"><AtListItem title="聯(lián)系客服" 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">聯(lián)系客服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">聯(lián)系客服Text>Button>View> */}View>);}}export default My;
request.js
/**?@format?*/// axios 默認(rèn)配置export const ApiConfig = {baseUrl: "http://XXX.XXX.XXX.XXX:8000/"};
推薦案例
溫暖提示

請長按識別二維碼
想學(xué)習(xí)更多的java功能案例請關(guān)注
Java項目開發(fā)

如果你覺得這個案例以及我們的分享思路不錯,對你有幫助,請分享給身邊更多需要學(xué)習(xí)的朋友。別忘了《留言+點在看》給作者一個鼓勵哦!
評論
圖片
表情

