2025 Java 微信小程序根据code获取openid,二次code获取手机号【工具类】拿来就用

一、controller调用 


/**
 * 登录
 *
 * @author jiaketao
 * @since 2024-04-10
 */
@RestController
@RequestMapping("/login")
public class LoginController {

    /**
     * 【小程序】登录获取session_key和openid
     *
     * @param code 前端传code
     * @return
     */
    @GetMapping("/getWXSessionKey")
    public 自己的业务返回体 getWXSessionKey(String code) {
        //根据前端传来的code,调用微信的接口获取到当前用户的openid
        JSONObject jsonObject = WeChatLoginUtils.getWXSessionKey(code);
        String openId = jsonObject.getString("openid");
        //自己的业务逻辑...    }

    /**
     * 【小程序】获取手机号
     *
     * @param code 第二次的code
     * @return
     */
    @GetMapping("/getWXPhoneInfo")
    public String getWXPhoneInfo(String code) throws IOException {
        return  WeChatLoginUtils.getWXPhoneInfo(code).getJSONObject("phone_info").getString("phoneNumber");
    }

  
}

二、封装的工具类

 1、微信登录核心工具类:WeChatLoginUtils

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.io.IOException;


/**
 * 微信小程序登录工具类
 *
 * @author jiaketao
 * @since 2024-04-10
 */
public class WeChatLoginUtils {

    /**
     * 1.获取session_key
     *
     * @param code 微信小程序的第一个code
     * @return
     */
    public static JSONObject getWXSessionKey(String code) {
        String urlResult = HttpRequestUrlUtil.httpGet("https://api.weixin.qq.com/sns/jscode2session?appid=" + WxConstUtils.WX_OPEN_APPLET_APPID + "&secret=" + WxConstUtils.WX_OPEN_APPLET_SECRET + "&js_code=" + code + "&grant_type=authorization_code");
        // 转为json
        JSONObject jsonObject = JSON.parseObject(urlResult);
        return jsonObject;
    }

    /**
     * 2.获取access_token
     *
     * @return
     */
    public static JSONObject getWXAccessToken() {
        String urlResult = HttpRequestUrlUtil.httpGet("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + WxConstUtils.WX_OPEN_APPLET_APPID + "&secret=" + WxConstUtils.WX_OPEN_APPLET_SECRET);
        // 转为json
        JSONObject jsonObject = JSON.parseObject(urlResult);
        return jsonObject;
    }

    /**
     * 获取手机号
     *
     * @param code 微信小程序的第2个code
     * @return
     */
    public static JSONObject getWXPhoneInfo(String code) throws IOException {
        // 调用 2.获取access_token
        String access_token = getWXAccessToken().getString("access_token");
        String getPhoneNumberUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + access_token;
        JSONObject json = new JSONObject();
        json.put("code", code);
        // post请求
        json = HttpRequestUrlUtil.postResponse(getPhoneNumberUrl, json);
        return json;
    }


}

2、配置文件:WxConstUtils

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


/**
 * 微信登录支付相关字段工具类
 *
 * @author zhaoyan
 * @since 2024-04-10
 */
@Component
public class WxConstUtils implements InitializingBean {
    // 小程序 appid
    public static String WX_OPEN_APPLET_APPID;
    // 小程序 secret密钥
    public static String WX_OPEN_APPLET_SECRET;
    // 商户号
    public static String WX_OPEN_MCHID;
    // 密钥key
    public static String WX_OPEN_KEY;

    //从配置文件获取以上内容的配置值
    // 小程序appid
    @Value("${wx.open.applet.app_id}")
    private String appletAppId;

    //小程序 secret密钥
    @Value("${wx.open.applet.secret}")
    private String appletSecret;

    // 商户号
    @Value("${wx.open.mch_id}")
    private String mchId;

    // 密钥key
    @Value("${wx.open_key}")
    private String key;

    @Override
    public void afterPropertiesSet() throws Exception {
        WX_OPEN_APPLET_APPID = appletAppId;
        WX_OPEN_APPLET_SECRET = appletSecret;
        WX_OPEN_MCHID = mchId;
        WX_OPEN_KEY = key;
    }
}

3、http请求工具类:HttpRequestUrlUtil

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
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.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;


/**
 * http请求
 *
 * @author jiaketao
 * @since 2024-04-10
 */
public class HttpRequestUrlUtil {

