Java(SpringBoot)實現釘釘機器人消息推送

零、前言

上一次作消息推送,是微信公衆號的定時消息通知。
因爲本身當時的水平不夠,加上企鵝家的開發文檔廣泛不太友好,致使根本看不懂文檔在寫什麼,不得不去看第三方博客來學習公衆號的開發。
此次就不同了,昨天剛看了一下,阿里的開發文檔比鵝廠要清晰的多,並且在同一功能上,使用了多種語言做爲示例代碼,能夠說很友好了。可能這就是阿里和鵝廠的區別吧...辣雞文檔和好文檔的區別...
本着「授之以漁」的態度,寫了這篇文章,做爲官方文檔的補充。html

圖片.png

1、在羣裏添加機器人

羣設置智能羣助手中添加自定義機器人,它長這個樣子:
圖片.pngjava

比較關鍵的一步,是進行安全設置
加密方式一共有三種,既能夠選擇一種也可使用多種方式組合:算法

  • 自定義關鍵詞
  • 加簽
  • IP地址

各類加密方式的介紹,詳見官網:
https://ding-doc.dingtalk.com...apache

爲了讓博客起到效果,咱們選擇相對安全、也比較難的加簽方式。
選擇加簽以後,把密鑰複製出來,而後就能夠點肯定了。json

2、構建請求地址和內容

先看看官方文檔怎麼描述加簽的:c#

第一步,把 timestamp+"\n"+密鑰當作簽名字符串,使用 HmacSHA256算法計算簽名,而後進行 Base64 encode,最後再把簽名參數再進行 urlEncode,獲得最終的簽名(須要使用UTF-8字符集)。
第二步,把  timestamp和第一步獲得的 簽名值拼接到URL中。

官方的解釋很高大上,其實原理很簡單,就是把機器人密鑰加密後,放在URL的參數中,因此咱們須要分別獲取時間戳密鑰,組合一下,加密一下,再拼接一下就行了,如圖:api

圖片.png

I have a Pen,
I have an Apple,
Oh~ Applepen~ 安全

官方給出了這樣的示例代碼:微信

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import java.net.URLEncoder;

public class Test {
    public static void main(String[] args) throws Exception {
        Long timestamp = System.currentTimeMillis();
        String secret = "this is secret";

        String stringToSign = timestamp + "\n" + secret;
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
        byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
        String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)),"UTF-8");
        System.out.println(sign);
    }
}

然而,org.apache.commons.codec.binary.Base64不是Java的內置類,也就是說,示例代碼並不能直接拿過來用app

圖片.png

查了一下,發現Java8中內置的java.util已經包含了Base64,所以用它替換掉原來的codec,無需再引入第三方包:

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.net.URLEncoder;

public class ding {
    public static void main(String[] args) throws Exception {
        //獲取時間戳
        Long timestamp = System.currentTimeMillis();
        //定義密鑰
        String secret = "this is secret";
        //把時間戳和密鑰拼接成字符串,中間加入一個換行符
        String stringToSign = timestamp + "\n" + secret;
        //聲明一個Mac對象,用來操做字符串
        Mac mac = Mac.getInstance("HmacSHA256");
        //初始化Mac對象,設置Mac對象操做的字符串是UTF-8類型,加密方式是SHA256
        mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
        //把字符串轉化成字節形式
        byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
        //新建一個Base64編碼對象
        Base64.Encoder encoder = Base64.getEncoder();
        //把上面的字符串進行Base64加密後再進行URL編碼
        String sign = URLEncoder.encode(new String(encoder.encodeToString(signData)),"UTF-8");
        //分別輸出時間戳和加密信息
        System.out.println(timestamp);
        System.out.println(sign);
    }
}

用最笨的方法,在終端執行一下看看:
圖片.png

成功輸出了時間戳驗證信息

咱們測試上述代碼的時候,能夠手動拼接URL,直接發起請求:
(URL一共有三個參數:access_token、timestamp、sign,須要換成本身的,也就是上面終端輸出的結果

//替換參數後,在終端執行
curl 'https://oapi.dingtalk.com/robot/send?access_token=70c168d03e73728ef36abea63c3c10048cbd054913cfeb&timestamp=1584607421017&sign=gJ3l4mhnlMuHxK1qFUx1kKUSdjuCNntsdG%2Bv%2BTCrLQM%3D' \
   -H 'Content-Type: application/json' \
   -d '{"msgtype": "text", 
        "text": {
             "content": "我就是我, 是不同的煙火"
        },
        "sign": "gJ3l4mhnlMuHxK1qFUx1kKUSdjuCNntsdG%2Bv%2BTCrLQM%3D"
      }'

