背景 項目上需要對接scom微軟監控系統告警,能夠拿到手的資料十分有限,只有幾個官方文檔地址: Operations Manager REST API Reference - Operations Manager REST API | Microsoft Learn SCOM: Quick Star ...
背景
項目上需要對接scom微軟監控系統告警,能夠拿到手的資料十分有限,只有幾個官方文檔地址:
Operations Manager REST API Reference - Operations Manager REST API | Microsoft Learn
SCOM告警儀錶盤頁面展示:
註意事項
僅支持 System Center Operations Manager 1801 及以上版本有RESTAPI,千萬註意;
對接過程
官網正好有一個獲取告警的示例,但是是powershell版本的:
#Set Headers for the request
$userName ="domain\username";
$password ="Password";
$AuthenticationMode= "Network"
$scomHeaders = New-Object “System.Collections.Generic.Dictionary[[String],[String]]”
$scomHeaders.Add('Content-Type','application/json; charset=utf-8')
$bodyraw = "($AuthenticationMode):$($userName):$($password)” //官網這裡寫的還有問題,少了個$
$Bytes = [System.Text.Encoding]::UTF8.GetBytes($bodyraw)
$EncodedText =[Convert]::ToBase64String($Bytes)
$jsonbody = $EncodedText | ConvertTo-Json
$uriBase = 'http://<scomserver>/OperationsManager'
#Authenticate
$auth = Invoke-WebRequest -Method POST -Uri $uriBase/authenticate -Headers $scomheaders -body $jsonbody -UseDefaultCredentials -SessionVariable $websession
#Include CSRF Token
$csrfTocken = $websession.Cookies.GetCookies($uriBase) | ? { $_.Name -eq 'SCOM-CSRF-TOKEN' }
$scomHeaders.Add('SCOM-CSRF-TOKEN', [System.Web.HttpUtility]::UrlDecode($csrfTocken.Value))
#Examples to Invoke the required SCOM API
#Data/alertDescription/{alertid}
$alertid="6A88FBC1-0EDC-4173-9BB1-30FFEC296672"
$uri = "$uriBase/data/alertDetails"
$Response = Invoke-WebRequest -Uri "$uriBase/data/alertDetails/$alertid" -Headers $scomheaders -Method Get -WebSession $websession
$Response.Content | Convertto-json
#Criteria: Enter the displayname of the SCOM object
$Criteria = "DisplayName LIKE '%SQL%'"
#Convert our criteria to JSON format
$JSONBody = $Criteria | ConvertTo-Json
$Response = Invoke-WebRequest -Uri "$uriBase/data/class/monitors" -Method Post -Body $JSONBody -WebSession $WebSession
$Response.Content | Convertto-json
#Data Request in JSON Format
$dashboardbody='
{
"refreshing": false,
"Name": "TestAPIDashboard",
"path": "35b6eba3-6202-8738-a47f-16acf476230f"
}'
$response = Invoke-WebRequest -Uri "$uriBase/monitoring/dashboard" -Headers $scomheaders -Method POST -Body $dashboardbody -ContentType "application/json" -UseDefaultCredentials -WebSession $websession
流程就是:
-
第一步: 拿到username(用戶名) password(賬密碼) AuthenticationMode(認證模式,示例中是Network) domain(域) 四個配置信息
-
第二步: 將信息按照 AuthenticationMode:domain\username:password 拼接在一起然後通過base64加密:
"TmV0d29yazphYmNcYWRtaW51c2VyOlBXRDEyMw=="
-
第三步:調用認證介面 POST:
http://<Servername>/OperationsManager/authenticate
,將上一步的字元串放到請求體,Content-Type設置為application/json -
第四步:將響應頭的Set-Cookie裡面的SCOM-CSRF-TOKEN 設置到請求頭中,發起查詢告警列表請求
POST
http://<Servername>/OperationsManager/data/alert
示例很美好,但是一試問題就來了,
- 問題一: 認證介面訪問時一直401
剛開始以為是用戶名密碼或者許可權問題,聯繫確認了web頁面直接訪問 http://IP/OperationsManager/ 該賬號可以正常登錄查詢,有操作員許可權,百思不得其解,意外打開F12發現頁面上也是用的這個介面,於是決定抓包
打開fidder/wireshark,抓包認證介面發現居然發起了三次請求
查看response header
反應過來原來還有一層NTLM認證,於是調整java代碼,使用httpclient發起NTLM請求,核心代碼如下:
public static final String AUTH_URL = "/authenticate";
// 獲取NTLM認證client
public CloseableHttpClient getBasicAuthHttpClient(String username, String password, String workStation, String domain) {
CredentialsProvider provider = new BasicCredentialsProvider();
NTCredentials ntCredentials = new NTCredentials(username, password, workStation, domain);
provider.setCredentials(AuthScope.ANY, ntCredentials);
return HttpClients.custom().setDefaultCredentialsProvider(provider).useSystemProperties().build();
}
// 認證介面,獲取token
public String askForToken() {
String result = "";
HttpPost httppost = new HttpPost(baseUrl + AUTH_URL);
CloseableHttpClient basicAuthHttpClient;
try {
httppost.addHeader("Content-Type", "application/json;charset=UTF-8");
BASE64Encoder base64Encoder = new BASE64Encoder();
String encodedText = base64Encoder.encode((authMode + ":" + domain + "\\" + username + ":" + password).getBytes(StandardCharsets.UTF_8));
httppost.setEntity(new StringEntity("\""+encodedText+"\"", DEFAULT_CHARSET));
basicAuthHttpClient = getBasicAuthHttpClient(username, password, authMode, domain);
HttpResponse response = basicAuthHttpClient.execute(httppost);
Header[] headers = response.getHeaders("Set-Cookie");
for (Header r : headers) {
String value = r.getValue();
if (value.startsWith("SCOM-CSRF-TOKEN")) {
return value.split(";")[0].split("=")[1] ;
}
}
if (log.isDebugEnabled()) {
log.debug("Request url:{},Request Parameter:{},Response:{}", baseUrl + AUTH_URL, encodedText, result);
}
} catch (Exception e) {
throw new SystemException(e);
}
return result;
}
非常順利,成功拿到token,然後遭遇了新的問題
-
問題二 :查詢告警介面依然401
查詢告警http://ip/OperationsManager/data/alert,直接拿上一步獲取的token加入請求Header中,
SCOM-CSRF-TOKEN: yjihbfEsRFLd9fMRgT_Rxf4MzDiact3jgvyXZBrMnQRA4MoKtaO8_si891ahn6Pm98SJltLoiQYQrEENBWhJXX5WkQbLa2hqI6gVG96Hj0Y1%3a5dV5XX9rOLp5850DHjRsJ93ioKTP_Fw2AakOi1QN35SI_Vr0nBYrw4n8bUzh_vbNLxKIek_7w9lHSlftqOA0TfKs6Rs0oU7O3w8GIegHvpPtYt_0fTgktvzq4nL7MTxF0
過濾條件官網給的非常模糊沒有解釋,這裡直接跟scom控制台保持一致,請求體:
{"classId":null,"objectIds":{},"criteria":"((Severity = '0') OR (Severity = '1') OR (Severity = '2')) AND ((Priority = '2') OR (Priority = '1') OR (Priority = '0')) AND ((ResolutionState != '255')) AND TimeRaised >= 'Sun, 30 Jan 2022 07:34:42 GMT'","displayColumns":["severity","monitoringobjectdisplayname","name","lastmodified","description","sitename","alertsource","netbiosdomainname"]}
依然抓包頁面請求,發現依然發起三個
所以NTLM每個請求都要用上一步的getBasicAuthHttpClient()返回的client來發起請求
- 問題三: 報錯 索引超出了數組界限
排查發現是header中的SCOM-CSRF-TOKEN需要先URLDecoder一下,不然會解析報錯。
SCOM-CSRF-TOKEN: yjihbfEsRFLd9fMRgT_Rxf4MzDiact3jgvyXZBrMnQRA4MoKtaO8_si891ahn6Pm98SJltLoiQYQrEENBWhJXX5WkQbLa2hqI6gVG96Hj0Y1:5dV5XX9rOLp5850DHjRsJ93ioKTP_Fw2AakOi1QN35SI_Vr0nBYrw4n8bUzh_vbNLxKIek_7w9lHSlftqOA0TfKs6Rs0oU7O3w8GIegHvpPtYt_0fTgktvzq4nL7MTxF0
- 問題四:提示會話已過期
到這裡我們已經接近成功了,原因是因為光設置token到Header還不夠,還要請求的時候還需要攜帶Cookie,把第一步認證介面返回的Set-Cookie裡面的值設置到Cookie中,
httppost.addHeader("Content-Type", "application/json;charset=UTF-8");
httppost.addHeader("SCOM-CSRF-TOKEN", token);//這個token需要decode
httppost.addHeader("Cookie", "SCOM-CSRF-TOKEN="+originToken+";SCOMSessionId="+sessionId+";SCOMUserStatus=loggedIn"); //這個token不需要decode
響應格式:
{
"tableColumns": [
{
"field": "ageinmilliseconds",
"header": "",
"type": null,
"hidden": true
},
{
"field": "severity",
"header": "Severity",
"type": null,
"hidden": false
},
{
"field": "monitoringobjectdisplayname",
"header": "Source",
"type": null,
"hidden": false
},
{
"field": "monitoringobjectpath",
"header": "Path",
"type": null,
"hidden": false
},
{
"field": "name",
"header": "Name",
"type": null,
"hidden": false
},
{
"field": "age",
"header": "Age",
"type": null,
"hidden": false
},
{
"field": "description",
"header": "Description",
"type": null,
"hidden": false
},
{
"field": "owner",
"header": "Owner",
"type": null,
"hidden": false
},
{
"field": "timeadded",
"header": "Created",
"type": null,
"hidden": false
},
{
"field": "id",
"header": "Id",
"type": null,
"hidden": true
}
],
"rows": [
{
"id": "d3144ac9-4316-45ce-94b9-f50936219e24",
"severity": "Error",
"monitoringobjectdisplayname": "Test Service Group",
"monitoringobjectpath": null,
"name": null,
"age": "6137 days, 6 hours",
"ageinmilliseconds": 530260033217.2775,
"description": "",
"owner": "DXPSQYVCYBXGIUDZJZVWNLFDYEKA",
"timeadded": "2022-05-17T03:29:36.7330000Z"
},
{
"id": "d3144ac9-4316-45ce-94b9-f50936219e24",
"severity": "Error",
"monitoringobjectdisplayname": "Test Service Group",
"monitoringobjectpath": null,
"name": null,
"age": "6137 days, 6 hours",
"ageinmilliseconds": 530260149093.94025,
"description": "",
"owner": "DXPSQYVCYBXGIUDZJZVWNLFDYEKA",
"timeadded": "2022-05-17T03:29:36.7330000Z"
}
]
}
解析告警,做屬性轉換:
alarmId->告警唯一ID
netbioscomputername->電腦名(可能為空)
monitoringobjectdisplayname->源
description->告警描述
lastmodified->上次更新時間
principalname->站點
severity->告警級別
步驟總結
1.獲取認證信息,確認認證方式是否開啟NTLM
2.發起認證NTLM請求,從response header獲取SCOM-CSRF-TOKEN,以及SCOMSessionId
3.發起查詢告警請求,將上一步的token urldecode後設置到request header,將原始token和sessionId設置到Cookie