    /**
     * post请求
     *
     * @param url
     * @param object
     * @return
     */
    public static String httpPost(String url, JSONObject object) {

        String result = "";

        // 使用CloseableHttpClient和CloseableHttpResponse保证httpcient被关闭
        CloseableHttpClient httpClient;

        CloseableHttpResponse response;

        HttpPost httpPost;

        UrlEncodedFormEntity entity;

        try {

            httpClient = HttpClients.custom().build();

            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();

            Set<String> keySets = object.keySet();

            Iterator<String> keys = keySets.iterator();

            while (keys.hasNext()) {

                String key = keys.next();

                nameValuePairs.add(new BasicNameValuePair(key, object.getString(key)));
            }

            entity = new UrlEncodedFormEntity(nameValuePairs, "utf-8");

            httpPost = new HttpPost(url);

            httpPost.setEntity(entity);

            //设置超时时间
            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(10000).build();

            httpPost.setConfig(requestConfig);

            response = httpClient.execute(httpPost);

            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {

                result = EntityUtils.toString(response.getEntity());
            }

        } catch (ClientProtocolException e) {

        } catch (IOException e) {

        } catch (Exception e) {

        } finally {

        }

        return result;
    }

    /**
     * get请求
     *
     * @param url
     * @return
     */
    public static String httpGet(String url) {

        String result = "";

        // 使用CloseableHttpClient和CloseableHttpResponse保证httpcient被关闭
        CloseableHttpClient httpClient = null;

        CloseableHttpResponse response = null;

        try {

            httpClient = HttpClients.custom().build();

            response = httpClient.execute(new HttpGet(url));

            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {

                result = EntityUtils.toString(response.getEntity());
            }

        } catch (ClientProtocolException e) {
        } catch (IOException e) {
        } catch (Exception e) {
        } finally {

        }

        return result;
    }

    /**
     * post请求封装 参数为?a=1&b=2&c=3
     *
     * @param path 接口地址
     * @param Info 参数
     * @return
     * @throws IOException
     */
    public static JSONObject postResponse(String path, String Info) throws IOException {

        //1, 得到URL对象
        URL url = new URL(path);

        //2, 打开连接
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        //3, 设置提交类型
        conn.setRequestMethod("POST");

        //4, 设置允许写出数据,默认是不允许 false
        conn.setDoOutput(true);
        conn.setDoInput(true);//当前的连接可以从服务器读取内容, 默认是true

        //5, 获取向服务器写出数据的流
        OutputStream os = conn.getOutputStream();
        //参数是键值队  , 不以"?"开始
        os.write(Info.getBytes());
        //os.write("googleTokenKey=&username=admin&password=5df5c29ae86331e1b5b526ad90d767e4".getBytes());
        os.flush();
        //6, 获取响应的数据
        //得到服务器写回的响应数据
        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
        String str = br.readLine();
        JSONObject json = JSONObject.parseObject(str);

        System.out.println("响应内容为:  " + json);

        return json;
    }

    /**
     * post请求封装 参数为{"a":1,"b":2,"c":3}
     *
     * @param path 接口地址
     * @param Info 参数
     * @return
     * @throws IOException
     */
    public static JSONObject postResponse(String path, JSONObject Info) throws IOException {
        HttpClient client = new DefaultHttpClient();
        HttpPost post = new HttpPost(path);

        post.setHeader("Content-Type", "application/json");
        post.addHeader("Authorization", "Basic YWRtaW46");
        String result = "";

        try {
            StringEntity s = new StringEntity(Info.toString(), "utf-8");
            s.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
            post.setEntity(s);

            // 发送请求
            HttpResponse httpResponse = client.execute(post);

            // 获取响应输入流
            InputStream inStream = httpResponse.getEntity().getContent();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, "utf-8"));
            StringBuilder strber = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null)
                strber.append(line + "\n");
            inStream.close();

            result = strber.toString();
            System.out.println(result);

            if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                System.out.println("请求服务器成功,做相应处理");
            } else {
                System.out.println("请求服务端失败");
            }

        } catch (Exception e) {
            System.out.println("请求异常");
            throw new RuntimeException(e);
        }

        return JSONObject.parseObject(result);
    }
}

三、maven依赖:pom.xml

<!--  导入Json格式化依赖 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>

<!--  导入Apache HttpClient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.14</version>
</dependency>


<!-- Spring Boot Starter (包含 Spring Core, Spring Beans, Spring Context 等) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.7.18</version> <!-- 或使用最新版本 -->
</dependency>

<!-- 如果需要 @Value 注解读取配置文件 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-config</artifactId>
    <version>2.7.18</version>
</dependency>

四、配置文件:application.properties