而後就出現了:
圖片.png

通過測試,代碼正常運行,接下來就是部署到生產環境了。

3、部署代碼

咱們須要先找一下Spring如何發起HTTP請求。

之前,筆者只用過前臺的HttpClient,對於後臺的HTTP工具並不瞭解。

一開始嘗試用Spring內置的RestTemplate,去網上查了它的用法,寫了一堆代碼,但怎麼也不成功。因爲歷來沒用過RestTemplate,也沒耐心去看它的源碼,因而放棄。

後來,只能老老實實的用apache的httpClient,查了一下用法,雖然有點麻煩,不少操做無法自動完成,但還算通俗易懂,並且它的包託管在Maven上,導入很方便。

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.9</version>
</dependency>

httpClient的使用很靈活,這裏使用的是POST方式,有一個參數,發起POST請求時,必須將字符集編碼設置成UTF-8

粗略步驟如圖:
圖片.png

直接來一段稍微改一下就能用的代碼:

public class DingService {
    //請求地址以及access_token
    String Webhook = "https://oapi.dingtalk.com/robot/send?access_token=YOUR TOKEN";
    //密鑰
    String secret = "YOUR SECRET";

    /*
    ** 生成時間戳和驗證信息
    */
    
    public String encode() throws Exception {
        //獲取時間戳
        Long timestamp = System.currentTimeMillis();
        //把時間戳和密鑰拼接成字符串,中間加入一個換行符
        String stringToSign = timestamp + "\n" + this.secret;
        //聲明一個Mac對象,用來操做字符串
        Mac mac = Mac.getInstance("HmacSHA256");
        //初始化,設置Mac對象操做的字符串是UTF-8類型,加密方式是SHA256
        mac.init(new SecretKeySpec(this.secret.getBytes("UTF-8"), "HmacSHA256"));
        //把字符串轉化成字節形式
        byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
        //新建一個Base64編碼對象
        Base64.Encoder encoder = Base64.getEncoder();
        //把上面的字符串進行Base64加密後再進行URL編碼
        String sign = URLEncoder.encode(new String(encoder.encodeToString(signData)),"UTF-8");
        System.out.println(timestamp);
        System.out.println(sign);
        String result = "&timestamp=" + timestamp + "&sign=" + sign;
        return result;
    };

    /* param: message 要發送的信息
    ** return: void 無返回值
    ** 做用:把傳入的message發送給釘釘機器人*/

    public void dingRequest(String message){
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        String url = null;
        try {
            url = this.Webhook + this.encode();
        } catch (Exception e) {
            e.printStackTrace();
        }
        HttpPost httpPost = new HttpPost(url);
        //設置http的請求頭,發送json字符串,編碼UTF-8
        httpPost.setHeader("Content-Type", "application/json;charset=utf8");
        //生成json對象傳入字符
        JSONObject result = new JSONObject();
        JSONObject text = new JSONObject();
        text.put("content", message);
        result.put("text", text);
        result.put("msgtype", "text");
        String jsonString = JSON.toJSONString(result);
        StringEntity entity = new StringEntity(jsonString, "UTF-8");
        //設置http請求的內容
        httpPost.setEntity(entity);
        // 響應模型
        CloseableHttpResponse response = null;
        try {
            // 由客戶端執行(發送)Post請求
            response = httpClient.execute(httpPost);
            // 從響應模型中獲取響應實體
            HttpEntity responseEntity = response.getEntity();
            System.out.println("響應狀態爲:" + response.getStatusLine());
            if (responseEntity != null) {
                System.out.println("響應內容長度爲:" + responseEntity.getContentLength());
                System.out.println("響應內容爲:" + EntityUtils.toString(responseEntity));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 釋放資源
                if (httpClient != null) {
                    httpClient.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

總結

消息推送別煩惱,這個功能很是好。
只要原理好好搞,融會貫通沒煩惱。
寫完代碼心情好,慶祝一下少不了。
出門上街處處跑,去吃祕製小漢堡。

其實消息推送的功能並不難,只是因爲初次接觸,須要查不少的文檔,在這個過程當中,鍛鍊了文本閱讀能力和獨立解決問題的能力。

參考資料

Java如何進行Base64的編碼(Encode)與解碼(Decode)

Spring RestTemplate介紹

Spring--Http請求--HttpClient

相關文章
相關標籤/搜索