小程序登錄、微信網頁授權(Java版)

小程序登錄、微信網頁授權(Java版)

首先呢,“登錄”、“授權”、“授權登錄”,是一樣的意思,不用糾結。

寫小程序授權登錄的代碼前,需要了解清楚openid與unionid的區別,這裏再簡單介紹一下:

  1. 騰訊有個 “微信·開放平臺”,只有企業才能註冊賬號,可理解爲微信體系裏,最頂級的賬號。官網地址:https://open.weixin.qq.com
  2. 除了這個微信開放平臺,還有另一個叫做 “微信公衆平臺”,可註冊四種賬號,包括服務號、訂閱號、小程序、企業微信。也就是說,公衆號(服務號和訂閱號可統稱爲公衆號)佔一個賬號,小程序也佔一個賬號。在沒有綁定開放平臺前,小程序授權登錄只能拿到用戶的openid。官網地址:https://mp.weixin.qq.com
  3. 小程序可綁定在公衆號下,公衆號可以綁定在微信開放平臺下,小程序也可以綁定在微信開放平臺下。(好像有點小繞)簡單點說,所有的公衆平臺賬號都需要綁定在 “開放平臺” 下,纔可獲得的unionid,這是打通同個企業下所有微信公衆賬號的最有效方法(官方推薦)
  4. 更加具體的可自行百度...

一、以下爲小程序登錄的代碼:

  • 方式一:通過code調用code2session接口獲得message,包含openid、session_key,滿足條件的情況下還能直接獲得unionid

    • 條件如下:(存在侷限性)
    1. 官方說明UnionID獲取途徑,如果開發者帳號下存在同主體的公衆號,並且該用戶已經關注了該公衆號。開發者可以直接通過 wx.login + code2Session 獲取到該用戶 UnionID,無須用戶再次授權。
    2. 開發者帳號下存在同主體的公衆號或移動應用,並且該用戶已經授權登錄過該公衆號或移動應用。也可通過code2session獲取該用戶的 UnionID。
/**
 * Author: huanglp
 * Date: 2018-11-28
 */
public class WeiXinUtils {

    private static Logger log = LoggerFactory.getLogger(WeiXinUtils.class);

    /**
     * 通過前端傳過來的code, 調用小程序登錄接口, 獲取到message並返回 (包含openid session_key等)
     *
     * @param code
     * @return
     */
    public static JSONObject login(String code) {
        log.info("==============小程序登錄方法開始================");
        WxMiniProperties properties = WeiXinPropertiesUtils.getWxMiniProperties();
        String url = properties.getInterfaceUrl() + "/sns/jscode2session?appid="
            + properties.getAppId() + "&secret=" + properties.getAppSecret() 
            + "&js_code=" + code + "&grant_type=authorization_code";
        JSONObject message;
        try {
            // RestTemplate是Spring封裝好的, 挺好用, 可做成單例模式
            RestTemplate restTemplate = new RestTemplate();
            String response = restTemplate.getForObject(url, String.class);
            message = JSON.parseObject(response);
        } catch (Exception e) {
            log.error("微信服務器請求錯誤", e);
            message = new JSONObject();
        }
        log.info("message:" + message.toString());
        log.info("==============小程序登錄方法結束================");
        return message;

        // 後續, 可獲取openid session_key等數據, 以下代碼一般放在Service層
        //if (message.get("errcode") != null) {
        //    throw new ValidationException(message.toString());
        //}
        //String openid = message.get("openid").toString();
        //String sessionKey = message.get("session_key").toString();
        //...

    }
}
    • 補充1: WeiXinPropertiesUtils工具類
public class WeiXinPropertiesUtils {

    // 微信小程序配置
    private static WxMiniProperties miniProperties;
    // 微信公衆號配置
    private static WxProperties wxProperties;

    private static void init() {
        if (miniProperties == null) {
            miniProperties = ContextLoader.getCurrentWebApplicationContext()
                .getBean(WxMiniProperties.class);
        }
        if (wxProperties == null) {
            wxProperties = ContextLoader.getCurrentWebApplicationContext()
                .getBean(WxProperties.class);
        }
    }

    public static WxMiniProperties getWxMiniProperties() {
        init();
        return miniProperties;
    }

    public static WxProperties getWxProperties() {
        init();
        return wxProperties;
    }
}
    • 補充2: WxMiniProperties配置類