#wechat  Config
wx.open.applet.app_id=自己的appid
wx.open.applet.secret=自己的secret密钥
#wx.open.applet.sub_app_id=
wx.open.mch_id=
wx.open_key=
wx.open.sub_mch_id=

<think>嗯,用户想了解微信小程序获取手机号的完整流程,以及如何在Java后端解密code获取session_key。首先,我需要回忆一下微信小程序的流程。小程序前端需要用户主动触发按钮,调用getPhoneNumber接口,然后会返回加密数据encryptedData和初始向量iv。然后,前端需要将这两个参数以及登录时获取code传给后端。 接下来,后端需要处理code获取session_key。根据微信的文档,code需要通过微信接口换成session_key和openid。这时候,我需要确认用户提供的引用中是否有相关的Java代码示例。比如引用[3]提到了使用appid、secret和code来构造请求参数,发送到微信的接口。然后解析返回的JSON数据,获取session_key和openid。 然后,有了session_key后,后端需要解密encryptedData和iv。解密算法应该是AES-128-CBC,并且可能需要处理填充。引用[2]提到了工具类中添加解密方法,可能涉及使用javax.crypto库中的Cipher类。这里需要注意处理密钥和初始向量是否正确,还有数据填充方式是否为PKCS7Padding,但Java默认可能不支持,需要自己处理或者使用Bouncy Castle库。 另外,用户可能遇到的常见问题包括code无效、session_key过期,或者解密失败。例如,code只能使用一次,且有效期5分钟,所以需要及时处理。解密失败可能因为session_key不匹配,或者iv和encryptedData不正确,需要检查前端传输是否正确,或者session_key是否正确获取。 还要考虑安全性问题,比如session_key不应该返回给前端,存储时要注意安全。引用[4]提到PHP的实现,但Java类似,需要确保解密过程安全可靠。 总结步骤应该是:1.前端按钮触发获取手机号;2.前端传递code、encryptedData和iv到后端;3.后端用code换取session_key;4.使用session_key解密手机号信息。需要确保每一步的参数正确,错误处理到位,比如网络请求失败、解密异常等。</think>### 微信小程序获取手机号Java后端解密流程详解 #### 一、完整流程概述 1. **前端交互**:用户点击小程序按钮触发`bindgetphonenumber`事件,返回加密数据`encryptedData`和初始向量`iv`[^4]。 2. **登录凭证**:前端先通过`wx.login()`获取`code`,用于后端换取`session_key`和`openid`[^3]。 3. **数据传输**:前端将`code`、`encryptedData`、`iv`传递给后端。 4. **后端处理**: - 用`code`请求微信接口换取`session_key`和`openid`[^1] - 使用`session_key`解密`encryptedData`和`iv`获取手机号[^2] #### 二、Java后端实现步骤 ##### 1. 通过code换取session_key ```java // 构造请求参数(引用自引用[3]) String url = "https://api.weixin.qq.com/sns/jscode2session"; String param = "appid=小程序id&secret=小程序secret&js_code=" + code + "&grant_type=authorization_code"; // 发送HTTP请求获取session_key HttpResponse response = HttpClient.get(url + "?" + param); JSONObject json = JSON.parseObject(response.body()); String sessionKey = json.getString("session_key"); String openid = json.getString("openid"); ``` ##### 2. 解密手机号数据(AES-128-CBC算法) ```java public static String decryptData(String encryptedData, String iv, String sessionKey) throws Exception { byte[] data = Base64.getDecoder().decode(encryptedData); byte[] key = Base64.getDecoder().decode(sessionKey); byte[] ivBytes = Base64.getDecoder().decode(iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); byte[] result = cipher.doFinal(data); return new String(result, StandardCharsets.UTF_8); } // 解密后得到JSON格式手机号数据: // {"phoneNumber":"13800000000","purePhoneNumber":"13800000000",...} ``` #### 三、关键注意事项 1. **时效性**:`code`有效期为5分钟,需及时使用 2. **安全存储**:`session_key`不可返回前端,需服务器安全存储 3. **异常处理**: - 网络请求失败重试机制 - 解密失败时检查参数编码(必须Base64解码) - 微信接口返回错误码处理(如40029无效code) #### 四、常见问题排查 1. **session_key不匹配**:确保使用同一用户的`code`和`session_key` 2. **解密失败**: - 检查`encryptedData`和`iv`是否完整传输 - 验证Base64解码是否正确 - 确认加密算法参数为`AES/CBC/PKCS5Padding`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

假客套

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值