@Data
@Component
@ConfigurationProperties(prefix = "luwei.module.wx-mini")
public class WxMiniProperties {

    private String appId;
    private String appSecret;
    private String interfaceUrl;

}

到此已能通過code獲取到用戶的openid和session_key,但若不滿足條件,即使將小程序綁定到微信開放平臺上,也獲取不到unionid,所以此方式不穩定,推薦使用解密的方式獲取數據。

  • 方式二:通過解密的方式獲取用戶unionid
/**
 * 通過encryptedData,sessionKey,iv獲得解密信息, 擁有用戶豐富的信息, 包含openid,unionid,暱稱等
 */
public static JSONObject decryptWxData(String encryptedData, String sessionKey, String iv) throws Exception {
    log.info("============小程序登錄解析數據方法開始==========");
    String result = AesCbcUtil.decrypt(encryptedData, sessionKey, iv, "UTF-8");
    JSONObject userInfo = new JSONObject();
    if (null != result && result.length() > 0) {
        userInfo = JSONObject.parseObject(result);
    }
    log.info("result: " + userInfo);
    log.info("============小程序登錄解析數據方法結束==========");
    return userInfo;
}
    • 補充1: AesCbcUtil工具類,直接複製即可,需要添加bouncycastle依賴。BouncyCastle是一個開源的加解密解決方案,官網可查看http://www.bouncycastle.org/
package com.luwei.common.utils;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;

/**
 * Updated by huanglp
 * Date: 2018-11-28
 */
public class AesCbcUtil {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * AES解密
     *
     * @param data     //被加密的數據
     * @param key      //加密祕鑰
     * @param iv       //偏移量
     * @param encoding //解密後的結果需要進行的編碼
     */
    public static String decrypt(String data, String key, String iv, String encoding) {

        // org.apache.commons.codec.binary.Base64
        byte[] dataByte = Base64.decodeBase64(data);
        byte[] keyByte = Base64.decodeBase64(key);
        byte[] ivByte = Base64.decodeBase64(iv);

        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            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) {
                return new String(resultByte, encoding);
            }
            return null;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

到此已經獲取到 JSONObject類型的 userInfo,包含openid,unionid,暱稱,頭像等數據

後續可以將用戶信息保存到數據庫,再返回給前端一個token即可,shiro經過公司封裝了一層,代碼如下:

...
// 獲得用戶ID
int userId = wxUser.getWxUserId();
shiroTokenService.afterLogout(userId);
String uuid = UUID.randomUUID().toString();
String token = StringUtils.deleteAny(uuid, "-") + Long.toString(System.currentTimeMillis(), Character.MAX_RADIX);
shiroTokenService.afterLogin(userId, token, null);
return token;

二、以下爲公衆號(網頁)授權的代碼:

網頁授權更加簡單,可查看 官方文檔

需添加 riversoft 相關依賴包,公衆號網頁授權,只需要將公衆號綁定了開放平臺,就能獲取到unionid及其他用戶信息。

public static OpenUser webSiteLogin(String code, String state) {
    log.info("============微信公衆號(網頁)授權開始===========");
    WxProperties properties = WeiXinPropertiesUtils.getWxProperties();
    AppSetting appSetting = new AppSetting(properties.getAppId(), properties.getAppSecret());
    OpenOAuth2s openOAuth2s = OpenOAuth2s.with(appSetting);
    AccessToken accessToken = openOAuth2s.getAccessToken(code);

    // 獲取用戶信息
    OpenUser openUser = openOAuth2s.userInfo(accessToken.getAccessToken(), accessToken.getOpenId());
    log.info("============微信公衆號(網頁)授權結束===========");
    return openUser;
    
    // 後續, 可將用戶信息保存
    // 最後一步, 生成token後, 需重定向回頁面
    //return "redirect:" + state + "?token=" + token;
}

署名

廣州蘆葦科技Java開發團隊

蘆葦科技-廣州專業互聯網軟件服務公司

抓住每一處細節 ,創造每一個美好

關注我們的公衆號,瞭解更多

想和我們一起奮鬥嗎?lagou搜索“ 蘆葦科技 ”或者投放簡歷到 [email protected] 加入我們吧

關注我們,你的評論和點贊對我們最大的支持

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章