亚洲国产精品无码久久大片,亚洲AV无码乱码麻豆精品国产,亚洲品质自拍网站,少妇伦子伦精品无码STYLES,国产精久久久久久久

文章采集調用

文章采集調用

【爆款文章】集成服務(wù)與RPA的強強聯(lián)袂,速來(lái)圍觀(guān)!

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 93 次瀏覽 ? 2022-08-17 14:29 ? 來(lái)自相關(guān)話(huà)題

  【爆款文章】集成服務(wù)與RPA的強強聯(lián)袂,速來(lái)圍觀(guān)!
  金蝶云·蒼穹V5.0新特性系列文章持續更新
  每工作日早上07:45準時(shí)推送
  為你帶來(lái)最新產(chǎn)品資訊
  老師,業(yè)務(wù)上有個(gè)需求是把某異構系統的采購訂單集成到星瀚系統中,可對方系統不提供接口,沒(méi)辦法獲取數據啊。
  我的想法是用RPA機器人自動(dòng)登錄該異構系統獲取采購訂單信息,保存到Excel文件里面,然后再到集成服務(wù)云使用離線(xiàn)導入功能手工上傳文件,設置好字段映射關(guān)系之后加載到星瀚當中。
  但手工設置費時(shí)費力,有沒(méi)有辦法讓這個(gè)流程自動(dòng)化完成而不需要人工參與呢?
  開(kāi)發(fā)小白
  集成專(zhuān)家
  很簡(jiǎn)單,服務(wù)流程最新發(fā)布特性支持配置RPA節點(diǎn)。只需稍微改造RPA機器人,讓他多做一點(diǎn)事情(稍后揭曉),再把RPA組裝到服務(wù)流程中即可實(shí)現RPA集成。
  通過(guò)該方案,只需執行一個(gè)服務(wù)流程就能完成數據的獲取、轉換和加載步驟了~
  聽(tīng)起來(lái)很棒,迫不及待想請老師指教了呢。
  開(kāi)發(fā)小白
  集成專(zhuān)家
  那就讓我們趕緊開(kāi)始吧!
  適用版本
  該功能適用版本為金蝶云·蒼穹V4.0.016及以上。
  特性展示
  RPA是通過(guò)模擬用戶(hù)界面操作的方式來(lái)完成自動(dòng)化的一項技術(shù)。通過(guò)配置集成服務(wù)云服務(wù)流程的RPA節點(diǎn),可以在集成服務(wù)中嵌入RPA流程,將集成云從各個(gè)系統獲取到的數據信息作用于更廣泛的業(yè)務(wù)場(chǎng)景。
  接下來(lái),小編將以某異構系統的采購訂單集成到星瀚采購訂單為例,為大家介紹服務(wù)流程如何實(shí)現RPA集成。
  
  01 整體思路
  通過(guò)RPA模擬前臺操作,登錄異構系統,打開(kāi)采購訂單,獲取采購訂單數據并上傳Excel文件到FTP服務(wù)器中。
  然后,集成服務(wù)云通過(guò)服務(wù)編排組裝該RPA節點(diǎn)后,獲取相應數據,其他節點(diǎn)進(jìn)行數據的轉換、映射和加載,完成后將執行結果通過(guò)通知節點(diǎn)短信通知到用戶(hù)。
  服務(wù)流程集成RPA案例示例
  02實(shí)現步驟
  步驟一:RPA控制臺參數配置
  使用該功能的前提是【系統服務(wù)云】→【配置工具】→【系統參數】中配置了RPA控制臺的參數信息。如下圖所示:
  注:沒(méi)有此參數請聯(lián)系RPA部門(mén)提供。
  RPA控制臺系統集成參數
  步驟二:用戶(hù)授權與查看
  配置服務(wù)流程的用戶(hù)需要授權RPA通用角色。如下圖所示:
  RPA通用角色
  完成角色授權后,登錄RPA控制臺即可查看當前用戶(hù)可使用的RPA流程。如下圖所示:
  查看RPA流程
  步驟三:設計RPA流程
  RPA流程中,通過(guò)登錄異構系統、打開(kāi)采購訂單、獲取采購訂單數據和保存數據并上傳Excel文件四個(gè)子流程完成異構系統的采購訂單信息的數據獲取。
 ?。ㄗⅲ捍税咐鶕枨髨?chǎng)景改造RPA流程,增加上傳數據文件到FTP服務(wù)器子流程,非必選改造。)
  RPA主流程 - 登錄/打開(kāi)采購訂單/獲取數據/上傳
  各子流程配置詳情如下圖所示:
  
  左右滑動(dòng)查看更多>>(從左至右依次為登錄異構系統、打開(kāi)采購訂單、獲取采購訂單數據、保存并上傳Excel文件)
  步驟四:配置服務(wù)流程
  創(chuàng )建服務(wù)流程后,在流程圖中配置RPA節點(diǎn),選擇RPA流程和執行流程的RPA機器人。如下圖所示:
  服務(wù)流程中嵌入RPA節點(diǎn)
  RPA節點(diǎn)詳情
  步驟五:運行監控
  雙擊流程實(shí)例中的RPA節點(diǎn),可查看RPA任務(wù)執行詳情,如下圖所示:
  服務(wù)流程實(shí)例與RPA節點(diǎn)
  RPA節點(diǎn)執行詳情
  亮點(diǎn)價(jià)值
  亮點(diǎn)一:集成服務(wù)流程和RPA強強聯(lián)合
  打通集成服務(wù)云與RPA控制臺,輕松實(shí)現異構系統的集成與標準化流程的執行、多渠道采集、加工與處理數據,提高工作效率,助力企業(yè)業(yè)務(wù)集成自動(dòng)化。
  亮點(diǎn)二:流程配置簡(jiǎn)單,執行過(guò)程可追溯
  服務(wù)流程中可以直接配置一個(gè)或多個(gè)RPA節點(diǎn),通過(guò)用戶(hù)獲取RPA流程與RPA機器人信息,在服務(wù)流程執行時(shí)自動(dòng)觸發(fā)RPA機器人的執行,并且支持在流程實(shí)例中查看RPA任務(wù)詳情、RPA錄屏和日志信息等。
  亮點(diǎn)三:RPA流程的業(yè)務(wù)角色更直觀(guān)
  服務(wù)流程即業(yè)務(wù)集成流程,RPA節點(diǎn)作為其中一部分,可以直觀(guān)地看到RPA如何在整條業(yè)務(wù)鏈中發(fā)揮作用。
  相關(guān)鏈接
  關(guān)于RPA集成的詳細內容可參考下方鏈接:
  RPA集成指南V1.0:
  RPA節點(diǎn)錄入幣別到異構系統示例:
  服務(wù)流程調用RPA視頻:
  劃重點(diǎn)
  1) 集成服務(wù)云提供了RPA集成功能。通過(guò)該功能,集成服務(wù)流程中可組裝RPA機器人,在集成業(yè)務(wù)流程中實(shí)現標準化流程的自動(dòng)執行。
  2) 集成服務(wù)云的RPA集成具備以下特點(diǎn): 查看全部

  【爆款文章】集成服務(wù)與RPA的強強聯(lián)袂,速來(lái)圍觀(guān)!
  金蝶云·蒼穹V5.0新特性系列文章持續更新
  每工作日早上07:45準時(shí)推送
  為你帶來(lái)最新產(chǎn)品資訊
  老師,業(yè)務(wù)上有個(gè)需求是把某異構系統的采購訂單集成到星瀚系統中,可對方系統不提供接口,沒(méi)辦法獲取數據啊。
  我的想法是用RPA機器人自動(dòng)登錄該異構系統獲取采購訂單信息,保存到Excel文件里面,然后再到集成服務(wù)云使用離線(xiàn)導入功能手工上傳文件,設置好字段映射關(guān)系之后加載到星瀚當中。
  但手工設置費時(shí)費力,有沒(méi)有辦法讓這個(gè)流程自動(dòng)化完成而不需要人工參與呢?
  開(kāi)發(fā)小白
  集成專(zhuān)家
  很簡(jiǎn)單,服務(wù)流程最新發(fā)布特性支持配置RPA節點(diǎn)。只需稍微改造RPA機器人,讓他多做一點(diǎn)事情(稍后揭曉),再把RPA組裝到服務(wù)流程中即可實(shí)現RPA集成。
  通過(guò)該方案,只需執行一個(gè)服務(wù)流程就能完成數據的獲取、轉換和加載步驟了~
  聽(tīng)起來(lái)很棒,迫不及待想請老師指教了呢。
  開(kāi)發(fā)小白
  集成專(zhuān)家
  那就讓我們趕緊開(kāi)始吧!
  適用版本
  該功能適用版本為金蝶云·蒼穹V4.0.016及以上。
  特性展示
  RPA是通過(guò)模擬用戶(hù)界面操作的方式來(lái)完成自動(dòng)化的一項技術(shù)。通過(guò)配置集成服務(wù)云服務(wù)流程的RPA節點(diǎn),可以在集成服務(wù)中嵌入RPA流程,將集成云從各個(gè)系統獲取到的數據信息作用于更廣泛的業(yè)務(wù)場(chǎng)景。
  接下來(lái),小編將以某異構系統的采購訂單集成到星瀚采購訂單為例,為大家介紹服務(wù)流程如何實(shí)現RPA集成。
  
  01 整體思路
  通過(guò)RPA模擬前臺操作,登錄異構系統,打開(kāi)采購訂單,獲取采購訂單數據并上傳Excel文件到FTP服務(wù)器中。
  然后,集成服務(wù)云通過(guò)服務(wù)編排組裝該RPA節點(diǎn)后,獲取相應數據,其他節點(diǎn)進(jìn)行數據的轉換、映射和加載,完成后將執行結果通過(guò)通知節點(diǎn)短信通知到用戶(hù)。
  服務(wù)流程集成RPA案例示例
  02實(shí)現步驟
  步驟一:RPA控制臺參數配置
  使用該功能的前提是【系統服務(wù)云】→【配置工具】→【系統參數】中配置了RPA控制臺的參數信息。如下圖所示:
  注:沒(méi)有此參數請聯(lián)系RPA部門(mén)提供。
  RPA控制臺系統集成參數
  步驟二:用戶(hù)授權與查看
  配置服務(wù)流程的用戶(hù)需要授權RPA通用角色。如下圖所示:
  RPA通用角色
  完成角色授權后,登錄RPA控制臺即可查看當前用戶(hù)可使用的RPA流程。如下圖所示:
  查看RPA流程
  步驟三:設計RPA流程
  RPA流程中,通過(guò)登錄異構系統、打開(kāi)采購訂單、獲取采購訂單數據和保存數據并上傳Excel文件四個(gè)子流程完成異構系統的采購訂單信息的數據獲取。
 ?。ㄗⅲ捍税咐鶕枨髨?chǎng)景改造RPA流程,增加上傳數據文件到FTP服務(wù)器子流程,非必選改造。)
  RPA主流程 - 登錄/打開(kāi)采購訂單/獲取數據/上傳
  各子流程配置詳情如下圖所示:
  
  左右滑動(dòng)查看更多>>(從左至右依次為登錄異構系統、打開(kāi)采購訂單、獲取采購訂單數據、保存并上傳Excel文件)
  步驟四:配置服務(wù)流程
  創(chuàng )建服務(wù)流程后,在流程圖中配置RPA節點(diǎn),選擇RPA流程和執行流程的RPA機器人。如下圖所示:
  服務(wù)流程中嵌入RPA節點(diǎn)
  RPA節點(diǎn)詳情
  步驟五:運行監控
  雙擊流程實(shí)例中的RPA節點(diǎn),可查看RPA任務(wù)執行詳情,如下圖所示:
  服務(wù)流程實(shí)例與RPA節點(diǎn)
  RPA節點(diǎn)執行詳情
  亮點(diǎn)價(jià)值
  亮點(diǎn)一:集成服務(wù)流程和RPA強強聯(lián)合
  打通集成服務(wù)云與RPA控制臺,輕松實(shí)現異構系統的集成與標準化流程的執行、多渠道采集、加工與處理數據,提高工作效率,助力企業(yè)業(yè)務(wù)集成自動(dòng)化。
  亮點(diǎn)二:流程配置簡(jiǎn)單,執行過(guò)程可追溯
  服務(wù)流程中可以直接配置一個(gè)或多個(gè)RPA節點(diǎn),通過(guò)用戶(hù)獲取RPA流程與RPA機器人信息,在服務(wù)流程執行時(shí)自動(dòng)觸發(fā)RPA機器人的執行,并且支持在流程實(shí)例中查看RPA任務(wù)詳情、RPA錄屏和日志信息等。
  亮點(diǎn)三:RPA流程的業(yè)務(wù)角色更直觀(guān)
  服務(wù)流程即業(yè)務(wù)集成流程,RPA節點(diǎn)作為其中一部分,可以直觀(guān)地看到RPA如何在整條業(yè)務(wù)鏈中發(fā)揮作用。
  相關(guān)鏈接
  關(guān)于RPA集成的詳細內容可參考下方鏈接:
  RPA集成指南V1.0:
  RPA節點(diǎn)錄入幣別到異構系統示例:
  服務(wù)流程調用RPA視頻:
  劃重點(diǎn)
  1) 集成服務(wù)云提供了RPA集成功能。通過(guò)該功能,集成服務(wù)流程中可組裝RPA機器人,在集成業(yè)務(wù)流程中實(shí)現標準化流程的自動(dòng)執行。
  2) 集成服務(wù)云的RPA集成具備以下特點(diǎn):

紅隊從資產(chǎn)收集到打點(diǎn)

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 97 次瀏覽 ? 2022-08-12 04:28 ? 來(lái)自相關(guān)話(huà)題

  紅隊從資產(chǎn)收集到打點(diǎn)
  最近想總結一下,在紅隊滲透拿到一個(gè)目標名或者刷src時(shí)候,怎么快速信息收集和批量檢測來(lái)打到一個(gè)點(diǎn),往往在實(shí)際項目中就是拼手速。
  信息收集到打點(diǎn)大致我就分為
  企業(yè)信息結構收集敏感信息收集域名主動(dòng)被動(dòng)收集整理域名ip資產(chǎn)掃描檢測打點(diǎn)
  其中每一步需要收集好幾個(gè)方面的信息,手動(dòng)很累也很慢
  1.企業(yè)信息結構收集
  企業(yè)信息結構收集包括對查詢(xún)目標企業(yè)的公司信息,涉及到哪些主站域名,有哪些控股很多的子公司,這些子公司涉及到哪些域名,然后再進(jìn)行備案反查,你又會(huì )得到一些新的公司,同理也能再次得到一些新的主站域名,將這些進(jìn)行整理---->得到一批待爆破的域名。
  還有的就是除了這些查到的主站域名,往往企業(yè)會(huì )有app、公眾號、小程序這些資產(chǎn),也要對這些資產(chǎn)進(jìn)行收集,然后你又拿到了一批域名。
  手動(dòng)查詢(xún)的話(huà)從以下查詢(xún)
  天眼查 查企業(yè)/子公司/域名/公眾號 https://www.tianyancha.com/愛(ài)企查 https://aiqicha.baidu.com/企查查詢(xún) https://www.qcc.com/啟信寶 https://www.qixin.com/
  工具:
  推薦cSubsidiary利用天眼查查詢(xún)企業(yè)子公司https://github.com/canc3s/cSubsidiary<br />還有pigat:https://github.com/teamssix/pigat<br />公眾號和app的收集:https://github.com/wgpsec/ENSc ... an_GO go版本
  2.敏感信息收集
  利用搜索引擎、github等托管平臺配合一些dorks就可以搜到很多信息。
  熟知的googlehack,gitdork,網(wǎng)盤(pán)泄露等等。
  敏感信息一共要搜集這個(gè)幾個(gè)方面:
  googlehack語(yǔ)法github泄露目標人員姓名/手機/郵箱
  1.googlehack
  但比如googlehack,你需要搜的好幾條語(yǔ)法加上域名
  比如:
  site:*.domain.cominurl:domain.comintitle:keywordkeyword?filetyle:doc|pdf
  一個(gè)域名可以配合多個(gè)語(yǔ)法搜,那么多域名手動(dòng)輸入搜很慢,推薦工具:
  https://github.com/r00tSe7en/GoogleHackingTool 在線(xiàn)Google Hacking 小工具https://www.exploit-db.com/google-hacking-database 語(yǔ)法,自己可以腳本里批量搜
  2.github泄露敏感信息:
  一些常用github dorks,直接搜對應目標信息:
  xxxxx.com "Authorization" #"Authorization: Bearer"xxxxx.com "filename:vim_settings.xml"xxxxx.com "language:PHP"
  也可以在github對各種信息搜索,比如文件類(lèi)型
  filename:manifest.xmlfilename:travis.ymlfilename:vim_settings.xmlfilename:databasefilename:prod.exs NOT prod.secret.exsfilename:prod.secret.exsfilename:.npmrc _authfilename:.dockercfg authfilename:WebServers.xmlfilename:.bash_history filename:sftp-config.jsonfilename:sftp.json path:.vscodefilename:secrets.yml passwordfilename:.esmtprc passwordfilename:passwd path:etcfilename:dbeaver-data-sources.xmlpath:sites databases passwordfilename:config.php dbpasswdfilename:prod.secret.exsfilename:configuration.php JConfig passwordfilename:.sh_history
  包含關(guān)鍵字的指定語(yǔ)言:
  language:python usernamelanguage:php usernamelanguage:sql usernamelanguage:html passwordlanguage:perl passwordlanguage:shell usernamelanguage:java apiHOMEBREW_GITHUB_API_TOKEN language:shell
  搜API/KEYS/TOEKNS關(guān)鍵字:
  api_key“api keys”authorization_bearer:oauthauthauthenticationclient_secretapi_token:“api token”client_idpassworduser_passworduser_passpasscodeclient_secretsecretpassword hashOTPuser auth
  很多關(guān)鍵字可以搜,還是批量搜高效,工具:
  https://github.com/obheda12/Gi ... dorks
  這類(lèi)工具需要設置git令牌,附上gitrob過(guò)程,踩坑:不要下relase ,自己編譯最好:
  git clone https://github.com/michenriksen/gitrob.gitgo mod init #to use go mod 如果報錯 運行g(shù)o mod init github.com/michenriksen/gitrobrm Gopkg* #remove the old stuffgo build #to build it<br /><br />./build.sh
  設置git令牌
  set GITROB_ACCESS_TOKEN=xxxxx
  使用后可以查看圖形界面的結果:
  
  3. 目標人員姓名/手機/郵箱
  通過(guò)開(kāi)源信息收集目標人員姓名/手機/郵箱,為后面打點(diǎn)做字典做準備。
  https://github.com/laramies/theHarvester
  通過(guò)搜索引擎、PGP服務(wù)器以及SHODAN數據庫收集用戶(hù)的email,子域名,主機,雇員名,開(kāi)放端口和banner信息。
  使用:
  -d 開(kāi)關(guān)用于定義域名,-l 用于限制結果數量
  theHarvester -d kali.org -l 200 -banubis,baidu,pentesttools,projectdiscovery,qwant,rapiddns,rocketreach,securityTrails,spyse,sublist3r,threatcrowd,threatminer,trello,twitter,urlscan,virustotal,yahoo,zoomeye,bing,binaryedge,bingapi,bufferoverun,censys,certspotter,crtsh,dnsdumpster,duckduckgo,fullhunt,github-code,google,hackertarget,hunter,intelx,linkedin,linkedin_links,n45ht,omnisint,otx
  按github跑就是了,但是有點(diǎn)坑點(diǎn):
  配置api-keys在/etc/theHarvester 目錄下api-keys.yaml填入對應的api key即可
  有個(gè)坑點(diǎn)是key:后要加個(gè)空格在放key字符串,不然跑不起來(lái)
  人員郵箱字典的構造:
  https://github.com/pry0cc/Goog ... ed.rb
  還可以使用一些社工信息來(lái)做字典,這樣的工具很多了,用一個(gè)就夠了沒(méi)必要用全部:Cupp/Cewl
  https://github.com/r3nt0n/bopscrkpython3 bopscrk.py -i
  3. 域名主動(dòng)被動(dòng)收集
  域名主動(dòng)信息收集內容就有點(diǎn)雜了。
  通過(guò)1、2點(diǎn)我們拿到了一批等待爆破的域名和人員的信息,以及泄露的一些敏感信息(運氣好的話(huà)用泄露的信息已經(jīng)打到點(diǎn)了。)
  現在需要對域名進(jìn)行whois信息查詢(xún)、dns域名正反查詢(xún)、子域名探測爆破三個(gè)方面收集。
  1.whois信息查詢(xún)
  whois需要查詢(xún)域名的whois,然后根據whois信息來(lái)查詢(xún)歷史和反查,這樣你就得到了一些郵箱和可疑域名。
  查域名信息沒(méi)什么說(shuō)的,主要看網(wǎng)址注冊人、到期記錄、創(chuàng )建域的時(shí)間、名稱(chēng)服務(wù)器和聯(lián)系信息等,查最新的一般都是托管的信息,而查看歷史信息就有可能查到真實(shí)聯(lián)系人郵箱電話(huà)等:
  一些常見(jiàn)whois查詢(xún),手動(dòng)的時(shí)候可以查詢(xún):
  https://domaineye.com/reverse- ... whois
  除了正向查詢(xún)whois,還要查詢(xún)whois歷史信息:
  以下幾個(gè)網(wǎng)站允許用戶(hù)訪(fǎng)問(wèn)連接的 WHOIS 數據庫以進(jìn)行調查。這些記錄是十多年來(lái)對有關(guān)域注冊的有用數據進(jìn)行網(wǎng)絡(luò )爬取的結果:
  https://whois.domaintools.com/ ... .com/
  whois歷史信息查詢(xún)不能錯過(guò),明顯可以在whois歷史信息中看真實(shí)郵箱并反查而不是目前托管的郵箱,以及非托管的dns服務(wù)器:
  whois 信息反查
  通過(guò)歷史whois信息找到真實(shí)郵箱or組織名,再反查域名,又可以得到一批資產(chǎn):
  other:
  https://www.reversewhois.io/
  整理一下whois分了三步,先whois查詢(xún)一個(gè)域名,然后對查詢(xún)的信息進(jìn)行歷史whois查詢(xún)和反查,最后得到一批郵箱和域名。手動(dòng)知道過(guò)程就行,實(shí)際做項目用工具批量查了整理:
  https://github.com/xugj-gits/domain-tool 批量whois查詢(xún)https://github.com/melbadry9/WhoEnum
  
  2.dns域名正向反向查詢(xún)
  dns域名查詢(xún)分兩個(gè)部分,歷史記錄和ip反查:
  DNS歷史記錄(doamin2ips)
  Dnsdumpster 是一個(gè)在線(xiàn)實(shí)用程序,我們使用它來(lái)查找子域、目標的 DNS 記錄。
  VT也是可以看dns數據信息的:
  ip反查(ip2domains)
  同ip查詢(xún)多個(gè)解析到這個(gè)ip的域名,尋找更多web資產(chǎn)
  https://viewdns.info/reverseip/
  https://dnslytics.com/
  ip反查也可以使用dig、nslookup、host命令完成:
  工具推薦:
  https://www.infobyip.com/ipbulklookup.php 批量ip反查https://github.com/Sma11New/ip2domain 國內域名推薦ip2domain,會(huì )查詢(xún)權重、ICP備案等
  通過(guò)dns查詢(xún),我們拿到了一些域名和可疑ip段
  3.子域名探測爆破
  沒(méi)啥好說(shuō)的,主要是收集的渠道全、過(guò)濾泛解析。
  常見(jiàn)手法爆破子域名、證書(shū)透明度、搜索引擎、信息泄露、ASN號等等,很多工具已經(jīng)做了這些工作
  https://github.com/shmilylty/O ... redns
  4. 整理域名ip資產(chǎn)
  到這里大致的收集就結束了,就是要對收集結果進(jìn)行整理,通過(guò)上面收集能拿到:
  一批待探測存活的域名一批待確定的ip段一些郵箱,姓名,手機號一些敏感文件、信息、通用密碼(敏感信息收集階段看臉)
  整理后大致如上,有一步需要做的就是把收集的這些域名,轉成ip段,但是是需要判斷這個(gè)ip屬不屬于cdn,屬不屬于泛解析的ip,然后轉成ip后要判斷ip段的權重,哪些段才可能是目標主要的C段。
  https://github.com/EdgeSecurityTeam/Eeyes 對subdomain數據處理、獲取其中真實(shí)IP并整理成c段https://github.com/canc3s/cIPR 整理后查看權重
  5. 掃描檢測打點(diǎn)
  這步就開(kāi)始快速打點(diǎn)了。
  上面整理后的資產(chǎn),需要我們探測的是一批域名和一批C段
  域名需要做的事:
  探測存活title、banner提取、指紋識別爬蟲(chóng)、目錄輕量掃描、輕量漏掃
  C段需要做的事:
  掃描端口,探測存活將掃的web和非web進(jìn)行分類(lèi),把掃到的web資產(chǎn)加入到域名需要做的事,和對待域名沒(méi)區別將掃到的非web(數據庫/遠程登錄協(xié)議)進(jìn)行爆破,比如mysql爆破,rdp爆破
  一批域名和一批C段就這樣做不同的事,來(lái)先探測是否有脆弱的點(diǎn),最后才是回歸常規web,一個(gè)站一個(gè)站的去撕
  一些工具:
  https://github.com/broken5/WebAliveScan web存活判斷https://github.com/fadinglr/EHole 紅隊重點(diǎn)攻擊系統指紋探測工具https://github.com/k8gege/K8CScan 漏洞掃描、密碼爆破https://github.com/b1gcat/DarkEye 主機發(fā)現+爆破https://github.com/Adminisme/ServerScan 高并發(fā)網(wǎng)絡(luò )掃描、服務(wù)探測工具https://github.com/dean2021/titlesearch 批量抓取域名title工具https://github.com/pmiaowu/PmWebDirScan 批量掃目錄備份
  還有的就是一些大家都熟知的xray,vulmap之類(lèi)的漏洞,批量輕量去掃描一下即可。
  把上面的幾個(gè)步驟,工具串起來(lái),行成快速信息收集,快速探測打點(diǎn),最好寫(xiě)個(gè)貫穿流程的工具調用的腳本,自己寫(xiě)過(guò)效果不錯但代碼不好就不拿出來(lái)丟人了,基本這樣過(guò)一遍就容易打到一些比較脆弱的點(diǎn)。 查看全部

  紅隊從資產(chǎn)收集到打點(diǎn)
  最近想總結一下,在紅隊滲透拿到一個(gè)目標名或者刷src時(shí)候,怎么快速信息收集和批量檢測來(lái)打到一個(gè)點(diǎn),往往在實(shí)際項目中就是拼手速。
  信息收集到打點(diǎn)大致我就分為
  企業(yè)信息結構收集敏感信息收集域名主動(dòng)被動(dòng)收集整理域名ip資產(chǎn)掃描檢測打點(diǎn)
  其中每一步需要收集好幾個(gè)方面的信息,手動(dòng)很累也很慢
  1.企業(yè)信息結構收集
  企業(yè)信息結構收集包括對查詢(xún)目標企業(yè)的公司信息,涉及到哪些主站域名,有哪些控股很多的子公司,這些子公司涉及到哪些域名,然后再進(jìn)行備案反查,你又會(huì )得到一些新的公司,同理也能再次得到一些新的主站域名,將這些進(jìn)行整理---->得到一批待爆破的域名。
  還有的就是除了這些查到的主站域名,往往企業(yè)會(huì )有app、公眾號、小程序這些資產(chǎn),也要對這些資產(chǎn)進(jìn)行收集,然后你又拿到了一批域名。
  手動(dòng)查詢(xún)的話(huà)從以下查詢(xún)
  天眼查 查企業(yè)/子公司/域名/公眾號 https://www.tianyancha.com/愛(ài)企查 https://aiqicha.baidu.com/企查查詢(xún) https://www.qcc.com/啟信寶 https://www.qixin.com/
  工具:
  推薦cSubsidiary利用天眼查查詢(xún)企業(yè)子公司https://github.com/canc3s/cSubsidiary<br />還有pigat:https://github.com/teamssix/pigat<br />公眾號和app的收集:https://github.com/wgpsec/ENSc ... an_GO go版本
  2.敏感信息收集
  利用搜索引擎、github等托管平臺配合一些dorks就可以搜到很多信息。
  熟知的googlehack,gitdork,網(wǎng)盤(pán)泄露等等。
  敏感信息一共要搜集這個(gè)幾個(gè)方面:
  googlehack語(yǔ)法github泄露目標人員姓名/手機/郵箱
  1.googlehack
  但比如googlehack,你需要搜的好幾條語(yǔ)法加上域名
  比如:
  site:*.domain.cominurl:domain.comintitle:keywordkeyword?filetyle:doc|pdf
  一個(gè)域名可以配合多個(gè)語(yǔ)法搜,那么多域名手動(dòng)輸入搜很慢,推薦工具:
  https://github.com/r00tSe7en/GoogleHackingTool 在線(xiàn)Google Hacking 小工具https://www.exploit-db.com/google-hacking-database 語(yǔ)法,自己可以腳本里批量搜
  2.github泄露敏感信息:
  一些常用github dorks,直接搜對應目標信息:
  xxxxx.com "Authorization" #"Authorization: Bearer"xxxxx.com "filename:vim_settings.xml"xxxxx.com "language:PHP"
  也可以在github對各種信息搜索,比如文件類(lèi)型
  filename:manifest.xmlfilename:travis.ymlfilename:vim_settings.xmlfilename:databasefilename:prod.exs NOT prod.secret.exsfilename:prod.secret.exsfilename:.npmrc _authfilename:.dockercfg authfilename:WebServers.xmlfilename:.bash_history filename:sftp-config.jsonfilename:sftp.json path:.vscodefilename:secrets.yml passwordfilename:.esmtprc passwordfilename:passwd path:etcfilename:dbeaver-data-sources.xmlpath:sites databases passwordfilename:config.php dbpasswdfilename:prod.secret.exsfilename:configuration.php JConfig passwordfilename:.sh_history
  包含關(guān)鍵字的指定語(yǔ)言:
  language:python usernamelanguage:php usernamelanguage:sql usernamelanguage:html passwordlanguage:perl passwordlanguage:shell usernamelanguage:java apiHOMEBREW_GITHUB_API_TOKEN language:shell
  搜API/KEYS/TOEKNS關(guān)鍵字:
  api_key“api keys”authorization_bearer:oauthauthauthenticationclient_secretapi_token:“api token”client_idpassworduser_passworduser_passpasscodeclient_secretsecretpassword hashOTPuser auth
  很多關(guān)鍵字可以搜,還是批量搜高效,工具:
  https://github.com/obheda12/Gi ... dorks
  這類(lèi)工具需要設置git令牌,附上gitrob過(guò)程,踩坑:不要下relase ,自己編譯最好:
  git clone https://github.com/michenriksen/gitrob.gitgo mod init #to use go mod 如果報錯 運行g(shù)o mod init github.com/michenriksen/gitrobrm Gopkg* #remove the old stuffgo build #to build it<br /><br />./build.sh
  設置git令牌
  set GITROB_ACCESS_TOKEN=xxxxx
  使用后可以查看圖形界面的結果:
  
  3. 目標人員姓名/手機/郵箱
  通過(guò)開(kāi)源信息收集目標人員姓名/手機/郵箱,為后面打點(diǎn)做字典做準備。
  https://github.com/laramies/theHarvester
  通過(guò)搜索引擎、PGP服務(wù)器以及SHODAN數據庫收集用戶(hù)的email,子域名,主機,雇員名,開(kāi)放端口和banner信息。
  使用:
  -d 開(kāi)關(guān)用于定義域名,-l 用于限制結果數量
  theHarvester -d kali.org -l 200 -banubis,baidu,pentesttools,projectdiscovery,qwant,rapiddns,rocketreach,securityTrails,spyse,sublist3r,threatcrowd,threatminer,trello,twitter,urlscan,virustotal,yahoo,zoomeye,bing,binaryedge,bingapi,bufferoverun,censys,certspotter,crtsh,dnsdumpster,duckduckgo,fullhunt,github-code,google,hackertarget,hunter,intelx,linkedin,linkedin_links,n45ht,omnisint,otx
  按github跑就是了,但是有點(diǎn)坑點(diǎn):
  配置api-keys在/etc/theHarvester 目錄下api-keys.yaml填入對應的api key即可
  有個(gè)坑點(diǎn)是key:后要加個(gè)空格在放key字符串,不然跑不起來(lái)
  人員郵箱字典的構造:
  https://github.com/pry0cc/Goog ... ed.rb
  還可以使用一些社工信息來(lái)做字典,這樣的工具很多了,用一個(gè)就夠了沒(méi)必要用全部:Cupp/Cewl
  https://github.com/r3nt0n/bopscrkpython3 bopscrk.py -i
  3. 域名主動(dòng)被動(dòng)收集
  域名主動(dòng)信息收集內容就有點(diǎn)雜了。
  通過(guò)1、2點(diǎn)我們拿到了一批等待爆破的域名和人員的信息,以及泄露的一些敏感信息(運氣好的話(huà)用泄露的信息已經(jīng)打到點(diǎn)了。)
  現在需要對域名進(jìn)行whois信息查詢(xún)、dns域名正反查詢(xún)、子域名探測爆破三個(gè)方面收集。
  1.whois信息查詢(xún)
  whois需要查詢(xún)域名的whois,然后根據whois信息來(lái)查詢(xún)歷史和反查,這樣你就得到了一些郵箱和可疑域名。
  查域名信息沒(méi)什么說(shuō)的,主要看網(wǎng)址注冊人、到期記錄、創(chuàng )建域的時(shí)間、名稱(chēng)服務(wù)器和聯(lián)系信息等,查最新的一般都是托管的信息,而查看歷史信息就有可能查到真實(shí)聯(lián)系人郵箱電話(huà)等:
  一些常見(jiàn)whois查詢(xún),手動(dòng)的時(shí)候可以查詢(xún):
  https://domaineye.com/reverse- ... whois
  除了正向查詢(xún)whois,還要查詢(xún)whois歷史信息:
  以下幾個(gè)網(wǎng)站允許用戶(hù)訪(fǎng)問(wèn)連接的 WHOIS 數據庫以進(jìn)行調查。這些記錄是十多年來(lái)對有關(guān)域注冊的有用數據進(jìn)行網(wǎng)絡(luò )爬取的結果:
  https://whois.domaintools.com/ ... .com/
  whois歷史信息查詢(xún)不能錯過(guò),明顯可以在whois歷史信息中看真實(shí)郵箱并反查而不是目前托管的郵箱,以及非托管的dns服務(wù)器:
  whois 信息反查
  通過(guò)歷史whois信息找到真實(shí)郵箱or組織名,再反查域名,又可以得到一批資產(chǎn):
  other:
  https://www.reversewhois.io/
  整理一下whois分了三步,先whois查詢(xún)一個(gè)域名,然后對查詢(xún)的信息進(jìn)行歷史whois查詢(xún)和反查,最后得到一批郵箱和域名。手動(dòng)知道過(guò)程就行,實(shí)際做項目用工具批量查了整理:
  https://github.com/xugj-gits/domain-tool 批量whois查詢(xún)https://github.com/melbadry9/WhoEnum
  
  2.dns域名正向反向查詢(xún)
  dns域名查詢(xún)分兩個(gè)部分,歷史記錄和ip反查:
  DNS歷史記錄(doamin2ips)
  Dnsdumpster 是一個(gè)在線(xiàn)實(shí)用程序,我們使用它來(lái)查找子域、目標的 DNS 記錄。
  VT也是可以看dns數據信息的:
  ip反查(ip2domains)
  同ip查詢(xún)多個(gè)解析到這個(gè)ip的域名,尋找更多web資產(chǎn)
  https://viewdns.info/reverseip/
  https://dnslytics.com/
  ip反查也可以使用dig、nslookup、host命令完成:
  工具推薦:
  https://www.infobyip.com/ipbulklookup.php 批量ip反查https://github.com/Sma11New/ip2domain 國內域名推薦ip2domain,會(huì )查詢(xún)權重、ICP備案等
  通過(guò)dns查詢(xún),我們拿到了一些域名和可疑ip段
  3.子域名探測爆破
  沒(méi)啥好說(shuō)的,主要是收集的渠道全、過(guò)濾泛解析。
  常見(jiàn)手法爆破子域名、證書(shū)透明度、搜索引擎、信息泄露、ASN號等等,很多工具已經(jīng)做了這些工作
  https://github.com/shmilylty/O ... redns
  4. 整理域名ip資產(chǎn)
  到這里大致的收集就結束了,就是要對收集結果進(jìn)行整理,通過(guò)上面收集能拿到:
  一批待探測存活的域名一批待確定的ip段一些郵箱,姓名,手機號一些敏感文件、信息、通用密碼(敏感信息收集階段看臉)
  整理后大致如上,有一步需要做的就是把收集的這些域名,轉成ip段,但是是需要判斷這個(gè)ip屬不屬于cdn,屬不屬于泛解析的ip,然后轉成ip后要判斷ip段的權重,哪些段才可能是目標主要的C段。
  https://github.com/EdgeSecurityTeam/Eeyes 對subdomain數據處理、獲取其中真實(shí)IP并整理成c段https://github.com/canc3s/cIPR 整理后查看權重
  5. 掃描檢測打點(diǎn)
  這步就開(kāi)始快速打點(diǎn)了。
  上面整理后的資產(chǎn),需要我們探測的是一批域名和一批C段
  域名需要做的事:
  探測存活title、banner提取、指紋識別爬蟲(chóng)、目錄輕量掃描、輕量漏掃
  C段需要做的事:
  掃描端口,探測存活將掃的web和非web進(jìn)行分類(lèi),把掃到的web資產(chǎn)加入到域名需要做的事,和對待域名沒(méi)區別將掃到的非web(數據庫/遠程登錄協(xié)議)進(jìn)行爆破,比如mysql爆破,rdp爆破
  一批域名和一批C段就這樣做不同的事,來(lái)先探測是否有脆弱的點(diǎn),最后才是回歸常規web,一個(gè)站一個(gè)站的去撕
  一些工具:
  https://github.com/broken5/WebAliveScan web存活判斷https://github.com/fadinglr/EHole 紅隊重點(diǎn)攻擊系統指紋探測工具https://github.com/k8gege/K8CScan 漏洞掃描、密碼爆破https://github.com/b1gcat/DarkEye 主機發(fā)現+爆破https://github.com/Adminisme/ServerScan 高并發(fā)網(wǎng)絡(luò )掃描、服務(wù)探測工具https://github.com/dean2021/titlesearch 批量抓取域名title工具https://github.com/pmiaowu/PmWebDirScan 批量掃目錄備份
  還有的就是一些大家都熟知的xray,vulmap之類(lèi)的漏洞,批量輕量去掃描一下即可。
  把上面的幾個(gè)步驟,工具串起來(lái),行成快速信息收集,快速探測打點(diǎn),最好寫(xiě)個(gè)貫穿流程的工具調用的腳本,自己寫(xiě)過(guò)效果不錯但代碼不好就不拿出來(lái)丟人了,基本這樣過(guò)一遍就容易打到一些比較脆弱的點(diǎn)。

文章采集調用 【】lotlib

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 139 次瀏覽 ? 2022-08-04 09:09 ? 來(lái)自相關(guān)話(huà)題

  文章采集調用 【】lotlib
  文章采集調用本地瀏覽器特定鏈接,ip就用轉發(fā)地址好了。其他調用平臺,一般也是接口上獲取。各個(gè)視頻站點(diǎn)可能使用smi等格式數據來(lái)互相傳播,arctime支持?;ヂ?lián)網(wǎng)時(shí)代,隨著(zhù)網(wǎng)速增快,付費會(huì )員日益增多,付費視頻,付費文件所占比例在不斷提高。
  我不知道怎么用,但是我知道現在視頻調試都是一個(gè)程序來(lái)監控的,
<p>importarctimeimportjsonimportmatplotlib。pyplotaspltimportnumpyasnp#獲取官方文件大小,并轉換成acrobat格式defget_ac_transactions(data_content):url=''returnr' 查看全部

  文章采集調用 【】lotlib
  文章采集調用本地瀏覽器特定鏈接,ip就用轉發(fā)地址好了。其他調用平臺,一般也是接口上獲取。各個(gè)視頻站點(diǎn)可能使用smi等格式數據來(lái)互相傳播,arctime支持?;ヂ?lián)網(wǎng)時(shí)代,隨著(zhù)網(wǎng)速增快,付費會(huì )員日益增多,付費視頻,付費文件所占比例在不斷提高。
  我不知道怎么用,但是我知道現在視頻調試都是一個(gè)程序來(lái)監控的,
<p>importarctimeimportjsonimportmatplotlib。pyplotaspltimportnumpyasnp#獲取官方文件大小,并轉換成acrobat格式defget_ac_transactions(data_content):url=''returnr'

天** | 使用selenium做數據采集

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 147 次瀏覽 ? 2022-07-30 11:09 ? 來(lái)自相關(guān)話(huà)題

  天** | 使用selenium做數據采集
  哈工程管工在讀博士,擅長(cháng)數據采集&挖掘。
  文末有代碼可供下載
  馬云在接受CNBC(美國消費者新聞與商業(yè)頻道)采訪(fǎng)時(shí)提出:“整個(gè)世界將變成數據,我認為這還是只是數據時(shí)代的開(kāi)始。新浪潮即將來(lái)臨,很多就業(yè)機會(huì )將被奪走。有些人會(huì )趕上潮流,變得更加富有和成功。但是對于那些落后的人,未來(lái)將是痛苦的?!本托【幙磥?lái),這種說(shuō)法在人文社科研究當中也同樣適用。在當前數以萬(wàn)計甚至數以十萬(wàn)計研究樣本“遍地走”的時(shí)代,若我們還拘泥于傳統的“小樣本”研究(比如樣本量為100多的調查問(wèn)卷數據等),不僅難以跟隨時(shí)代的腳步,還會(huì )逐漸喪失學(xué)術(shù)競爭力、從而被時(shí)代淘汰。那么,究竟該如何獲取屬于自己的大樣本數據呢?今天小編就帶大家用selenium庫來(lái)爬取國內某知名第三方企業(yè)信息平臺(天**)的企業(yè)工商信息。
  一、自動(dòng)打開(kāi)網(wǎng)站頁(yè)面
  首先,數據爬取的第一步是利用selenium庫啟動(dòng)瀏覽器,打開(kāi)我們的目標網(wǎng)站。部分代碼
  #?@Author??:?Jacob-ZHANG<br />import?requests,base64<br />from?PIL?import?Image<br />import?csv,re<br />from?selenium?import?webdriver<br />import?time,random<br /><br />#1啟動(dòng)瀏覽器。<br />#win<br />browser=webdriver.Chrome(executable_path='driver/chromedriver.exe')<br />#mac<br />#browser=webdriver.Chrome(executable_path='driver/chromedriver')<br /><br />#2加入這個(gè)腳本可以避免被識別<br />browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",?{<br />"source":?"""<br />?????Object.defineProperty(navigator,?'webdriver',?{<br />??????get:?()?=>?undefined<br />????})<br />???"""?})<br /><br />#3延遲10s啟動(dòng)<br />browser.implicitly_wait(10)<br /><br />#4利用谷歌瀏覽器打開(kāi)目標網(wǎng)頁(yè)<br />browser.get('https://pro.xxxxxx.com/searchx')<br /><br />#5將窗口最大化<br />browser.maximize_window()<br /><br />#6給網(wǎng)頁(yè)一些時(shí)間加載<br />time.sleep(random.randint(1,?2))<br />...<br />...<br />...<br />
  非常簡(jiǎn)單,如下所示。
  需要說(shuō)明的是:
  二、模擬登陸
  天**反扒的第一關(guān)便是需要登錄才能夠查看具體的頁(yè)面信息。相比于利用復雜JS逆向技術(shù)完成登陸而言,利用selenium庫模擬人的操作、從而實(shí)現網(wǎng)站自動(dòng)化登陸的做法則顯得更為簡(jiǎn)單易行。從下圖來(lái)看,我們需要利用selenium庫來(lái)完成“點(diǎn)擊密碼登錄(切換到密碼登錄頁(yè)面,也即下圖所示頁(yè)面)-向賬號對話(huà)框內輸入賬號-向密碼對話(huà)框內輸入密碼-向驗證碼對話(huà)框內輸入驗證碼-點(diǎn)擊登錄”等一系列操作后,才能登錄到網(wǎng)站的信息頁(yè)面,獲取自己要想的數據。
  
  對于網(wǎng)站的登錄我們提供了以下兩種方法:一種是自動(dòng)化登錄;另一種則是手動(dòng)登錄。
  2.1 自動(dòng)化登錄
  首先,我們先來(lái)看看較為復雜的自動(dòng)化登錄。要想實(shí)現網(wǎng)頁(yè)的自動(dòng)化登錄,其關(guān)鍵在于利用 「外部力量」 來(lái)識別驗證碼并完成導入。具體而言,我們首先需要定位驗證碼在網(wǎng)頁(yè)當中的元素位置,其次利用截圖軟件根據驗證碼元素位置來(lái)截取驗證碼圖片,再次利用外部庫對驗證碼圖片進(jìn)行識別,最后將識別出的驗證碼錄入對話(huà)框。自動(dòng)化登錄的具體過(guò)程可以分為 get_code_image函數 和 parse_code函數 兩個(gè)步驟進(jìn)行,具體代碼如下所示。其中,驗證碼的解析小編是調用了百度AI的開(kāi)源庫進(jìn)行的。另外,需要注意的是,利用selenium庫打開(kāi)的登錄頁(yè)面一開(kāi)始是不顯示驗證碼的,必須向賬號框和密碼框輸入內容以后,它才會(huì )顯示驗證碼。因此,對于驗證碼的識別和錄入,我們將它放在了所有操作中的最后部分。
  parse_code函數
  def?parse_code():<br />????#用百度API解析圖片<br />????request_url?=?"https://aip.baidubce.com/rest/ ... %3Bbr />????f?=?open('temp/驗證碼.png',?'rb')<br />????img?=?base64.b64encode(f.read())<br />????params?=?{"image":?img}<br />????access_token?=?'24.a7fbbfb9dcab2e1054cc827f09d09234.2592000.1625930266.282335-19004069'<br /><br />????request_url?=?request_url?+?"?access_token="?+?access_token<br />????headers?=?{'content-type':?'application/x-www-form-urlencoded'}<br />????response?=?requests.post(request_url,?data=params,?headers=headers)<br />????<br />????#得到解析結果<br />????dictionary=response.json()<br />????<br />????#得到驗證碼<br />????yanzhengma=dictionary['words_result'][0]['words']<br />????<br />????#錄入驗證碼<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[6]/input').send_keys(yanzhengma)<br />????<br />????#?點(diǎn)擊登錄按鈕<br />????time.sleep(random.randint(1,?2))<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[8]').click()<br /><br />
  parse_code函數
  def?get_code_image():<br />????# 1啟動(dòng)瀏覽器。<br />????browser?=?webdriver.Chrome()<br />????#?2加入這個(gè)腳本可以避免被識別<br />????browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",?{<br />????????"source":?"""<br />?????????Object.defineProperty(navigator,?'webdriver',?{<br />??????????get:?()?=>?undefined<br />????????})<br />???????"""})<br />????#?3延遲10s啟動(dòng)<br />????browser.implicitly_wait(10)<br />????<br />????#?4利用谷歌瀏覽器打開(kāi)目標網(wǎng)頁(yè)<br />????browser.get('https://pro.xxxxxx.com/searchx')<br />????<br />????#?5將窗口最大化<br />????browser.maximize_window()<br />????<br />????#?6給網(wǎng)頁(yè)一些時(shí)間加載<br />????time.sleep(random.randint(1,?2))<br /><br />????#將頁(yè)面從快捷登錄切換到密碼登錄<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/div[1]/div[2]').click()<br />????time.sleep(0.5)<br />????<br />????#輸入賬號<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[2]/input').send_keys(<br />????????'******')<br />????time.sleep(random.randint(1,?2))<br />????<br />????#?輸入密碼<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[4]/input').send_keys('*******')<br />????time.sleep(random.randint(1,?2))<br />????browser.save_screenshot('temp/屏幕.png')#截圖整個(gè)頁(yè)面】<br />????<br />????#定位驗證碼x,y坐標<br />????left_angle=browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[6]/img').location<br />????image=browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[6]/img')<br />????<br />????#獲取驗證碼的長(cháng)和寬<br />????size=image.size<br />????<br />????#設定我們需要截取的位置<br />????rangle?=?(int(left_angle['x']),?int(left_angle['y']?),?int(left_angle['x']?+?size['width']?+?230),<br /><br />??????????????int(left_angle['y']?+?size['height']?+?300))<br />????<br />????#打開(kāi)截圖<br />????open_image=Image.open('temp/屏幕.png')<br />????<br />????#從圖片中截取我們需要的的區域<br />????jietu=open_image.crop(rangle)<br />????jietu.save('temp/驗證碼.png')<br />
  接下來(lái),我們再來(lái)看看如何實(shí)現手動(dòng)登錄。相比于自動(dòng)化登錄,手動(dòng)登錄的操作更為簡(jiǎn)單。具體地,我們只要在完成自動(dòng)打開(kāi)網(wǎng)站頁(yè)面的代碼后加入input()函數,然后自己手動(dòng)向網(wǎng)站的對話(huà)框內輸入賬號、密碼、驗證碼并點(diǎn)擊登錄,就可以進(jìn)入到網(wǎng)站的信息頁(yè)面。
  三、獲取自己想要的數據
  完成登陸之后,就會(huì )跳轉到如下頁(yè)面。然后,大家就可以根據自己的目標繼續撰寫(xiě)屬于自己的“個(gè)性化代碼”了。下面,小編以獲取31個(gè)省市的特定類(lèi)型的企業(yè)數據為例,給大家分享一下自己獲取數據的過(guò)程。
  
  其實(shí),代碼撰寫(xiě)的邏輯很簡(jiǎn)單。首先要做的就是先選中我們要爬取的目標城市。下來(lái)就是根據自己的需求來(lái)定制個(gè)性化的篩選標準。以小編自己的需求為例,先通過(guò)點(diǎn)擊高級模式,向企業(yè)名稱(chēng)對話(huà)框里輸入關(guān)鍵詞,比如醫院(當然,大家也可以通過(guò)限定行業(yè)來(lái)挑選目標);然后,去掉機構類(lèi)型中已勾選的企業(yè),選擇事業(yè)單位;接下來(lái),勾選全部企業(yè)類(lèi)型;最后點(diǎn)擊查看結果。
  至此就完成了篩選,得到了滿(mǎn)足我們要求的所有企業(yè)(見(jiàn)下圖1)。接下來(lái),我們要做的就是遍歷每一頁(yè)里的每一家企業(yè),然后獲取企業(yè)頁(yè)面信息(見(jiàn)下圖2)中自己想要的數據了。
  由于后續代碼較長(cháng),就不在這里一一列舉了。有需要的小伙伴可以在后臺留言,然后向xx索取。
  說(shuō)明
  對于初學(xué)者而言,直接上手可能比較難,建議先收藏本文,待熟練掌握爬蟲(chóng)可以實(shí)驗本文的代碼。
  如果想復現本文代碼,需熟悉
  本文教程&代碼免費分享,但作者時(shí)間和精力寶貴,可能無(wú)法做到一一指導,盡請包涵。
  代碼鏈接: 提取碼: ob2j
  精選文章
  <p style="margin: 0px;padding: 0px;clear: both;min-height: 1em;outline: 0px;max-width: 100%;color: rgb(63, 63, 63);font-size: 15px;letter-spacing: 0px;white-space: normal;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;line-height: normal;box-sizing: border-box !important;overflow-wrap: break-word !important;">長(cháng)期招募小伙伴
  從符號到嵌入:計算社會(huì )科學(xué)的兩種文本表示<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  推薦 | 社科(經(jīng)管)文本分析快速指南<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  使用cntext訓練Glove詞嵌入模型
  cntext庫 | 關(guān)于DUTIR被污染解決辦法<br style="margin: 0px;padding: 0px;" />
  EmoBank | 中文維度情感詞典<br />
  sklearnex庫 | 兩行代碼百倍加速你的機器學(xué)習代碼<br style="margin: 0px;padding: 0px;" />
  認知的測量 | 向量距離vs語(yǔ)義投影
  Wordify | 發(fā)現和區分消費者詞匯的工具
  karateclub庫 | 計算社交網(wǎng)絡(luò )中節點(diǎn)的向量
  視頻專(zhuān)欄課 | Python網(wǎng)絡(luò )爬蟲(chóng)與文本分析
  擴增內置pkl | 歡迎各位向cntext庫分享情感詞典<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  文本分析 | 中國企業(yè)高管團隊創(chuàng )新注意力(含代碼)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  LIWC vs Python | 文本分析之詞典統計法略講(含代碼)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  PNAS | 文本網(wǎng)絡(luò )分析&文化橋梁Python代碼實(shí)現<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  Wordify | 發(fā)現和區分消費者詞匯的工具
  BERTopic庫 | 使用預訓練模型做話(huà)題建模
  tomotopy | 速度最快的LDA主題模型
  文本分析方法在《管理世界》(2021.5)中的應用
  Wow~70G上市公司定期報告數據集
  doccano|為機器學(xué)習建模做數據標注
  使用WeasyPrint自動(dòng)生成pdf報告文件
  100min視頻 | Python文本分析與會(huì )計
  在jupyter內運行R代碼</p> 查看全部

  天** | 使用selenium做數據采集
  哈工程管工在讀博士,擅長(cháng)數據采集&挖掘。
  文末有代碼可供下載
  馬云在接受CNBC(美國消費者新聞與商業(yè)頻道)采訪(fǎng)時(shí)提出:“整個(gè)世界將變成數據,我認為這還是只是數據時(shí)代的開(kāi)始。新浪潮即將來(lái)臨,很多就業(yè)機會(huì )將被奪走。有些人會(huì )趕上潮流,變得更加富有和成功。但是對于那些落后的人,未來(lái)將是痛苦的?!本托【幙磥?lái),這種說(shuō)法在人文社科研究當中也同樣適用。在當前數以萬(wàn)計甚至數以十萬(wàn)計研究樣本“遍地走”的時(shí)代,若我們還拘泥于傳統的“小樣本”研究(比如樣本量為100多的調查問(wèn)卷數據等),不僅難以跟隨時(shí)代的腳步,還會(huì )逐漸喪失學(xué)術(shù)競爭力、從而被時(shí)代淘汰。那么,究竟該如何獲取屬于自己的大樣本數據呢?今天小編就帶大家用selenium庫來(lái)爬取國內某知名第三方企業(yè)信息平臺(天**)的企業(yè)工商信息。
  一、自動(dòng)打開(kāi)網(wǎng)站頁(yè)面
  首先,數據爬取的第一步是利用selenium庫啟動(dòng)瀏覽器,打開(kāi)我們的目標網(wǎng)站。部分代碼
  #?@Author??:?Jacob-ZHANG<br />import?requests,base64<br />from?PIL?import?Image<br />import?csv,re<br />from?selenium?import?webdriver<br />import?time,random<br /><br />#1啟動(dòng)瀏覽器。<br />#win<br />browser=webdriver.Chrome(executable_path='driver/chromedriver.exe')<br />#mac<br />#browser=webdriver.Chrome(executable_path='driver/chromedriver')<br /><br />#2加入這個(gè)腳本可以避免被識別<br />browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",?{<br />"source":?"""<br />?????Object.defineProperty(navigator,?'webdriver',?{<br />??????get:?()?=>?undefined<br />????})<br />???"""?})<br /><br />#3延遲10s啟動(dòng)<br />browser.implicitly_wait(10)<br /><br />#4利用谷歌瀏覽器打開(kāi)目標網(wǎng)頁(yè)<br />browser.get('https://pro.xxxxxx.com/searchx')<br /><br />#5將窗口最大化<br />browser.maximize_window()<br /><br />#6給網(wǎng)頁(yè)一些時(shí)間加載<br />time.sleep(random.randint(1,?2))<br />...<br />...<br />...<br />
  非常簡(jiǎn)單,如下所示。
  需要說(shuō)明的是:
  二、模擬登陸
  天**反扒的第一關(guān)便是需要登錄才能夠查看具體的頁(yè)面信息。相比于利用復雜JS逆向技術(shù)完成登陸而言,利用selenium庫模擬人的操作、從而實(shí)現網(wǎng)站自動(dòng)化登陸的做法則顯得更為簡(jiǎn)單易行。從下圖來(lái)看,我們需要利用selenium庫來(lái)完成“點(diǎn)擊密碼登錄(切換到密碼登錄頁(yè)面,也即下圖所示頁(yè)面)-向賬號對話(huà)框內輸入賬號-向密碼對話(huà)框內輸入密碼-向驗證碼對話(huà)框內輸入驗證碼-點(diǎn)擊登錄”等一系列操作后,才能登錄到網(wǎng)站的信息頁(yè)面,獲取自己要想的數據。
  
  對于網(wǎng)站的登錄我們提供了以下兩種方法:一種是自動(dòng)化登錄;另一種則是手動(dòng)登錄。
  2.1 自動(dòng)化登錄
  首先,我們先來(lái)看看較為復雜的自動(dòng)化登錄。要想實(shí)現網(wǎng)頁(yè)的自動(dòng)化登錄,其關(guān)鍵在于利用 「外部力量」 來(lái)識別驗證碼并完成導入。具體而言,我們首先需要定位驗證碼在網(wǎng)頁(yè)當中的元素位置,其次利用截圖軟件根據驗證碼元素位置來(lái)截取驗證碼圖片,再次利用外部庫對驗證碼圖片進(jìn)行識別,最后將識別出的驗證碼錄入對話(huà)框。自動(dòng)化登錄的具體過(guò)程可以分為 get_code_image函數 和 parse_code函數 兩個(gè)步驟進(jìn)行,具體代碼如下所示。其中,驗證碼的解析小編是調用了百度AI的開(kāi)源庫進(jìn)行的。另外,需要注意的是,利用selenium庫打開(kāi)的登錄頁(yè)面一開(kāi)始是不顯示驗證碼的,必須向賬號框和密碼框輸入內容以后,它才會(huì )顯示驗證碼。因此,對于驗證碼的識別和錄入,我們將它放在了所有操作中的最后部分。
  parse_code函數
  def?parse_code():<br />????#用百度API解析圖片<br />????request_url?=?"https://aip.baidubce.com/rest/ ... %3Bbr />????f?=?open('temp/驗證碼.png',?'rb')<br />????img?=?base64.b64encode(f.read())<br />????params?=?{"image":?img}<br />????access_token?=?'24.a7fbbfb9dcab2e1054cc827f09d09234.2592000.1625930266.282335-19004069'<br /><br />????request_url?=?request_url?+?"?access_token="?+?access_token<br />????headers?=?{'content-type':?'application/x-www-form-urlencoded'}<br />????response?=?requests.post(request_url,?data=params,?headers=headers)<br />????<br />????#得到解析結果<br />????dictionary=response.json()<br />????<br />????#得到驗證碼<br />????yanzhengma=dictionary['words_result'][0]['words']<br />????<br />????#錄入驗證碼<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[6]/input').send_keys(yanzhengma)<br />????<br />????#?點(diǎn)擊登錄按鈕<br />????time.sleep(random.randint(1,?2))<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[8]').click()<br /><br />
  parse_code函數
  def?get_code_image():<br />????# 1啟動(dòng)瀏覽器。<br />????browser?=?webdriver.Chrome()<br />????#?2加入這個(gè)腳本可以避免被識別<br />????browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",?{<br />????????"source":?"""<br />?????????Object.defineProperty(navigator,?'webdriver',?{<br />??????????get:?()?=>?undefined<br />????????})<br />???????"""})<br />????#?3延遲10s啟動(dòng)<br />????browser.implicitly_wait(10)<br />????<br />????#?4利用谷歌瀏覽器打開(kāi)目標網(wǎng)頁(yè)<br />????browser.get('https://pro.xxxxxx.com/searchx')<br />????<br />????#?5將窗口最大化<br />????browser.maximize_window()<br />????<br />????#?6給網(wǎng)頁(yè)一些時(shí)間加載<br />????time.sleep(random.randint(1,?2))<br /><br />????#將頁(yè)面從快捷登錄切換到密碼登錄<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/div[1]/div[2]').click()<br />????time.sleep(0.5)<br />????<br />????#輸入賬號<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[2]/input').send_keys(<br />????????'******')<br />????time.sleep(random.randint(1,?2))<br />????<br />????#?輸入密碼<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[4]/input').send_keys('*******')<br />????time.sleep(random.randint(1,?2))<br />????browser.save_screenshot('temp/屏幕.png')#截圖整個(gè)頁(yè)面】<br />????<br />????#定位驗證碼x,y坐標<br />????left_angle=browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[6]/img').location<br />????image=browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[6]/img')<br />????<br />????#獲取驗證碼的長(cháng)和寬<br />????size=image.size<br />????<br />????#設定我們需要截取的位置<br />????rangle?=?(int(left_angle['x']),?int(left_angle['y']?),?int(left_angle['x']?+?size['width']?+?230),<br /><br />??????????????int(left_angle['y']?+?size['height']?+?300))<br />????<br />????#打開(kāi)截圖<br />????open_image=Image.open('temp/屏幕.png')<br />????<br />????#從圖片中截取我們需要的的區域<br />????jietu=open_image.crop(rangle)<br />????jietu.save('temp/驗證碼.png')<br />
  接下來(lái),我們再來(lái)看看如何實(shí)現手動(dòng)登錄。相比于自動(dòng)化登錄,手動(dòng)登錄的操作更為簡(jiǎn)單。具體地,我們只要在完成自動(dòng)打開(kāi)網(wǎng)站頁(yè)面的代碼后加入input()函數,然后自己手動(dòng)向網(wǎng)站的對話(huà)框內輸入賬號、密碼、驗證碼并點(diǎn)擊登錄,就可以進(jìn)入到網(wǎng)站的信息頁(yè)面。
  三、獲取自己想要的數據
  完成登陸之后,就會(huì )跳轉到如下頁(yè)面。然后,大家就可以根據自己的目標繼續撰寫(xiě)屬于自己的“個(gè)性化代碼”了。下面,小編以獲取31個(gè)省市的特定類(lèi)型的企業(yè)數據為例,給大家分享一下自己獲取數據的過(guò)程。
  
  其實(shí),代碼撰寫(xiě)的邏輯很簡(jiǎn)單。首先要做的就是先選中我們要爬取的目標城市。下來(lái)就是根據自己的需求來(lái)定制個(gè)性化的篩選標準。以小編自己的需求為例,先通過(guò)點(diǎn)擊高級模式,向企業(yè)名稱(chēng)對話(huà)框里輸入關(guān)鍵詞,比如醫院(當然,大家也可以通過(guò)限定行業(yè)來(lái)挑選目標);然后,去掉機構類(lèi)型中已勾選的企業(yè),選擇事業(yè)單位;接下來(lái),勾選全部企業(yè)類(lèi)型;最后點(diǎn)擊查看結果。
  至此就完成了篩選,得到了滿(mǎn)足我們要求的所有企業(yè)(見(jiàn)下圖1)。接下來(lái),我們要做的就是遍歷每一頁(yè)里的每一家企業(yè),然后獲取企業(yè)頁(yè)面信息(見(jiàn)下圖2)中自己想要的數據了。
  由于后續代碼較長(cháng),就不在這里一一列舉了。有需要的小伙伴可以在后臺留言,然后向xx索取。
  說(shuō)明
  對于初學(xué)者而言,直接上手可能比較難,建議先收藏本文,待熟練掌握爬蟲(chóng)可以實(shí)驗本文的代碼。
  如果想復現本文代碼,需熟悉
  本文教程&代碼免費分享,但作者時(shí)間和精力寶貴,可能無(wú)法做到一一指導,盡請包涵。
  代碼鏈接: 提取碼: ob2j
  精選文章
  <p style="margin: 0px;padding: 0px;clear: both;min-height: 1em;outline: 0px;max-width: 100%;color: rgb(63, 63, 63);font-size: 15px;letter-spacing: 0px;white-space: normal;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;line-height: normal;box-sizing: border-box !important;overflow-wrap: break-word !important;">長(cháng)期招募小伙伴
  從符號到嵌入:計算社會(huì )科學(xué)的兩種文本表示<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  推薦 | 社科(經(jīng)管)文本分析快速指南<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  使用cntext訓練Glove詞嵌入模型
  cntext庫 | 關(guān)于DUTIR被污染解決辦法<br style="margin: 0px;padding: 0px;" />
  EmoBank | 中文維度情感詞典<br />
  sklearnex庫 | 兩行代碼百倍加速你的機器學(xué)習代碼<br style="margin: 0px;padding: 0px;" />
  認知的測量 | 向量距離vs語(yǔ)義投影
  Wordify | 發(fā)現和區分消費者詞匯的工具
  karateclub庫 | 計算社交網(wǎng)絡(luò )中節點(diǎn)的向量
  視頻專(zhuān)欄課 | Python網(wǎng)絡(luò )爬蟲(chóng)與文本分析
  擴增內置pkl | 歡迎各位向cntext庫分享情感詞典<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  文本分析 | 中國企業(yè)高管團隊創(chuàng )新注意力(含代碼)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  LIWC vs Python | 文本分析之詞典統計法略講(含代碼)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  PNAS | 文本網(wǎng)絡(luò )分析&文化橋梁Python代碼實(shí)現<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  Wordify | 發(fā)現和區分消費者詞匯的工具
  BERTopic庫 | 使用預訓練模型做話(huà)題建模
  tomotopy | 速度最快的LDA主題模型
  文本分析方法在《管理世界》(2021.5)中的應用
  Wow~70G上市公司定期報告數據集
  doccano|為機器學(xué)習建模做數據標注
  使用WeasyPrint自動(dòng)生成pdf報告文件
  100min視頻 | Python文本分析與會(huì )計
  在jupyter內運行R代碼</p>

文章采集調用 《抽獎》某次紅藍對抗之Solr-RCE實(shí)戰繞過(guò)

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 133 次瀏覽 ? 2022-07-28 03:27 ? 來(lái)自相關(guān)話(huà)題

  文章采集調用 《抽獎》某次紅藍對抗之Solr-RCE實(shí)戰繞過(guò)
  ?
  文章首發(fā)于:先知社區
  前言
  在某次紅藍對抗過(guò)程中。
  要結束的時(shí)候突然發(fā)現了掃描器爆出了
  Solr-RCE-CVE-2019-0192漏洞。
  但是進(jìn)行測試過(guò)程中,發(fā)現存在各種各樣的問(wèn)題
  繞過(guò)1
  進(jìn)行測試,發(fā)現目標只可以執行單命令,返回字段比較少的命令。
  輸入:whoami、ipconfig
  執行dir,無(wú)法執行。
  不出網(wǎng)
  想著(zhù)直接執行ps直接上線(xiàn)就好了,各種嘗試之后,后知后覺(jué)發(fā)現對方不出網(wǎng)
  寫(xiě)websgell
  發(fā)現目標不出網(wǎng)的時(shí)候,只有寫(xiě)webshell這一條路子可以走了。
  但是目標只能執行個(gè)別命令還無(wú)法解決。
  dir無(wú)法執行。陷入了沉思,加入單雙引號也不行。
  根據以前的內網(wǎng)經(jīng)驗是不是系統無(wú)法默認調用到dir.exe。
  那么 cmd /c dir是不是可以。
  驚奇的發(fā)現,可以完美的執行命令。
  通過(guò)dir,找到目錄。進(jìn)行寫(xiě)馬嘗試。
  但是發(fā)現目標路由規則寫(xiě)死了,無(wú)法直接訪(fǎng)問(wèn)到.jsp的文件。
  
  剛開(kāi)始以為是根目錄的問(wèn)題。
  發(fā)現不止根目錄,常用的css/js和img下面的也不行。
  后續在webshell中看到,翻文件看到了有類(lèi)似路由機制的驗證
  言歸正傳
  在執行rce的時(shí)候,找到了solr的目錄。發(fā)現這里的.jsp是沒(méi)有這個(gè)驗證的。
  利用命令執行進(jìn)行找到該位置,進(jìn)行寫(xiě)文件。
  問(wèn)題來(lái)了。echo 寫(xiě)入一直無(wú)法寫(xiě)入。。
  問(wèn)題解決
  把這里的特殊字符進(jìn)行特殊字符編碼,即可成功寫(xiě)入。
  又遇到一個(gè)問(wèn)題。jsp的馬子都有%號,這里不論怎么做 %就是寫(xiě)不進(jìn)去。
  差點(diǎn)放棄。找不到不帶%的馬子。
  柳暗花明
  但是想到了上午利用過(guò)的Certutil可以進(jìn)行編碼解碼
  這樣就沒(méi)有特殊字符了。
  完全沒(méi)問(wèn)題。
  剛開(kāi)始一點(diǎn)一點(diǎn)追加,發(fā)現下面的會(huì )寫(xiě)進(jìn)去兩行
  看了一下就最后有一個(gè)+號,沒(méi)有特殊字符。
  全部直接寫(xiě)進(jìn)去。
  然后decode進(jìn)行解碼,完美。
  
  訪(fǎng)問(wèn)但是是500.不過(guò)確很開(kāi)心,因為確實(shí)寫(xiě)上來(lái)。
  接下來(lái)解決為啥500就可以了。
  type 123.jsp
  查看一下。
  發(fā)現最后decode的時(shí)候,少了一個(gè)>
  本地測試,是沒(méi)有這個(gè)問(wèn)題的??赡苁悄繕艘淮涡宰址L(cháng)度的問(wèn)題。
  這里很簡(jiǎn)單了。
  追加一下就可以了。
  連接成功
  驗證
  文末福利:
  《安卓Frida逆向與協(xié)議分析》
  本書(shū)翔實(shí)地介紹流行的Frida工具在安卓逆向工程中的應用,內容包括:如何安裝和使用Frida、基本環(huán)境的搭建、Frida-tools、Frida腳本、Frida API、批量自動(dòng)化Trace和分析、RPC遠程方法調用、在無(wú)須逆向算法具體實(shí)現的情況下對Frida工具的調用,并提供了大量App逆向與協(xié)議分析案例,書(shū)中還介紹了更加穩定的Xposed框架的使用方法,以及從安卓源碼開(kāi)始定制屬于自己的抓包沙箱,打造無(wú)法被繞過(guò)的抓包環(huán)境等內容。
  本書(shū)案例豐富,注重實(shí)操,適合安卓應用安全工程師、安卓逆向分析工程師、爬蟲(chóng)工程師以及大數據采集和分析工程師使用。
  昨天文章抽獎圖片有點(diǎn)問(wèn)題,師傅們重新掃碼再抽一波把。
  福利時(shí)間:7月25日-7月31日
  2. 活動(dòng)規則:
 ?、?掃描下方二維碼參與抽獎或公眾號后臺回復安卓逆向即可參與;
 ?、?必要條件:轉發(fā)本文到朋友圈,抽獎前不可刪除;
 ?、?開(kāi)獎結束后,請中獎小伙伴及時(shí)將中獎信息和朋友圈轉發(fā)記錄發(fā)送到公眾號后臺聯(lián)系,超過(guò)24小時(shí)未領(lǐng)取的視為自動(dòng)放棄哈?。?!
  未滿(mǎn)足②條件但被抽中,則獲獎資格會(huì )被取消哦
  昨天文章抽獎圖片有點(diǎn)問(wèn)題,師傅們重新掃碼再抽一波把。 查看全部

  文章采集調用 《抽獎》某次紅藍對抗之Solr-RCE實(shí)戰繞過(guò)
  ?
  文章首發(fā)于:先知社區
  前言
  在某次紅藍對抗過(guò)程中。
  要結束的時(shí)候突然發(fā)現了掃描器爆出了
  Solr-RCE-CVE-2019-0192漏洞。
  但是進(jìn)行測試過(guò)程中,發(fā)現存在各種各樣的問(wèn)題
  繞過(guò)1
  進(jìn)行測試,發(fā)現目標只可以執行單命令,返回字段比較少的命令。
  輸入:whoami、ipconfig
  執行dir,無(wú)法執行。
  不出網(wǎng)
  想著(zhù)直接執行ps直接上線(xiàn)就好了,各種嘗試之后,后知后覺(jué)發(fā)現對方不出網(wǎng)
  寫(xiě)websgell
  發(fā)現目標不出網(wǎng)的時(shí)候,只有寫(xiě)webshell這一條路子可以走了。
  但是目標只能執行個(gè)別命令還無(wú)法解決。
  dir無(wú)法執行。陷入了沉思,加入單雙引號也不行。
  根據以前的內網(wǎng)經(jīng)驗是不是系統無(wú)法默認調用到dir.exe。
  那么 cmd /c dir是不是可以。
  驚奇的發(fā)現,可以完美的執行命令。
  通過(guò)dir,找到目錄。進(jìn)行寫(xiě)馬嘗試。
  但是發(fā)現目標路由規則寫(xiě)死了,無(wú)法直接訪(fǎng)問(wèn)到.jsp的文件。
  
  剛開(kāi)始以為是根目錄的問(wèn)題。
  發(fā)現不止根目錄,常用的css/js和img下面的也不行。
  后續在webshell中看到,翻文件看到了有類(lèi)似路由機制的驗證
  言歸正傳
  在執行rce的時(shí)候,找到了solr的目錄。發(fā)現這里的.jsp是沒(méi)有這個(gè)驗證的。
  利用命令執行進(jìn)行找到該位置,進(jìn)行寫(xiě)文件。
  問(wèn)題來(lái)了。echo 寫(xiě)入一直無(wú)法寫(xiě)入。。
  問(wèn)題解決
  把這里的特殊字符進(jìn)行特殊字符編碼,即可成功寫(xiě)入。
  又遇到一個(gè)問(wèn)題。jsp的馬子都有%號,這里不論怎么做 %就是寫(xiě)不進(jìn)去。
  差點(diǎn)放棄。找不到不帶%的馬子。
  柳暗花明
  但是想到了上午利用過(guò)的Certutil可以進(jìn)行編碼解碼
  這樣就沒(méi)有特殊字符了。
  完全沒(méi)問(wèn)題。
  剛開(kāi)始一點(diǎn)一點(diǎn)追加,發(fā)現下面的會(huì )寫(xiě)進(jìn)去兩行
  看了一下就最后有一個(gè)+號,沒(méi)有特殊字符。
  全部直接寫(xiě)進(jìn)去。
  然后decode進(jìn)行解碼,完美。
  
  訪(fǎng)問(wèn)但是是500.不過(guò)確很開(kāi)心,因為確實(shí)寫(xiě)上來(lái)。
  接下來(lái)解決為啥500就可以了。
  type 123.jsp
  查看一下。
  發(fā)現最后decode的時(shí)候,少了一個(gè)>
  本地測試,是沒(méi)有這個(gè)問(wèn)題的??赡苁悄繕艘淮涡宰址L(cháng)度的問(wèn)題。
  這里很簡(jiǎn)單了。
  追加一下就可以了。
  連接成功
  驗證
  文末福利:
  《安卓Frida逆向與協(xié)議分析》
  本書(shū)翔實(shí)地介紹流行的Frida工具在安卓逆向工程中的應用,內容包括:如何安裝和使用Frida、基本環(huán)境的搭建、Frida-tools、Frida腳本、Frida API、批量自動(dòng)化Trace和分析、RPC遠程方法調用、在無(wú)須逆向算法具體實(shí)現的情況下對Frida工具的調用,并提供了大量App逆向與協(xié)議分析案例,書(shū)中還介紹了更加穩定的Xposed框架的使用方法,以及從安卓源碼開(kāi)始定制屬于自己的抓包沙箱,打造無(wú)法被繞過(guò)的抓包環(huán)境等內容。
  本書(shū)案例豐富,注重實(shí)操,適合安卓應用安全工程師、安卓逆向分析工程師、爬蟲(chóng)工程師以及大數據采集和分析工程師使用。
  昨天文章抽獎圖片有點(diǎn)問(wèn)題,師傅們重新掃碼再抽一波把。
  福利時(shí)間:7月25日-7月31日
  2. 活動(dòng)規則:
 ?、?掃描下方二維碼參與抽獎或公眾號后臺回復安卓逆向即可參與;
 ?、?必要條件:轉發(fā)本文到朋友圈,抽獎前不可刪除;
 ?、?開(kāi)獎結束后,請中獎小伙伴及時(shí)將中獎信息和朋友圈轉發(fā)記錄發(fā)送到公眾號后臺聯(lián)系,超過(guò)24小時(shí)未領(lǐng)取的視為自動(dòng)放棄哈?。?!
  未滿(mǎn)足②條件但被抽中,則獲獎資格會(huì )被取消哦
  昨天文章抽獎圖片有點(diǎn)問(wèn)題,師傅們重新掃碼再抽一波把。

社區精選 | 搭建前端監控,采集用戶(hù)行為的 N 種姿勢

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 155 次瀏覽 ? 2022-07-27 21:31 ? 來(lái)自相關(guān)話(huà)題

  社區精選 | 搭建前端監控,采集用戶(hù)行為的 N 種姿勢
  今天為各位帶來(lái)的是社區作者楊成功的文章,在這篇文章中他為大家介紹了搭建前端監控,采集用戶(hù)行為的 N 種姿勢。
  讓我們一起來(lái)了解吧~
  大家好,我是楊成功。
  上一篇我們詳細介紹了前端如何采集異常數據。采集異常數據是為了隨時(shí)監測線(xiàn)上項目的運行情況,發(fā)現問(wèn)題及時(shí)修復。在很多場(chǎng)景下,除了異常監控有用,收集用戶(hù)的行為數據同樣有意義。
  怎么定義行為數據?顧名思義,就是用戶(hù)在使用產(chǎn)品過(guò)程中產(chǎn)生的行為軌跡。比如去過(guò)哪幾個(gè)頁(yè)面,點(diǎn)過(guò)哪幾個(gè)按鈕,甚至在某個(gè)頁(yè)面停留了多長(cháng)時(shí)間,某個(gè)按鈕點(diǎn)擊了多少次,如果有需求都可以記錄下來(lái)。
  但是記錄行為數據是一個(gè)和業(yè)務(wù)緊密關(guān)聯(lián)的事情,不可能把每個(gè)用戶(hù)每一步操作都極其詳細的記錄下來(lái),這樣會(huì )產(chǎn)生極其龐大的數據,很顯然不現實(shí)。
  合理的做法是,根據產(chǎn)品的實(shí)際情況評估,哪個(gè)模塊哪個(gè)按鈕需要重點(diǎn)記錄,則可以采集的詳細一些;哪些模塊不需要重點(diǎn)關(guān)注,則簡(jiǎn)單記錄一下基本信息。
  根據這個(gè)邏輯,我們可以把行為數據分為兩類(lèi):
  通用數據特定數據
  下面分別介紹這兩類(lèi)數據該如何收集。
  通用數據
  在一個(gè)產(chǎn)品中,用戶(hù)最基本的行為就是切換頁(yè)面。用戶(hù)使用了哪些功能,也能從切換頁(yè)面中體現出來(lái)。因此通用數據一般是在頁(yè)面切換時(shí)產(chǎn)生,表示某個(gè)用戶(hù)訪(fǎng)問(wèn)了某個(gè)頁(yè)面。
  頁(yè)面切換對應到前端就是路由切換,可以通過(guò)監聽(tīng)路由變化來(lái)拿到新頁(yè)面的數據。Vue 在全局路由守衛中監聽(tīng)路由變化,任意路由切換都能執行這里的回調函數。
  //?Vue3?路由寫(xiě)法<br />const?router?=?createRouter({?...?})<br />router.beforeEach(to?=>?{<br />??//?to?代表新頁(yè)面的路由對象<br />??recordBehaviors(to)<br />})<br />
  React 在組件的 useEffect 中實(shí)現相同的功能。不過(guò)要注意一點(diǎn),監聽(tīng)所有路由變化,則需要所有路由都經(jīng)過(guò)這個(gè)組件,監聽(tīng)才有效果。具體的方法是配置路由時(shí)加*配置:
  import?HomePage?from?'@/pages/Home'<br />,<br />
  然后在這個(gè)組件的的 useEffect 中監聽(tīng)路由變化:
  //?HomePage.jsx<br />const?{?pathname?}?=?useLocation();<br />useEffect(()?=>?{<br />??//?路由切換這個(gè)函數觸發(fā)<br />??recordBehaviors(pathname);<br />},?[pathname]);<br />
  上面代碼中,在路由切換時(shí)都調用了recordBehaviors()方法并傳入了參數。
  Vue 傳的是一個(gè)路由對象,React 傳的是路由地址,接下來(lái)就可以在這個(gè)函數內收集數據了。
  明確了在哪里收集數據,我們還要知道收集哪些數據。收集行為數據最基本的字段如下:
  上面的字段中,應用標識、環(huán)境、版本號統稱(chēng)應用字段,用于標志數據的來(lái)源。其他字段主要分為用戶(hù),頁(yè)面,時(shí)間三類(lèi),通過(guò)這三類(lèi)數據就可以簡(jiǎn)單的判斷出一件事:誰(shuí)到過(guò)哪個(gè)頁(yè)面,并停留了多長(cháng)時(shí)間。
  應用字段的配置和獲取方式我們在上一節中講過(guò),就不做多余介紹了,獲取字段的方式都是通用的。
  下面介紹其他的幾類(lèi)數據如何獲取。
  獲取用戶(hù)信息
  現代前端應用存儲用戶(hù)信息的方式基本都是一樣的,localStorage 存一份,狀態(tài)管理里存一份。因此獲取用戶(hù)信息從這兩處的任意一處獲得即可。這里簡(jiǎn)單介紹下如何從狀態(tài)管理中獲取。
  最簡(jiǎn)單的方法,在函數recordBehaviors()所處的 js 文件中,直接導入用戶(hù)狀態(tài):
  //?從狀態(tài)管理里中導出用戶(hù)數據<br />import?{?UserStore?}?from?'@/stores';<br />let?{?user_id,?user_name?}?=?UserStore;<br />
  這里的@/stores 指向我項目中的文件 src/stores/index.ts,表示狀態(tài)管理的入口文件,使用時(shí)替換成自己項目的實(shí)際位置。實(shí)際情況中還會(huì )有用戶(hù)數據為空的問(wèn)題,這里需要單獨處理一下,方便我們在后續的數據查看中能看出分別:
  import?{?UserStore?}?from?'@/stores';<br /><br />//?收集行為函數<br />const?recordBehaviors?=?()=>?{<br />??let?report_date?=?{<br />????...<br />??}<br />??if(UserStore)?{<br />????let?{?user_id,?user_name}?=?UserStore<br />????report_date.user_id?=?user_id?||?0<br />????report_date.user_name?=?user_name?||?'未命名'<br />??}?else?{<br />????report_date.user_id?=?user_id?||?-1<br />????report_date.user_name?=?user_name?||?'未獲取'<br />??}<br />}<br />
  上面代碼中,首先判斷了狀態(tài)管理中是否有用戶(hù)數據,如果有則獲取,沒(méi)有則指定默認值。這里指定默認值的細節要注意,不是隨便指定的,比如 user_id 的默認值有如下意義:
  用戶(hù)數據是經(jīng)常容易出錯的地方,因為涉及到登錄狀態(tài)和權限等復雜問(wèn)題。指定了上述默認值后,就可以從收集到的行為數據中判斷出某個(gè)頁(yè)面用戶(hù)狀態(tài)是否正常。
  
  獲取頁(yè)面信息
  前面我們在監聽(tīng)路由變化的地方調用了 recordBehaviors 函數并傳入了參數,頁(yè)面信息可以從參數中拿到,我們先看在 Vue 中怎么獲?。?br />   //?路由配置<br />{<br />??path:?'/test',<br />??meta:?{<br />????title:?'測試頁(yè)面'<br />??},<br />??component:?()?=>?import('@/views/test/Index.vue')<br />}<br /><br />//?獲取配置<br />const?recordBehaviors?=?(to)=>?{<br />??let?page_route?=?to.path<br />??let?page_title?=?to.meta.title<br />}<br />
  Vue 中比較簡(jiǎn)單,可以直接從參數中拿到頁(yè)面數據。相比之下,React 的參數只是一個(gè)路由地址,想拿到頁(yè)面名稱(chēng)還需要做單獨處理。
  一般在設計權限時(shí),我們會(huì )在服務(wù)端會(huì )維護一套路由數據,包含路由地址和名稱(chēng)。路由數據在登錄后獲取,存在狀態(tài)管理中,那么有了 pathname 就可以從路由數據中找到對應的路由名稱(chēng)。
  //?React?中<br />import?{?RouteStore?}?from?'@/stores';<br /><br />const?recordBehaviors?=?(pathname)?=>?{<br />??let?{?routers?}?=?RouteStore;?//?取出路由數據<br />??let?route?=?routers.find((row)?=>?(row.path?=?pathname));<br />??if?(route)?{<br />????let?page_route?=?route.path;<br />????let?page_title?=?route.title;<br />??}<br />};<br />
  這樣,頁(yè)面信息的 page_route、page_title 兩個(gè)字段也拿到了。
  設置時(shí)間
  行為數據中用兩個(gè)字段start_at、end_at分別表示用戶(hù)進(jìn)入頁(yè)面和離開(kāi)頁(yè)面的時(shí)間。這兩個(gè)字段非常重要,我們在后續使用數據的時(shí)候可以判斷出很多信息,比如:
  還有很多信息,都能根據這兩個(gè)時(shí)間字段判斷。開(kāi)始時(shí)間很好辦,函數觸發(fā)時(shí)直接獲取當前時(shí)間:
  <br />var?start_at?=?new?Date();<br />
  結束時(shí)間這里需要考慮的情況比較多。首先要確定數據什么時(shí)候上報?用戶(hù)進(jìn)入頁(yè)面后上報,還是離開(kāi)頁(yè)面時(shí)上報?
  如果進(jìn)入頁(yè)面時(shí)上報,可以保證行為數據一定會(huì )被記錄,不會(huì )丟失,但此時(shí) end_at 字段必然為空。這樣的話(huà),就需要在離開(kāi)頁(yè)面時(shí)再調接口,將這條記錄的 end_time 更新,這種方式的實(shí)現比較麻煩一些:
  //?進(jìn)入頁(yè)面時(shí)調用<br />const?recordBehaviors?=?()?=>?{<br />??let?report_date?=?{...}?//?此時(shí)?end_at?為空<br />??http.post('/behaviors/insert',?report_date).then(res=>?{<br />????let?id?=?res.id?//?數據?id<br />????localStorage.setItem('CURRENT_BEHAVIOR_ID',?id)<br />??})<br />}<br /><br />//?離開(kāi)頁(yè)面時(shí)調用:<br />const?updateBehaviors?=?()=>?{<br />??let?id?=?localStorage.getItem('CURRENT_BEHAVIOR_ID')<br />??let?end_at?=?new?Date()<br />??http.post('/behaviors/update/'+id,?end_at)?//?根據?id?更新結束時(shí)間<br />??localStorage.removeItem('CURRENT_BEHAVIOR_ID')<br />}<br />
  上面代碼中,進(jìn)入頁(yè)面先上報數據,并保存下 id,離開(kāi)頁(yè)面再根據 id 更新這條數據的結束時(shí)間。
  如果在離開(kāi)頁(yè)面時(shí)上報,那么就要保證離開(kāi)頁(yè)面前上報接口已經(jīng)觸發(fā),否則會(huì )導致數據丟失。在滿(mǎn)足這個(gè)前提條件下,上報邏輯會(huì )變成這樣:
  //?進(jìn)入頁(yè)面時(shí)調用<br />const?recordBehaviors?=?()?=>?{<br />??let?report_date?=?{...}?//?此時(shí)?end_at?為空<br />??localStorage.setItem('CURRENT_BEHAVIOR',?JSON.stringify(report_date));<br />}<br /><br />//?離開(kāi)頁(yè)面時(shí)調用<br />const?reportBehaviors?=?()?=>?{<br />??let?end_at?=?new?Date()<br />??let?report_str?=?localStorage.getItem('CURRENT_BEHAVIOR')<br />??if(report_str)?{<br />????let?report_date?=?JSON.parse(report_str)<br />????report_date.end_at?=?end_at<br />????http.post('/behaviors/insert',?report_date)<br />??}?else?{<br />????console.log('無(wú)行為數據')<br />??}<br />}<br />
  對比一下這兩種方案,第一種的弊端是接口需要調兩次,這會(huì )使接口請求量倍增。第二種方案只調用一次,但是需要特別注意可靠性處理,總體來(lái)說(shuō)第二種方案更好些。
  特定數據
  除了通用數據,大部分情況我們還要在具體的頁(yè)面中收集某些特定的行為。比如某個(gè)關(guān)鍵的按鈕有沒(méi)有點(diǎn)擊,點(diǎn)了多少次;或者某個(gè)關(guān)鍵區域用戶(hù)有沒(méi)有看到,看到(曝光)了多少次等等。
  收集數據還有一個(gè)更專(zhuān)業(yè)的叫法 ———— 埋點(diǎn)。直觀(guān)理解是,哪里需要上報數據,就埋一個(gè)上報函數進(jìn)去。
  通用數據針對所有頁(yè)面自動(dòng)收集,特定數據就需要根據每個(gè)頁(yè)面的實(shí)際需求手動(dòng)添加。以一個(gè)按鈕為例:
  點(diǎn)擊;<br />const?onClick?=?(e)?=>?{<br />??//?console.log(e);<br />??repoerEvents(e);<br />};<br />
  上面代碼中,我們想記錄這個(gè)按鈕的點(diǎn)擊情況,所以做了一個(gè)簡(jiǎn)單的埋點(diǎn) ———— 在按鈕點(diǎn)擊事件中調用 repoerEvents()方法,這個(gè)方法內部會(huì )收集數據并上報。
  這是最原始的埋點(diǎn)方式,直接將上報方法放到事件函數中。repoerEvents() 方法接收一個(gè)事件對象參數,在參數中獲取需要上報的事件數據。
  特定數據與通用數據的許多字段是一樣的,收集特定數據需要的基本字段如下:
  這些基本字段中,前 7 個(gè)字段與前面通用數據的獲取完全一樣,這里就不贅述了。實(shí)際上特定數據需要獲取的專(zhuān)有字段只有 3 個(gè):
  這三個(gè)字段也非常容易獲取。event_type 表示事件觸發(fā)的類(lèi)型,比如點(diǎn)擊、滾動(dòng)、拖動(dòng)等,可以在事件對象中拿到。action_tag 和 action_label 是必須指定的屬性,表示本次埋點(diǎn)的標識和文字描述,用于在后續的數據處理時(shí)方便查閱和統計。
  了解了采集特定數據是怎么回事,接下來(lái)我們用代碼實(shí)現。
  手動(dòng)埋點(diǎn)上報
  假設要為登錄按鈕做埋點(diǎn),按照上面的數據采集方式,我們書(shū)寫(xiě)代碼如下:
  <br />??登錄<br />;<br />const?onClick?=?(e)?=>?{<br />??//?console.log(e);<br />??repoerEvents(e);<br />};<br />
  代碼中,我們通過(guò)元素的自定義屬性傳遞了 tag 和 label 兩個(gè)標識,用于在上報函數中獲取。
  上報函數 repoerEvents() 代碼邏輯如下:
  
  //?埋點(diǎn)上報函數<br />const?repoerEvents?=?(e)=>?{<br />??let?report_date?=?{...}<br />??let?{?tag,?label?}?=?e.target.dataset<br />??if(!tag?||?!label)?{<br />????return?new?Error('上報元素屬性缺失')<br />??}<br />??report_date.event_type?=?e.type<br />??report_date.action_tag?=?tag<br />??report_date.action_label?=?label<br /><br />??//?上報數據<br />??http.post('/events/insert',?report_date)<br />}<br />
  這樣就實(shí)現了一個(gè)基本的特定數據埋點(diǎn)上報功能。
  全局自動(dòng)上報
  現在我們回過(guò)頭來(lái)梳理一下這個(gè)上報流程,雖然基本功能實(shí)現了,但是還有些不合理之處,比如:
  首先我們的埋點(diǎn)方式是基于事件的,也就是說(shuō),不管元素本身是否需要事件處理,我們都要給他加上,并在函數內部調用 repoerEvents() 方法。如果一個(gè)項目需要埋點(diǎn)的地方非常多,這種方式的接入成本就會(huì )非常高。
  參考之前做異常監控的邏輯,我們換一個(gè)思路:能否全局監聽(tīng)事件自動(dòng)上報呢?
  思考一下,如果要做全局監聽(tīng)事件,那么只能監聽(tīng)需要埋點(diǎn)的元素的事件。那么如何判斷哪些元素需要埋點(diǎn)呢?
  上面我們?yōu)槁顸c(diǎn)的元素指定了data-tag和data-label兩個(gè)自定義屬性,那是不是根據這兩個(gè)自定義屬性判斷就可以?我們來(lái)試驗一下:
  window.addEventListener('click',?(event)?=>?{<br />??let?{?tag,?label,?trigger?}?=?event.target.dataset;<br />??if?(tag?&&?label?&&?trigger?==?'click')?{<br />????//?說(shuō)明該元素需要埋點(diǎn)<br />????repoerEvents(event);<br />??}<br />});<br />
  上面代碼還多判斷了一個(gè)自定義屬性 dataset.trigger,表示元素在哪種事件觸發(fā)時(shí)需要上報。全局監聽(tīng)事件需要這個(gè)標識,這樣可避免事件沖突。
  添加全局監聽(tīng)后,收集某個(gè)元素的特定數據就簡(jiǎn)單了,方法如下:
  <br />??保存<br /><br />
  試驗證明,上述全局處理的方式是可行的,這樣的話(huà)就不需要在每一個(gè)元素上添加或修改事件處理函數了,只需要在元素中添加三個(gè)自定義屬性 data-tag,data-label,data-trigger 就能自動(dòng)實(shí)現數據埋點(diǎn)上報。
  組件上報
  上面全局監聽(tīng)事件上報的方式已經(jīng)比手動(dòng)埋點(diǎn)高效了許多,現在我們再換一個(gè)場(chǎng)景。
  一般情況下當埋點(diǎn)功能成熟之后,會(huì )封裝成一個(gè) SDK 供其他項目使用。如果我們將采集數據按照 SDK 的思路實(shí)現,讓開(kāi)發(fā)者在全局監聽(tīng)事件,是不是一個(gè)好的方式呢?
  顯然是不太友好的。如果是一個(gè) SDK,那么最好的方式是將所有內容聚合成一個(gè)組件,在組件內實(shí)現上報的所有功能,而不是讓使用者在項目中添加監聽(tīng)事件。
  封裝組件的話(huà),那么組件的功能最好是將要添加埋點(diǎn)的元素包裹,這樣自定義元素也就不需要指定了,而轉為組件的屬性,然后在組件內實(shí)現事件監聽(tīng)。
  以 React 為例,我們看一下如何將上面的采集功能封裝為組件:
  import?{?useEffect,?useRef?}?from?'react';<br /><br />const?CusReport?=?(props)?=>?{<br />??const?dom?=?useRef(null);<br />??const?handelEvent?=?()?=>?{<br />????console.log(props);?//?{tag:xx,?label:xx,?trigger:xx}<br />????repoerEvents(props);<br />??};<br />??useEffect(()?=>?{<br />????if?(dom.current?instanceof?HTMLElement)?{<br />??????dom.current.addEventListener(props.trigger,?handelEvent);<br />????}<br />??},?[]);<br />??return?(<br />????<br />??????{props.children}<br />????<br />??);<br />};<br /><br />export?default?CusReport;<br />
  組件使用方式如下:
  <br />??測試<br /><br />
  這樣就比較優(yōu)雅了,不需要修改目標元素,只要把組件包裹在目標元素之外即可。
  總結
  本文介紹了搭建前端監控如何采集行為數據,將數據分為通用數據和特定數據兩個(gè)大類(lèi)分別處理。同時(shí)也介紹了多種上報數據的方式,不同的場(chǎng)景可以選擇不同的方式。
  其中的數據部分只介紹了實(shí)現功能的基礎字段,實(shí)際情況中可以根據自己的業(yè)務(wù)需求添加。
  許多小伙伴留言這套前端監控能否開(kāi)源,肯定是要開(kāi)源的,不過(guò)內容比較多我還在做,等到基本完善了我會(huì )發(fā)一個(gè)版本,感謝小伙伴們的關(guān)注。
  SegmentFault 思否社區小編說(shuō)
  自 2022-07-01 起 SegmentFault 思否公眾號改版啦!之后將陸續推出新的欄目和大家見(jiàn)面?。ㄕ埵媚恳源絶?)
  在「社區精選」欄目中,我們將為廣大開(kāi)發(fā)者推薦來(lái)自 SegmentFault 思否開(kāi)發(fā)者社區的優(yōu)質(zhì)技術(shù)文章,這些文章全部出自社區中充滿(mǎn)智慧的技術(shù)創(chuàng )作者哦!
  希望通過(guò)這一欄目,大家可以共同學(xué)習技術(shù)干貨,GET 新技能和各種花式技術(shù)小 Tips。
  歡迎越來(lái)越多的開(kāi)發(fā)者加入創(chuàng )作者的行列,我們將持續甄選出社區中優(yōu)質(zhì)的內容推介給更多人,讓閃閃發(fā)光的技術(shù)創(chuàng )作者們走到聚光燈下,被更多人認識。
  「社區精選」投稿郵箱:
  投稿請附上社區文章地址 查看全部

  社區精選 | 搭建前端監控,采集用戶(hù)行為的 N 種姿勢
  今天為各位帶來(lái)的是社區作者楊成功的文章,在這篇文章中他為大家介紹了搭建前端監控,采集用戶(hù)行為的 N 種姿勢。
  讓我們一起來(lái)了解吧~
  大家好,我是楊成功。
  上一篇我們詳細介紹了前端如何采集異常數據。采集異常數據是為了隨時(shí)監測線(xiàn)上項目的運行情況,發(fā)現問(wèn)題及時(shí)修復。在很多場(chǎng)景下,除了異常監控有用,收集用戶(hù)的行為數據同樣有意義。
  怎么定義行為數據?顧名思義,就是用戶(hù)在使用產(chǎn)品過(guò)程中產(chǎn)生的行為軌跡。比如去過(guò)哪幾個(gè)頁(yè)面,點(diǎn)過(guò)哪幾個(gè)按鈕,甚至在某個(gè)頁(yè)面停留了多長(cháng)時(shí)間,某個(gè)按鈕點(diǎn)擊了多少次,如果有需求都可以記錄下來(lái)。
  但是記錄行為數據是一個(gè)和業(yè)務(wù)緊密關(guān)聯(lián)的事情,不可能把每個(gè)用戶(hù)每一步操作都極其詳細的記錄下來(lái),這樣會(huì )產(chǎn)生極其龐大的數據,很顯然不現實(shí)。
  合理的做法是,根據產(chǎn)品的實(shí)際情況評估,哪個(gè)模塊哪個(gè)按鈕需要重點(diǎn)記錄,則可以采集的詳細一些;哪些模塊不需要重點(diǎn)關(guān)注,則簡(jiǎn)單記錄一下基本信息。
  根據這個(gè)邏輯,我們可以把行為數據分為兩類(lèi):
  通用數據特定數據
  下面分別介紹這兩類(lèi)數據該如何收集。
  通用數據
  在一個(gè)產(chǎn)品中,用戶(hù)最基本的行為就是切換頁(yè)面。用戶(hù)使用了哪些功能,也能從切換頁(yè)面中體現出來(lái)。因此通用數據一般是在頁(yè)面切換時(shí)產(chǎn)生,表示某個(gè)用戶(hù)訪(fǎng)問(wèn)了某個(gè)頁(yè)面。
  頁(yè)面切換對應到前端就是路由切換,可以通過(guò)監聽(tīng)路由變化來(lái)拿到新頁(yè)面的數據。Vue 在全局路由守衛中監聽(tīng)路由變化,任意路由切換都能執行這里的回調函數。
  //?Vue3?路由寫(xiě)法<br />const?router?=?createRouter({?...?})<br />router.beforeEach(to?=>?{<br />??//?to?代表新頁(yè)面的路由對象<br />??recordBehaviors(to)<br />})<br />
  React 在組件的 useEffect 中實(shí)現相同的功能。不過(guò)要注意一點(diǎn),監聽(tīng)所有路由變化,則需要所有路由都經(jīng)過(guò)這個(gè)組件,監聽(tīng)才有效果。具體的方法是配置路由時(shí)加*配置:
  import?HomePage?from?'@/pages/Home'<br />,<br />
  然后在這個(gè)組件的的 useEffect 中監聽(tīng)路由變化:
  //?HomePage.jsx<br />const?{?pathname?}?=?useLocation();<br />useEffect(()?=>?{<br />??//?路由切換這個(gè)函數觸發(fā)<br />??recordBehaviors(pathname);<br />},?[pathname]);<br />
  上面代碼中,在路由切換時(shí)都調用了recordBehaviors()方法并傳入了參數。
  Vue 傳的是一個(gè)路由對象,React 傳的是路由地址,接下來(lái)就可以在這個(gè)函數內收集數據了。
  明確了在哪里收集數據,我們還要知道收集哪些數據。收集行為數據最基本的字段如下:
  上面的字段中,應用標識、環(huán)境、版本號統稱(chēng)應用字段,用于標志數據的來(lái)源。其他字段主要分為用戶(hù),頁(yè)面,時(shí)間三類(lèi),通過(guò)這三類(lèi)數據就可以簡(jiǎn)單的判斷出一件事:誰(shuí)到過(guò)哪個(gè)頁(yè)面,并停留了多長(cháng)時(shí)間。
  應用字段的配置和獲取方式我們在上一節中講過(guò),就不做多余介紹了,獲取字段的方式都是通用的。
  下面介紹其他的幾類(lèi)數據如何獲取。
  獲取用戶(hù)信息
  現代前端應用存儲用戶(hù)信息的方式基本都是一樣的,localStorage 存一份,狀態(tài)管理里存一份。因此獲取用戶(hù)信息從這兩處的任意一處獲得即可。這里簡(jiǎn)單介紹下如何從狀態(tài)管理中獲取。
  最簡(jiǎn)單的方法,在函數recordBehaviors()所處的 js 文件中,直接導入用戶(hù)狀態(tài):
  //?從狀態(tài)管理里中導出用戶(hù)數據<br />import?{?UserStore?}?from?'@/stores';<br />let?{?user_id,?user_name?}?=?UserStore;<br />
  這里的@/stores 指向我項目中的文件 src/stores/index.ts,表示狀態(tài)管理的入口文件,使用時(shí)替換成自己項目的實(shí)際位置。實(shí)際情況中還會(huì )有用戶(hù)數據為空的問(wèn)題,這里需要單獨處理一下,方便我們在后續的數據查看中能看出分別:
  import?{?UserStore?}?from?'@/stores';<br /><br />//?收集行為函數<br />const?recordBehaviors?=?()=>?{<br />??let?report_date?=?{<br />????...<br />??}<br />??if(UserStore)?{<br />????let?{?user_id,?user_name}?=?UserStore<br />????report_date.user_id?=?user_id?||?0<br />????report_date.user_name?=?user_name?||?'未命名'<br />??}?else?{<br />????report_date.user_id?=?user_id?||?-1<br />????report_date.user_name?=?user_name?||?'未獲取'<br />??}<br />}<br />
  上面代碼中,首先判斷了狀態(tài)管理中是否有用戶(hù)數據,如果有則獲取,沒(méi)有則指定默認值。這里指定默認值的細節要注意,不是隨便指定的,比如 user_id 的默認值有如下意義:
  用戶(hù)數據是經(jīng)常容易出錯的地方,因為涉及到登錄狀態(tài)和權限等復雜問(wèn)題。指定了上述默認值后,就可以從收集到的行為數據中判斷出某個(gè)頁(yè)面用戶(hù)狀態(tài)是否正常。
  
  獲取頁(yè)面信息
  前面我們在監聽(tīng)路由變化的地方調用了 recordBehaviors 函數并傳入了參數,頁(yè)面信息可以從參數中拿到,我們先看在 Vue 中怎么獲?。?br />   //?路由配置<br />{<br />??path:?'/test',<br />??meta:?{<br />????title:?'測試頁(yè)面'<br />??},<br />??component:?()?=>?import('@/views/test/Index.vue')<br />}<br /><br />//?獲取配置<br />const?recordBehaviors?=?(to)=>?{<br />??let?page_route?=?to.path<br />??let?page_title?=?to.meta.title<br />}<br />
  Vue 中比較簡(jiǎn)單,可以直接從參數中拿到頁(yè)面數據。相比之下,React 的參數只是一個(gè)路由地址,想拿到頁(yè)面名稱(chēng)還需要做單獨處理。
  一般在設計權限時(shí),我們會(huì )在服務(wù)端會(huì )維護一套路由數據,包含路由地址和名稱(chēng)。路由數據在登錄后獲取,存在狀態(tài)管理中,那么有了 pathname 就可以從路由數據中找到對應的路由名稱(chēng)。
  //?React?中<br />import?{?RouteStore?}?from?'@/stores';<br /><br />const?recordBehaviors?=?(pathname)?=>?{<br />??let?{?routers?}?=?RouteStore;?//?取出路由數據<br />??let?route?=?routers.find((row)?=>?(row.path?=?pathname));<br />??if?(route)?{<br />????let?page_route?=?route.path;<br />????let?page_title?=?route.title;<br />??}<br />};<br />
  這樣,頁(yè)面信息的 page_route、page_title 兩個(gè)字段也拿到了。
  設置時(shí)間
  行為數據中用兩個(gè)字段start_at、end_at分別表示用戶(hù)進(jìn)入頁(yè)面和離開(kāi)頁(yè)面的時(shí)間。這兩個(gè)字段非常重要,我們在后續使用數據的時(shí)候可以判斷出很多信息,比如:
  還有很多信息,都能根據這兩個(gè)時(shí)間字段判斷。開(kāi)始時(shí)間很好辦,函數觸發(fā)時(shí)直接獲取當前時(shí)間:
  <br />var?start_at?=?new?Date();<br />
  結束時(shí)間這里需要考慮的情況比較多。首先要確定數據什么時(shí)候上報?用戶(hù)進(jìn)入頁(yè)面后上報,還是離開(kāi)頁(yè)面時(shí)上報?
  如果進(jìn)入頁(yè)面時(shí)上報,可以保證行為數據一定會(huì )被記錄,不會(huì )丟失,但此時(shí) end_at 字段必然為空。這樣的話(huà),就需要在離開(kāi)頁(yè)面時(shí)再調接口,將這條記錄的 end_time 更新,這種方式的實(shí)現比較麻煩一些:
  //?進(jìn)入頁(yè)面時(shí)調用<br />const?recordBehaviors?=?()?=>?{<br />??let?report_date?=?{...}?//?此時(shí)?end_at?為空<br />??http.post('/behaviors/insert',?report_date).then(res=>?{<br />????let?id?=?res.id?//?數據?id<br />????localStorage.setItem('CURRENT_BEHAVIOR_ID',?id)<br />??})<br />}<br /><br />//?離開(kāi)頁(yè)面時(shí)調用:<br />const?updateBehaviors?=?()=>?{<br />??let?id?=?localStorage.getItem('CURRENT_BEHAVIOR_ID')<br />??let?end_at?=?new?Date()<br />??http.post('/behaviors/update/'+id,?end_at)?//?根據?id?更新結束時(shí)間<br />??localStorage.removeItem('CURRENT_BEHAVIOR_ID')<br />}<br />
  上面代碼中,進(jìn)入頁(yè)面先上報數據,并保存下 id,離開(kāi)頁(yè)面再根據 id 更新這條數據的結束時(shí)間。
  如果在離開(kāi)頁(yè)面時(shí)上報,那么就要保證離開(kāi)頁(yè)面前上報接口已經(jīng)觸發(fā),否則會(huì )導致數據丟失。在滿(mǎn)足這個(gè)前提條件下,上報邏輯會(huì )變成這樣:
  //?進(jìn)入頁(yè)面時(shí)調用<br />const?recordBehaviors?=?()?=>?{<br />??let?report_date?=?{...}?//?此時(shí)?end_at?為空<br />??localStorage.setItem('CURRENT_BEHAVIOR',?JSON.stringify(report_date));<br />}<br /><br />//?離開(kāi)頁(yè)面時(shí)調用<br />const?reportBehaviors?=?()?=>?{<br />??let?end_at?=?new?Date()<br />??let?report_str?=?localStorage.getItem('CURRENT_BEHAVIOR')<br />??if(report_str)?{<br />????let?report_date?=?JSON.parse(report_str)<br />????report_date.end_at?=?end_at<br />????http.post('/behaviors/insert',?report_date)<br />??}?else?{<br />????console.log('無(wú)行為數據')<br />??}<br />}<br />
  對比一下這兩種方案,第一種的弊端是接口需要調兩次,這會(huì )使接口請求量倍增。第二種方案只調用一次,但是需要特別注意可靠性處理,總體來(lái)說(shuō)第二種方案更好些。
  特定數據
  除了通用數據,大部分情況我們還要在具體的頁(yè)面中收集某些特定的行為。比如某個(gè)關(guān)鍵的按鈕有沒(méi)有點(diǎn)擊,點(diǎn)了多少次;或者某個(gè)關(guān)鍵區域用戶(hù)有沒(méi)有看到,看到(曝光)了多少次等等。
  收集數據還有一個(gè)更專(zhuān)業(yè)的叫法 ———— 埋點(diǎn)。直觀(guān)理解是,哪里需要上報數據,就埋一個(gè)上報函數進(jìn)去。
  通用數據針對所有頁(yè)面自動(dòng)收集,特定數據就需要根據每個(gè)頁(yè)面的實(shí)際需求手動(dòng)添加。以一個(gè)按鈕為例:
  點(diǎn)擊;<br />const?onClick?=?(e)?=>?{<br />??//?console.log(e);<br />??repoerEvents(e);<br />};<br />
  上面代碼中,我們想記錄這個(gè)按鈕的點(diǎn)擊情況,所以做了一個(gè)簡(jiǎn)單的埋點(diǎn) ———— 在按鈕點(diǎn)擊事件中調用 repoerEvents()方法,這個(gè)方法內部會(huì )收集數據并上報。
  這是最原始的埋點(diǎn)方式,直接將上報方法放到事件函數中。repoerEvents() 方法接收一個(gè)事件對象參數,在參數中獲取需要上報的事件數據。
  特定數據與通用數據的許多字段是一樣的,收集特定數據需要的基本字段如下:
  這些基本字段中,前 7 個(gè)字段與前面通用數據的獲取完全一樣,這里就不贅述了。實(shí)際上特定數據需要獲取的專(zhuān)有字段只有 3 個(gè):
  這三個(gè)字段也非常容易獲取。event_type 表示事件觸發(fā)的類(lèi)型,比如點(diǎn)擊、滾動(dòng)、拖動(dòng)等,可以在事件對象中拿到。action_tag 和 action_label 是必須指定的屬性,表示本次埋點(diǎn)的標識和文字描述,用于在后續的數據處理時(shí)方便查閱和統計。
  了解了采集特定數據是怎么回事,接下來(lái)我們用代碼實(shí)現。
  手動(dòng)埋點(diǎn)上報
  假設要為登錄按鈕做埋點(diǎn),按照上面的數據采集方式,我們書(shū)寫(xiě)代碼如下:
  <br />??登錄<br />;<br />const?onClick?=?(e)?=>?{<br />??//?console.log(e);<br />??repoerEvents(e);<br />};<br />
  代碼中,我們通過(guò)元素的自定義屬性傳遞了 tag 和 label 兩個(gè)標識,用于在上報函數中獲取。
  上報函數 repoerEvents() 代碼邏輯如下:
  
  //?埋點(diǎn)上報函數<br />const?repoerEvents?=?(e)=>?{<br />??let?report_date?=?{...}<br />??let?{?tag,?label?}?=?e.target.dataset<br />??if(!tag?||?!label)?{<br />????return?new?Error('上報元素屬性缺失')<br />??}<br />??report_date.event_type?=?e.type<br />??report_date.action_tag?=?tag<br />??report_date.action_label?=?label<br /><br />??//?上報數據<br />??http.post('/events/insert',?report_date)<br />}<br />
  這樣就實(shí)現了一個(gè)基本的特定數據埋點(diǎn)上報功能。
  全局自動(dòng)上報
  現在我們回過(guò)頭來(lái)梳理一下這個(gè)上報流程,雖然基本功能實(shí)現了,但是還有些不合理之處,比如:
  首先我們的埋點(diǎn)方式是基于事件的,也就是說(shuō),不管元素本身是否需要事件處理,我們都要給他加上,并在函數內部調用 repoerEvents() 方法。如果一個(gè)項目需要埋點(diǎn)的地方非常多,這種方式的接入成本就會(huì )非常高。
  參考之前做異常監控的邏輯,我們換一個(gè)思路:能否全局監聽(tīng)事件自動(dòng)上報呢?
  思考一下,如果要做全局監聽(tīng)事件,那么只能監聽(tīng)需要埋點(diǎn)的元素的事件。那么如何判斷哪些元素需要埋點(diǎn)呢?
  上面我們?yōu)槁顸c(diǎn)的元素指定了data-tag和data-label兩個(gè)自定義屬性,那是不是根據這兩個(gè)自定義屬性判斷就可以?我們來(lái)試驗一下:
  window.addEventListener('click',?(event)?=>?{<br />??let?{?tag,?label,?trigger?}?=?event.target.dataset;<br />??if?(tag?&&?label?&&?trigger?==?'click')?{<br />????//?說(shuō)明該元素需要埋點(diǎn)<br />????repoerEvents(event);<br />??}<br />});<br />
  上面代碼還多判斷了一個(gè)自定義屬性 dataset.trigger,表示元素在哪種事件觸發(fā)時(shí)需要上報。全局監聽(tīng)事件需要這個(gè)標識,這樣可避免事件沖突。
  添加全局監聽(tīng)后,收集某個(gè)元素的特定數據就簡(jiǎn)單了,方法如下:
  <br />??保存<br /><br />
  試驗證明,上述全局處理的方式是可行的,這樣的話(huà)就不需要在每一個(gè)元素上添加或修改事件處理函數了,只需要在元素中添加三個(gè)自定義屬性 data-tag,data-label,data-trigger 就能自動(dòng)實(shí)現數據埋點(diǎn)上報。
  組件上報
  上面全局監聽(tīng)事件上報的方式已經(jīng)比手動(dòng)埋點(diǎn)高效了許多,現在我們再換一個(gè)場(chǎng)景。
  一般情況下當埋點(diǎn)功能成熟之后,會(huì )封裝成一個(gè) SDK 供其他項目使用。如果我們將采集數據按照 SDK 的思路實(shí)現,讓開(kāi)發(fā)者在全局監聽(tīng)事件,是不是一個(gè)好的方式呢?
  顯然是不太友好的。如果是一個(gè) SDK,那么最好的方式是將所有內容聚合成一個(gè)組件,在組件內實(shí)現上報的所有功能,而不是讓使用者在項目中添加監聽(tīng)事件。
  封裝組件的話(huà),那么組件的功能最好是將要添加埋點(diǎn)的元素包裹,這樣自定義元素也就不需要指定了,而轉為組件的屬性,然后在組件內實(shí)現事件監聽(tīng)。
  以 React 為例,我們看一下如何將上面的采集功能封裝為組件:
  import?{?useEffect,?useRef?}?from?'react';<br /><br />const?CusReport?=?(props)?=>?{<br />??const?dom?=?useRef(null);<br />??const?handelEvent?=?()?=>?{<br />????console.log(props);?//?{tag:xx,?label:xx,?trigger:xx}<br />????repoerEvents(props);<br />??};<br />??useEffect(()?=>?{<br />????if?(dom.current?instanceof?HTMLElement)?{<br />??????dom.current.addEventListener(props.trigger,?handelEvent);<br />????}<br />??},?[]);<br />??return?(<br />????<br />??????{props.children}<br />????<br />??);<br />};<br /><br />export?default?CusReport;<br />
  組件使用方式如下:
  <br />??測試<br /><br />
  這樣就比較優(yōu)雅了,不需要修改目標元素,只要把組件包裹在目標元素之外即可。
  總結
  本文介紹了搭建前端監控如何采集行為數據,將數據分為通用數據和特定數據兩個(gè)大類(lèi)分別處理。同時(shí)也介紹了多種上報數據的方式,不同的場(chǎng)景可以選擇不同的方式。
  其中的數據部分只介紹了實(shí)現功能的基礎字段,實(shí)際情況中可以根據自己的業(yè)務(wù)需求添加。
  許多小伙伴留言這套前端監控能否開(kāi)源,肯定是要開(kāi)源的,不過(guò)內容比較多我還在做,等到基本完善了我會(huì )發(fā)一個(gè)版本,感謝小伙伴們的關(guān)注。
  SegmentFault 思否社區小編說(shuō)
  自 2022-07-01 起 SegmentFault 思否公眾號改版啦!之后將陸續推出新的欄目和大家見(jiàn)面?。ㄕ埵媚恳源絶?)
  在「社區精選」欄目中,我們將為廣大開(kāi)發(fā)者推薦來(lái)自 SegmentFault 思否開(kāi)發(fā)者社區的優(yōu)質(zhì)技術(shù)文章,這些文章全部出自社區中充滿(mǎn)智慧的技術(shù)創(chuàng )作者哦!
  希望通過(guò)這一欄目,大家可以共同學(xué)習技術(shù)干貨,GET 新技能和各種花式技術(shù)小 Tips。
  歡迎越來(lái)越多的開(kāi)發(fā)者加入創(chuàng )作者的行列,我們將持續甄選出社區中優(yōu)質(zhì)的內容推介給更多人,讓閃閃發(fā)光的技術(shù)創(chuàng )作者們走到聚光燈下,被更多人認識。
  「社區精選」投稿郵箱:
  投稿請附上社區文章地址

實(shí)戰 | SRC信息收集思路總結

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 100 次瀏覽 ? 2022-06-18 20:11 ? 來(lái)自相關(guān)話(huà)題

  實(shí)戰 | SRC信息收集思路總結
  說(shuō)到信息收集,網(wǎng)上已經(jīng)有許多文章進(jìn)行描述了,那么從正常的子域名、端口、旁站、C段等進(jìn)行信息收集的話(huà),對于正常項目已經(jīng)夠用了,但是挖掘SRC的話(huà),在諸多競爭對手的“幫助”下,大家收集到的信息都差不多,挖掘的漏洞也往往存在重復的情況。
  那么現在我就想分享一下平時(shí)自己進(jìn)行SRC挖掘過(guò)程中,主要是如何進(jìn)行入手的。以下均為小弟拙見(jiàn),大佬勿噴。
  0x01 確定目標
  無(wú)目標隨便打,有沒(méi)有自己對應的SRC應急響應平臺不說(shuō),還往往會(huì )因為一開(kāi)始沒(méi)有挖掘到漏洞而隨意放棄,這樣往往不能挖掘到深層次的漏洞。挖到的大多數是大家都可以簡(jiǎn)單挖到的漏洞,存在大概率重復可能。所以在真的想要花點(diǎn)時(shí)間在SRC漏洞挖掘上的話(huà),建議先選好目標。
  那么目標怎么選呢,考慮到收益回報與付出的比例來(lái)看,建議是從專(zhuān)屬SRC入手,特別在一些活動(dòng)中,可以獲取比平時(shí)更高的收益。
  微信搜一搜:
  
  百度搜一搜:
  現在有活動(dòng)的src已經(jīng)浮現水面了,那么我們就可與從中選擇自己感興趣的SRC。
  0x02 確認測試范圍
  前面說(shuō)到確定測什么SRC,那么下面就要通過(guò)一些方法,獲取這個(gè)SRC的測試范圍,以免測偏。
  1、公眾號
  從公眾號推文入手,活動(dòng)頁(yè)面中可以發(fā)現測試范圍
  2、應急響應官網(wǎng)
  在應急響應官網(wǎng),往往會(huì )有一些活動(dòng)的公告,在里面可以獲取到相應的測試范圍。
  3、愛(ài)企查
  從愛(ài)企查等商業(yè)查詢(xún)平臺獲取公司所屬域名
  
  搜索想要測試等SRC所屬公司名稱(chēng),在知識產(chǎn)權->網(wǎng)站備案中可以獲取測試范圍。
  0x03 子域名(oneforall)
  拿到域名之后,下一步我考慮使用oneforall掃描獲取子域名,就像網(wǎng)上信息收集的文章一樣,主域名的站點(diǎn)不是靜態(tài)界面就是安全防護等級極強,不是隨便就能夠發(fā)現漏洞的,我們挖掘SRC也是要從子域名開(kāi)始,從邊緣資產(chǎn)或一般資產(chǎn)中發(fā)現漏洞。
  工具下載:
  https://github.com/shmilylty/OneForAll
  具體用法如下:
  常用的獲取子域名有2種選擇,一種使用--target指定單個(gè)域名,一種使用--targets指定域名文件。
  python3 oneforall.py --target example.com run<br />python3 oneforall.py --targets ./domains.txt run
  其他獲取子域名的工具還有layer子域名挖掘機、Sublist3r、證書(shū)透明度、在線(xiàn)工具等,這里就不一一闡述了,大體思路是一樣等,獲取子域,然后從中篩選邊緣資產(chǎn),安全防護低資產(chǎn)。
  0x04 系統指紋探測
  通過(guò)上面的方法,我們可以在/OneForAll-0.4.3/results/路徑下獲取以域名為名字的csv文件。里面放入到便是掃描到到所有子域名以及相應信息了。
  下一步便是將收集到到域名全部進(jìn)行一遍指紋探測,從中找出一些明顯使用CMS、OA系統、shiro、Fastjson等的站點(diǎn)。下面介紹平時(shí)使用的2款工具:
  1、Ehole
  下載地址:
  https://github.com/EdgeSecurityTeam/EHole
  使用方法:
  ./Ehole-darwin -l url.txt //URL地址需帶上協(xié)議,每行一個(gè)<br />./Ehole-darwin -f 192.168.1.1/24 //支持單IP或IP段,fofa識別需要配置fofa密鑰和郵箱<br />./Ehole-darwin -l url.txt -json export.json //結果輸出至export.json文件
  2、Glass
  下載地址:
  https://github.com/s7ckTeam/Glass
  使用方法:
  python3 Glass.py -u http://www.examples.com // 單url測試<br />python3 Glass.py -w domain.txt -o 1.txt // url文件內
  0x05 框架型站點(diǎn)漏洞測試
  前面經(jīng)過(guò)了子域名收集以及對收集到的子域名進(jìn)行了指紋信息識別之后,那么對于框架型的站點(diǎn),我們可以?xún)?yōu)先進(jìn)行測試。
  類(lèi)似用友NC、通達OA、藍凌OA等,可以通過(guò)嘗試現有的Nday漏洞進(jìn)行攻擊。
  
  0x06 非框架型站點(diǎn)漏洞測試
  前面測試完框架型的站點(diǎn)了,之后就應該往正常網(wǎng)站,或者經(jīng)過(guò)了二開(kāi)未能直接檢測出指紋的站點(diǎn)進(jìn)行滲透了。那么對于這類(lèi)站點(diǎn),最經(jīng)常遇到的便是登錄框,在這里,我們便可以開(kāi)始測試了。
  
  1、用戶(hù)名枚舉
  抓包嘗試是否用戶(hù)名存在與不存在的情況,返回結果不同。
  2、驗證碼
  是否存在驗證碼,驗證碼是否可以抓包截斷繞過(guò),驗證碼是否可以為空。
  3、暴力破解
  下面是我收集的集中常見(jiàn)的用戶(hù)名
  1.弱口令用戶(hù)名如admin,test,ceshi等<br />2.員工姓名全拼,員工姓名簡(jiǎn)拼<br />3.公司特征+員工工號/員工姓名<br />4.員工工號+姓名簡(jiǎn)拼<br />5.員工姓名全拼+員工工號<br />6.員工姓名全拼+重復次數,如zhangsan和zhangsan01<br />7.其他
  關(guān)于暴力破解我要扯一句了,就是關(guān)于密碼字典的問(wèn)題。經(jīng)常會(huì )聽(tīng)到某人說(shuō)他的字典多么多么的大,有好幾個(gè)G之類(lèi)的,但是在我覺(jué)得,這很沒(méi)有必要,有些密碼是你跑幾天都跑不出來(lái)的,就算字典確實(shí)夠大,也沒(méi)有必要這樣跑,可能影響心情不說(shuō),大規模地暴力破解,很容易讓人覺(jué)得你在拒絕服務(wù)攻擊。
  其實(shí)我的話(huà)一般跑一跑弱口令就差不多了。
  關(guān)于弱口令字典的問(wèn)題,我也想說(shuō)一嘴,你最好看看,你字典里面的admin、123456、password處在什么位置。記得之前玩CTF的時(shí)候,默認密碼123456,但是那個(gè)師傅死活做不出來(lái),后面一看,字典里面居然沒(méi)有123456這個(gè)密碼。。。
  這里推薦一個(gè)字典,個(gè)人感覺(jué)還是挺好用的。當然更多的是需要自己不斷更新。
  https://github.com/fuzz-security/SuperWordlist
  4、工具cupp和cewl
  對于一些情況,密碼不是直接使用弱口令,而是通過(guò)一些公司的特征+個(gè)人信息制作的,那么這個(gè)時(shí)候,我們的字典便不能直接使用了,需要在這之前加上一些特征,例如阿里SRC可能是a;百度SRC可能是bd等。
  下面2款kali自帶等工具,可以通過(guò)收集信息,生成好用的字典,方便滲透。說(shuō)真的,在滲透測試過(guò)程中,弱口令,YYDS!
  具體使用說(shuō)明和工具介紹,可以查看文章:
  5、自行注冊
  如果能夠注冊那就好辦了,自己注冊一下賬戶(hù)即可。
  6、小總結
  對于非框架的站點(diǎn),登錄接口一般是必不可少的,可能就在主頁(yè),也可能在某個(gè)路徑下,藏著(zhù)后臺的登錄接口,在嘗試了多種方法成功登錄之后,記得嘗試里面是否存在未授權漏洞、越權等漏洞。
  這里借用來(lái)自WS師傅的建議:可以直接掃描出來(lái)的洞,基本都被交完了,可以更多往邏輯漏洞方面找。登錄后的漏洞重復率,比登錄前的往往會(huì )低很多。
  0x07 端口掃描
  前面就是正常的滲透了,那么一個(gè)域名只是在80、443端口才有web服務(wù)嗎?不可否認有些時(shí)候真的是,但是絕大多數情況下,類(lèi)似8080、8443、8081、8089、7001等端口,往往會(huì )有驚喜哦~
  端口掃描也算是老生常談了,市面上也有很多介紹端口掃描的工具使用方法,這里也不細說(shuō)了,就放出平時(shí)使用的命令吧。
  sudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v examples.comsudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -p 1-65535 examples.com
  0x08 目錄掃描dirsearch
  目錄掃描在滲透測試過(guò)程中我認為是必不可少的,一個(gè)站點(diǎn)在不同目錄下的不同文件,往往可能有驚喜哦。
  個(gè)人是喜歡使用dirserach這款工具,不僅高效、頁(yè)面也好看。市面上還有例如御劍、御劍t00ls版等,也是不錯的選擇。
  dirsearch下載地址:
  https://github.com/maurosoria/dirsearch
  具體使用方法可以查看github介紹,這里我一般是使用如下命令(因為擔心線(xiàn)程太高所以通過(guò)-t參數設置為2)
  python3 dirsearch.py -u www.xxx.com -e * -t 2
  關(guān)鍵的地方是大家都可以下載這款工具,獲取它自帶的字典,那么路徑的話(huà),便是大家都能夠搜得到的了,所以這里我推薦是可以適當整合一些師傅們發(fā)出來(lái)的路徑字典到/dirsearch-0.4.2/db/dicc.txt中。例如我的話(huà),是增加了springboot未授權的一些路徑、swagger的路徑以及一些例如vmvare-vcenter的漏洞路徑。
  
  0x09 JS信息收集
  在一個(gè)站點(diǎn)掃描了目錄、嘗試登錄失敗并且沒(méi)有自己注冊功能的情況下,我們還可以從JS文件入手,獲取一些URL,也許某個(gè)URL便能夠未授權訪(fǎng)問(wèn)獲取敏感信息呢。
  1、JSFinder
  工具下載:
  JSFinder是一款用作快速在網(wǎng)站的js文件中提取URL,子域名的工具。個(gè)人覺(jué)得美中不足的地方便是不能對獲取到到URL進(jìn)行一些過(guò)濾,在某些情況下,JS文件中可以爬取非常多的URL,這其中可能大部分是頁(yè)面空或者返回200但是頁(yè)面顯示404的。來(lái)自HZ師傅的建議,可以修改一下工具,基于當前的基礎上,檢測獲取的URL是否可以訪(fǎng)問(wèn),訪(fǎng)問(wèn)后的頁(yè)面大小為多少,標題是什么。。。
  思路放這了,找個(gè)時(shí)間改一改?
  #檢測URL狀態(tài)碼#-----------------------#! /usr/bin/env python#coding=utf-8import sysimport requestsurl='xxxx'request = requests.get(url)httpStatusCode = request.status_codeif httpStatusCode == 200: xxxxelse: xxxx
  #檢測URL返回包大小#-----------------------import requestsdef hum_convert(value): units = ["B", "KB", "MB", "GB", "TB", "PB"] size = 1024.0 for i in range(len(units)): if (value / size) < 1: return "%.2f%s" % (value, units[i]) value = value / sizer = requests.get('https://www.baidu.com')r.status_coder.headerslength = len(r.text)print(hum_convert(length))
  #獲取網(wǎng)站標題#-----------------------#!/usr/bin/python#coding=utf-8urllib.requestimport urllib.requestimport reurl = urllib.request.urlopen('http://www.xxx.com')html = url.read().decode('utf-8')title=re.findall('(.+)',html)print (title)
  2、JS文件
  JS文件與HTML、CSS等文件統一作為前端文件,是可以通過(guò)瀏覽器訪(fǎng)問(wèn)到的,相對于HTML和CSS等文件的顯示和美化作用,JS文件將會(huì )能夠將頁(yè)面的功能點(diǎn)進(jìn)行升華。
  
  對于滲透測試來(lái)說(shuō),JS文件不僅僅能夠找到一些URL、內網(wǎng)IP地址、手機號、調用的組件版本等信息,還存在一些接口,因為前端需要,所以一些接口將會(huì )在JS文件中直接或間接呈現。下面我將介紹如何發(fā)現這些隱藏的接口。
  1、首先在某個(gè)頁(yè)面中,鼠標右鍵,選擇檢查
  2、點(diǎn)擊Application
  
  3、在Frames->top->Scripts中能夠獲取當前頁(yè)面請求到的所有JS
  
  4、火狐瀏覽器的話(huà),則是在調試中
  
  5、如果你請求的JS文件內容都疊在了前幾行的話(huà),下面這個(gè)鍵可以幫你美化輸出
  6、在JS文件中,可以尤為注意帶有api字眼的文件或內容,例如下面這里我發(fā)現了一個(gè)接口。
  
  0xA小程序、APP
  web端沒(méi)有思路的時(shí)候,可以結合小程序、APP來(lái)進(jìn)行滲透。小程序或APP的服務(wù)端其實(shí)可以在一定程度上與web應用的服務(wù)端相聯(lián)系。也就是說(shuō),我們在小程序或者APP上,一樣能夠挖掘web端的漏洞如SQL注入、XSS等,并且相對來(lái)說(shuō),這類(lèi)等服務(wù)端安全措施會(huì )相對沒(méi)有那么完備,所以在web端確實(shí)沒(méi)有思路的時(shí)候,可以迂回滲透,從小程序、APP中進(jìn)行。
  #小程序抓包、APP抓包參考鏈接:<br />https://mp.weixin.qq.com/s/xuo ... %3Bbr />https://mp.weixin.qq.com/s/45Y ... %3Bbr />https://mp.weixin.qq.com/s/M5x ... %3Bbr />https://mp.weixin.qq.com/s/Mfkbxtrxv5AvY-n_bMU7ig
  0xB總結
  以上就是我個(gè)人挖掘SRC的一些信息收集思路,挖掘SRC有的時(shí)候真的很看運氣,也許別人對一個(gè)接口簡(jiǎn)單Fuzz,便出了一個(gè)注入,而我們花了幾天,還是一直看到返回內容為404。所以有的時(shí)候真的可以換個(gè)站試試,也許就挖到高危甚至嚴重了~
  作為一名SRC小白,以上內容均為小弟拙見(jiàn),希望能夠通過(guò)這篇文章,幫到更多的網(wǎng)絡(luò )安全小白,沒(méi)能幫上大佬們真的很抱歉~后續也會(huì )持續提高自己,將學(xué)到的更多的東西分享給大家。
  0XC 推薦一個(gè)網(wǎng)站
  
  有SRC的廠(chǎng)商列表,可以自己去專(zhuān)屬的SRC提交漏洞
   查看全部

  實(shí)戰 | SRC信息收集思路總結
  說(shuō)到信息收集,網(wǎng)上已經(jīng)有許多文章進(jìn)行描述了,那么從正常的子域名、端口、旁站、C段等進(jìn)行信息收集的話(huà),對于正常項目已經(jīng)夠用了,但是挖掘SRC的話(huà),在諸多競爭對手的“幫助”下,大家收集到的信息都差不多,挖掘的漏洞也往往存在重復的情況。
  那么現在我就想分享一下平時(shí)自己進(jìn)行SRC挖掘過(guò)程中,主要是如何進(jìn)行入手的。以下均為小弟拙見(jiàn),大佬勿噴。
  0x01 確定目標
  無(wú)目標隨便打,有沒(méi)有自己對應的SRC應急響應平臺不說(shuō),還往往會(huì )因為一開(kāi)始沒(méi)有挖掘到漏洞而隨意放棄,這樣往往不能挖掘到深層次的漏洞。挖到的大多數是大家都可以簡(jiǎn)單挖到的漏洞,存在大概率重復可能。所以在真的想要花點(diǎn)時(shí)間在SRC漏洞挖掘上的話(huà),建議先選好目標。
  那么目標怎么選呢,考慮到收益回報與付出的比例來(lái)看,建議是從專(zhuān)屬SRC入手,特別在一些活動(dòng)中,可以獲取比平時(shí)更高的收益。
  微信搜一搜:
  
  百度搜一搜:
  現在有活動(dòng)的src已經(jīng)浮現水面了,那么我們就可與從中選擇自己感興趣的SRC。
  0x02 確認測試范圍
  前面說(shuō)到確定測什么SRC,那么下面就要通過(guò)一些方法,獲取這個(gè)SRC的測試范圍,以免測偏。
  1、公眾號
  從公眾號推文入手,活動(dòng)頁(yè)面中可以發(fā)現測試范圍
  2、應急響應官網(wǎng)
  在應急響應官網(wǎng),往往會(huì )有一些活動(dòng)的公告,在里面可以獲取到相應的測試范圍。
  3、愛(ài)企查
  從愛(ài)企查等商業(yè)查詢(xún)平臺獲取公司所屬域名
  
  搜索想要測試等SRC所屬公司名稱(chēng),在知識產(chǎn)權->網(wǎng)站備案中可以獲取測試范圍。
  0x03 子域名(oneforall)
  拿到域名之后,下一步我考慮使用oneforall掃描獲取子域名,就像網(wǎng)上信息收集的文章一樣,主域名的站點(diǎn)不是靜態(tài)界面就是安全防護等級極強,不是隨便就能夠發(fā)現漏洞的,我們挖掘SRC也是要從子域名開(kāi)始,從邊緣資產(chǎn)或一般資產(chǎn)中發(fā)現漏洞。
  工具下載:
  https://github.com/shmilylty/OneForAll
  具體用法如下:
  常用的獲取子域名有2種選擇,一種使用--target指定單個(gè)域名,一種使用--targets指定域名文件。
  python3 oneforall.py --target example.com run<br />python3 oneforall.py --targets ./domains.txt run
  其他獲取子域名的工具還有layer子域名挖掘機、Sublist3r、證書(shū)透明度、在線(xiàn)工具等,這里就不一一闡述了,大體思路是一樣等,獲取子域,然后從中篩選邊緣資產(chǎn),安全防護低資產(chǎn)。
  0x04 系統指紋探測
  通過(guò)上面的方法,我們可以在/OneForAll-0.4.3/results/路徑下獲取以域名為名字的csv文件。里面放入到便是掃描到到所有子域名以及相應信息了。
  下一步便是將收集到到域名全部進(jìn)行一遍指紋探測,從中找出一些明顯使用CMS、OA系統、shiro、Fastjson等的站點(diǎn)。下面介紹平時(shí)使用的2款工具:
  1、Ehole
  下載地址:
  https://github.com/EdgeSecurityTeam/EHole
  使用方法:
  ./Ehole-darwin -l url.txt //URL地址需帶上協(xié)議,每行一個(gè)<br />./Ehole-darwin -f 192.168.1.1/24 //支持單IP或IP段,fofa識別需要配置fofa密鑰和郵箱<br />./Ehole-darwin -l url.txt -json export.json //結果輸出至export.json文件
  2、Glass
  下載地址:
  https://github.com/s7ckTeam/Glass
  使用方法:
  python3 Glass.py -u http://www.examples.com // 單url測試<br />python3 Glass.py -w domain.txt -o 1.txt // url文件內
  0x05 框架型站點(diǎn)漏洞測試
  前面經(jīng)過(guò)了子域名收集以及對收集到的子域名進(jìn)行了指紋信息識別之后,那么對于框架型的站點(diǎn),我們可以?xún)?yōu)先進(jìn)行測試。
  類(lèi)似用友NC、通達OA、藍凌OA等,可以通過(guò)嘗試現有的Nday漏洞進(jìn)行攻擊。
  
  0x06 非框架型站點(diǎn)漏洞測試
  前面測試完框架型的站點(diǎn)了,之后就應該往正常網(wǎng)站,或者經(jīng)過(guò)了二開(kāi)未能直接檢測出指紋的站點(diǎn)進(jìn)行滲透了。那么對于這類(lèi)站點(diǎn),最經(jīng)常遇到的便是登錄框,在這里,我們便可以開(kāi)始測試了。
  
  1、用戶(hù)名枚舉
  抓包嘗試是否用戶(hù)名存在與不存在的情況,返回結果不同。
  2、驗證碼
  是否存在驗證碼,驗證碼是否可以抓包截斷繞過(guò),驗證碼是否可以為空。
  3、暴力破解
  下面是我收集的集中常見(jiàn)的用戶(hù)名
  1.弱口令用戶(hù)名如admin,test,ceshi等<br />2.員工姓名全拼,員工姓名簡(jiǎn)拼<br />3.公司特征+員工工號/員工姓名<br />4.員工工號+姓名簡(jiǎn)拼<br />5.員工姓名全拼+員工工號<br />6.員工姓名全拼+重復次數,如zhangsan和zhangsan01<br />7.其他
  關(guān)于暴力破解我要扯一句了,就是關(guān)于密碼字典的問(wèn)題。經(jīng)常會(huì )聽(tīng)到某人說(shuō)他的字典多么多么的大,有好幾個(gè)G之類(lèi)的,但是在我覺(jué)得,這很沒(méi)有必要,有些密碼是你跑幾天都跑不出來(lái)的,就算字典確實(shí)夠大,也沒(méi)有必要這樣跑,可能影響心情不說(shuō),大規模地暴力破解,很容易讓人覺(jué)得你在拒絕服務(wù)攻擊。
  其實(shí)我的話(huà)一般跑一跑弱口令就差不多了。
  關(guān)于弱口令字典的問(wèn)題,我也想說(shuō)一嘴,你最好看看,你字典里面的admin、123456、password處在什么位置。記得之前玩CTF的時(shí)候,默認密碼123456,但是那個(gè)師傅死活做不出來(lái),后面一看,字典里面居然沒(méi)有123456這個(gè)密碼。。。
  這里推薦一個(gè)字典,個(gè)人感覺(jué)還是挺好用的。當然更多的是需要自己不斷更新。
  https://github.com/fuzz-security/SuperWordlist
  4、工具cupp和cewl
  對于一些情況,密碼不是直接使用弱口令,而是通過(guò)一些公司的特征+個(gè)人信息制作的,那么這個(gè)時(shí)候,我們的字典便不能直接使用了,需要在這之前加上一些特征,例如阿里SRC可能是a;百度SRC可能是bd等。
  下面2款kali自帶等工具,可以通過(guò)收集信息,生成好用的字典,方便滲透。說(shuō)真的,在滲透測試過(guò)程中,弱口令,YYDS!
  具體使用說(shuō)明和工具介紹,可以查看文章:
  5、自行注冊
  如果能夠注冊那就好辦了,自己注冊一下賬戶(hù)即可。
  6、小總結
  對于非框架的站點(diǎn),登錄接口一般是必不可少的,可能就在主頁(yè),也可能在某個(gè)路徑下,藏著(zhù)后臺的登錄接口,在嘗試了多種方法成功登錄之后,記得嘗試里面是否存在未授權漏洞、越權等漏洞。
  這里借用來(lái)自WS師傅的建議:可以直接掃描出來(lái)的洞,基本都被交完了,可以更多往邏輯漏洞方面找。登錄后的漏洞重復率,比登錄前的往往會(huì )低很多。
  0x07 端口掃描
  前面就是正常的滲透了,那么一個(gè)域名只是在80、443端口才有web服務(wù)嗎?不可否認有些時(shí)候真的是,但是絕大多數情況下,類(lèi)似8080、8443、8081、8089、7001等端口,往往會(huì )有驚喜哦~
  端口掃描也算是老生常談了,市面上也有很多介紹端口掃描的工具使用方法,這里也不細說(shuō)了,就放出平時(shí)使用的命令吧。
  sudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v examples.comsudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -p 1-65535 examples.com
  0x08 目錄掃描dirsearch
  目錄掃描在滲透測試過(guò)程中我認為是必不可少的,一個(gè)站點(diǎn)在不同目錄下的不同文件,往往可能有驚喜哦。
  個(gè)人是喜歡使用dirserach這款工具,不僅高效、頁(yè)面也好看。市面上還有例如御劍、御劍t00ls版等,也是不錯的選擇。
  dirsearch下載地址:
  https://github.com/maurosoria/dirsearch
  具體使用方法可以查看github介紹,這里我一般是使用如下命令(因為擔心線(xiàn)程太高所以通過(guò)-t參數設置為2)
  python3 dirsearch.py -u www.xxx.com -e * -t 2
  關(guān)鍵的地方是大家都可以下載這款工具,獲取它自帶的字典,那么路徑的話(huà),便是大家都能夠搜得到的了,所以這里我推薦是可以適當整合一些師傅們發(fā)出來(lái)的路徑字典到/dirsearch-0.4.2/db/dicc.txt中。例如我的話(huà),是增加了springboot未授權的一些路徑、swagger的路徑以及一些例如vmvare-vcenter的漏洞路徑。
  
  0x09 JS信息收集
  在一個(gè)站點(diǎn)掃描了目錄、嘗試登錄失敗并且沒(méi)有自己注冊功能的情況下,我們還可以從JS文件入手,獲取一些URL,也許某個(gè)URL便能夠未授權訪(fǎng)問(wèn)獲取敏感信息呢。
  1、JSFinder
  工具下載:
  JSFinder是一款用作快速在網(wǎng)站的js文件中提取URL,子域名的工具。個(gè)人覺(jué)得美中不足的地方便是不能對獲取到到URL進(jìn)行一些過(guò)濾,在某些情況下,JS文件中可以爬取非常多的URL,這其中可能大部分是頁(yè)面空或者返回200但是頁(yè)面顯示404的。來(lái)自HZ師傅的建議,可以修改一下工具,基于當前的基礎上,檢測獲取的URL是否可以訪(fǎng)問(wèn),訪(fǎng)問(wèn)后的頁(yè)面大小為多少,標題是什么。。。
  思路放這了,找個(gè)時(shí)間改一改?
  #檢測URL狀態(tài)碼#-----------------------#! /usr/bin/env python#coding=utf-8import sysimport requestsurl='xxxx'request = requests.get(url)httpStatusCode = request.status_codeif httpStatusCode == 200: xxxxelse: xxxx
  #檢測URL返回包大小#-----------------------import requestsdef hum_convert(value): units = ["B", "KB", "MB", "GB", "TB", "PB"] size = 1024.0 for i in range(len(units)): if (value / size) < 1: return "%.2f%s" % (value, units[i]) value = value / sizer = requests.get('https://www.baidu.com')r.status_coder.headerslength = len(r.text)print(hum_convert(length))
  #獲取網(wǎng)站標題#-----------------------#!/usr/bin/python#coding=utf-8urllib.requestimport urllib.requestimport reurl = urllib.request.urlopen('http://www.xxx.com')html = url.read().decode('utf-8')title=re.findall('(.+)',html)print (title)
  2、JS文件
  JS文件與HTML、CSS等文件統一作為前端文件,是可以通過(guò)瀏覽器訪(fǎng)問(wèn)到的,相對于HTML和CSS等文件的顯示和美化作用,JS文件將會(huì )能夠將頁(yè)面的功能點(diǎn)進(jìn)行升華。
  
  對于滲透測試來(lái)說(shuō),JS文件不僅僅能夠找到一些URL、內網(wǎng)IP地址、手機號、調用的組件版本等信息,還存在一些接口,因為前端需要,所以一些接口將會(huì )在JS文件中直接或間接呈現。下面我將介紹如何發(fā)現這些隱藏的接口。
  1、首先在某個(gè)頁(yè)面中,鼠標右鍵,選擇檢查
  2、點(diǎn)擊Application
  
  3、在Frames->top->Scripts中能夠獲取當前頁(yè)面請求到的所有JS
  
  4、火狐瀏覽器的話(huà),則是在調試中
  
  5、如果你請求的JS文件內容都疊在了前幾行的話(huà),下面這個(gè)鍵可以幫你美化輸出
  6、在JS文件中,可以尤為注意帶有api字眼的文件或內容,例如下面這里我發(fā)現了一個(gè)接口。
  
  0xA小程序、APP
  web端沒(méi)有思路的時(shí)候,可以結合小程序、APP來(lái)進(jìn)行滲透。小程序或APP的服務(wù)端其實(shí)可以在一定程度上與web應用的服務(wù)端相聯(lián)系。也就是說(shuō),我們在小程序或者APP上,一樣能夠挖掘web端的漏洞如SQL注入、XSS等,并且相對來(lái)說(shuō),這類(lèi)等服務(wù)端安全措施會(huì )相對沒(méi)有那么完備,所以在web端確實(shí)沒(méi)有思路的時(shí)候,可以迂回滲透,從小程序、APP中進(jìn)行。
  #小程序抓包、APP抓包參考鏈接:<br />https://mp.weixin.qq.com/s/xuo ... %3Bbr />https://mp.weixin.qq.com/s/45Y ... %3Bbr />https://mp.weixin.qq.com/s/M5x ... %3Bbr />https://mp.weixin.qq.com/s/Mfkbxtrxv5AvY-n_bMU7ig
  0xB總結
  以上就是我個(gè)人挖掘SRC的一些信息收集思路,挖掘SRC有的時(shí)候真的很看運氣,也許別人對一個(gè)接口簡(jiǎn)單Fuzz,便出了一個(gè)注入,而我們花了幾天,還是一直看到返回內容為404。所以有的時(shí)候真的可以換個(gè)站試試,也許就挖到高危甚至嚴重了~
  作為一名SRC小白,以上內容均為小弟拙見(jiàn),希望能夠通過(guò)這篇文章,幫到更多的網(wǎng)絡(luò )安全小白,沒(méi)能幫上大佬們真的很抱歉~后續也會(huì )持續提高自己,將學(xué)到的更多的東西分享給大家。
  0XC 推薦一個(gè)網(wǎng)站
  
  有SRC的廠(chǎng)商列表,可以自己去專(zhuān)屬的SRC提交漏洞
  

SRC信息收集思路分享,很有用!

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 108 次瀏覽 ? 2022-06-18 15:01 ? 來(lái)自相關(guān)話(huà)題

  SRC信息收集思路分享,很有用!
  說(shuō)到信息收集,網(wǎng)上已經(jīng)有許多文章進(jìn)行描述了,那么從正常的子域名、端口、旁站、C段等進(jìn)行信息收集的話(huà),對于正常項目已經(jīng)夠用了,但是挖掘SRC的話(huà),在諸多競爭對手的“幫助”下,大家收集到的信息都差不多,挖掘的漏洞也往往存在重復的情況。
  那么現在我就想分享一下平時(shí)自己進(jìn)行SRC挖掘過(guò)程中,主要是如何進(jìn)行入手的。
  確定目標
  無(wú)目標隨便打,有沒(méi)有自己對應的SRC應急響應平臺不說(shuō),還往往會(huì )因為一開(kāi)始沒(méi)有挖掘到漏洞而隨意放棄,這樣往往不能挖掘到深層次的漏洞。挖到的大多數是大家都可以簡(jiǎn)單挖到的漏洞,存在大概率重復可能。
  所以在真的想要花點(diǎn)時(shí)間在SRC漏洞挖掘上的話(huà),建議先選好目標。
  那么目標怎么選呢,考慮到收益回報與付出的比例來(lái)看,建議是從專(zhuān)屬SRC入手,特別在一些活動(dòng)中,可以獲取比平時(shí)更高的收益。關(guān)聯(lián)閱讀:
  微信搜一搜:
  
  百度搜一搜:
  現在有活動(dòng)的src已經(jīng)浮現水面了,那么我們就可以從中選擇自己感興趣的SRC。
  確認測試范圍
  前面說(shuō)到確定測什么SRC,那么下面就要通過(guò)一些方法,獲取這個(gè)SRC的測試范圍,以免測偏。
  1、公眾號
  從公眾號推文入手,活動(dòng)頁(yè)面中可以發(fā)現測試范圍
  2、應急響應官網(wǎng)
  在應急響應官網(wǎng),往往會(huì )有一些活動(dòng)的公告,在里面可以獲取到相應的測試范圍。
  3、愛(ài)企查
  從愛(ài)企查等商業(yè)查詢(xún)平臺獲取公司所屬域名
  
  搜索想要測試等SRC所屬公司名稱(chēng),在知識產(chǎn)權->網(wǎng)站備案中可以獲取測試范圍。
  子域名(oneforall)
  拿到域名之后,下一步我考慮使用oneforall掃描獲取子域名,就像網(wǎng)上信息收集的文章一樣,主域名的站點(diǎn)不是靜態(tài)界面就是安全防護等級極強,不是隨便就能夠發(fā)現漏洞的,我們挖掘SRC也是要從子域名開(kāi)始,從邊緣資產(chǎn)或一般資產(chǎn)中發(fā)現漏洞。
  工具下載:
  https://github.com/shmilylty/OneForAll
  具體用法如下:
  常用的獲取子域名有2種選擇,一種使用--target指定單個(gè)域名,一種使用--targets指定域名文件。
  python3 oneforall.py --target example.com run<br />python3 oneforall.py --targets ./domains.txt run
  其他獲取子域名的工具還有layer子域名挖掘機、Sublist3r、證書(shū)透明度、在線(xiàn)工具等,這里就不一一闡述了,大體思路是一樣等,獲取子域,然后從中篩選邊緣資產(chǎn),安全防護低資產(chǎn)。
  系統指紋探測
  通過(guò)上面的方法,我們可以在/OneForAll-0.4.3/results/路徑下獲取以域名為名字的csv文件。
  里面放入到便是掃描到到所有子域名以及相應信息了。
  下一步便是將收集到到域名全部進(jìn)行一遍指紋探測,從中找出一些明顯使用CMS、OA系統、shiro、Fastjson等的站點(diǎn)。
  下面介紹平時(shí)使用的2款工具:
  1、Ehole
  下載地址:
  https://github.com/EdgeSecurityTeam/EHole
  使用方法:
  ./Ehole-darwin -l url.txt //URL地址需帶上協(xié)議,每行一個(gè)<br />./Ehole-darwin -f 192.168.1.1/24 //支持單IP或IP段,fofa識別需要配置fofa密鑰和郵箱<br />./Ehole-darwin -l url.txt -json export.json //結果輸出至export.json文件
  2、Glass(現在無(wú)法使用,勿下載)
  下載地址:
  https://github.com/s7ckTeam/Glass
  使用方法:
  python3 Glass.py -u http://www.examples.com // 單url測試<br />python3 Glass.py -w domain.txt -o 1.txt // url文件內
  框架型站點(diǎn)漏洞測試
  前面經(jīng)過(guò)了子域名收集以及對收集到的子域名進(jìn)行了指紋信息識別之后,那么對于框架型的站點(diǎn),我們可以?xún)?yōu)先進(jìn)行測試。
  類(lèi)似用友NC、通達OA、藍凌OA等,可以通過(guò)嘗試現有的Nday漏洞進(jìn)行攻擊。
  
  非框架型站點(diǎn)漏洞測試
  前面測試完框架型的站點(diǎn)了,之后就應該往正常網(wǎng)站,或者經(jīng)過(guò)了二開(kāi)未能直接檢測出指紋的站點(diǎn)進(jìn)行滲透了。
  那么對于這類(lèi)站點(diǎn),最經(jīng)常遇到的便是登錄框,在這里,我們便可以開(kāi)始測試了。
  
  1、用戶(hù)名枚舉
  抓包嘗試是否用戶(hù)名存在與不存在的情況,返回結果不同。
  2、驗證碼
  是否存在驗證碼,驗證碼是否可以抓包截斷繞過(guò),驗證碼是否可以為空。
  3、暴力破解
  下面是我收集的集中常見(jiàn)的用戶(hù)名
  1.弱口令用戶(hù)名如admin,test,ceshi等<br />2.員工姓名全拼,員工姓名簡(jiǎn)拼<br />3.公司特征+員工工號/員工姓名<br />4.員工工號+姓名簡(jiǎn)拼<br />5.員工姓名全拼+員工工號<br />6.員工姓名全拼+重復次數,如zhangsan和zhangsan01<br />7.其他
  關(guān)于暴力破解我要扯一句了,就是關(guān)于密碼字典的問(wèn)題。
  經(jīng)常會(huì )聽(tīng)到某人說(shuō)他的字典多么多么的大,有好幾個(gè)G之類(lèi)的,但是在我覺(jué)得,這很沒(méi)有必要,有些密碼是你跑幾天都跑不出來(lái)的,就算字典確實(shí)夠大,也沒(méi)有必要這樣跑,可能影響心情不說(shuō),大規模地暴力破解,很容易讓人覺(jué)得你在拒絕服務(wù)攻擊。
  其實(shí)我的話(huà)一般跑一跑弱口令就差不多了。
  關(guān)于弱口令字典的問(wèn)題,我也想說(shuō)一嘴,你最好看看,你字典里面的admin、123456、password處在什么位置。記得之前玩CTF的時(shí)候,默認密碼123456,但是那個(gè)師傅死活做不出來(lái),后面一看,字典里面居然沒(méi)有123456這個(gè)密碼。。。
  這里推薦一個(gè)字典,個(gè)人感覺(jué)還是挺好用的。當然更多的是需要自己不斷更新。
  https://github.com/fuzz-security/SuperWordlist
  4、工具cupp和cewl
  對于一些情況,密碼不是直接使用弱口令,而是通過(guò)一些公司的特征+個(gè)人信息制作的,那么這個(gè)時(shí)候,我們的字典便不能直接使用了,需要在這之前加上一些特征,例如阿里SRC可能是a;百度SRC可能是bd等。
  下面2款kali自帶等工具,可以通過(guò)收集信息,生成好用的字典,方便滲透。
  說(shuō)真的,在滲透測試過(guò)程中,弱口令,YYDS!
  具體使用說(shuō)明和工具介紹,可以查看文章:
  5、自行注冊
  如果能夠注冊那就好辦了,自己注冊一下賬戶(hù)即可。
  6、小總結
  對于非框架的站點(diǎn),登錄接口一般是必不可少的,可能就在主頁(yè),也可能在某個(gè)路徑下,藏著(zhù)后臺的登錄接口,在嘗試了多種方法成功登錄之后,記得嘗試里面是否存在未授權漏洞、越權等漏洞。
  這里借用來(lái)自WS師傅的建議:可以直接掃描出來(lái)的洞,基本都被交完了,可以更多往邏輯漏洞方面找。登錄后的漏洞重復率,比登錄前的往往會(huì )低很多。
  端口掃描
  前面就是正常的滲透了,那么一個(gè)域名只是在80、443端口才有web服務(wù)嗎?
  不可否認有些時(shí)候真的是,但是絕大多數情況下,類(lèi)似8080、8443、8081、8089、7001等端口,往往會(huì )有驚喜哦~
  端口掃描也算是老生常談了,市面上也有很多介紹端口掃描的工具使用方法,這里也不細說(shuō)了,就放出平時(shí)使用的命令吧。
  sudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v examples.comsudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -p 1-65535 examples.com
  目錄掃描dirsearch
  目錄掃描在滲透測試過(guò)程中我認為是必不可少的,一個(gè)站點(diǎn)在不同目錄下的不同文件,往往可能有驚喜哦。
  個(gè)人是喜歡使用dirserach這款工具,不僅高效、頁(yè)面也好看。市面上還有例如御劍、御劍t00ls版等,也是不錯的選擇。
  dirsearch下載地址:
  https://github.com/maurosoria/dirsearch
  具體使用方法可以查看github介紹,這里我一般是使用如下命令(因為擔心線(xiàn)程太高所以通過(guò)-t參數設置為2)
  python3 dirsearch.py -u www.xxx.com -e * -t 2
  關(guān)鍵的地方是大家都可以下載這款工具,獲取它自帶的字典,那么路徑的話(huà),便是大家都能夠搜得到的了,所以這里我推薦是可以適當整合一些師傅們發(fā)出來(lái)的路徑字典到/dirsearch-0.4.2/db/dicc.txt中。
  例如我的話(huà),是增加了springboot未授權的一些路徑、swagger的路徑以及一些例如vmvare-vcenter的漏洞路徑。
  
  JS信息收集
  在一個(gè)站點(diǎn)掃描了目錄、嘗試登錄失敗并且沒(méi)有自己注冊功能的情況下,我們還可以從JS文件入手,獲取一些URL,也許某個(gè)URL便能夠未授權訪(fǎng)問(wèn)獲取敏感信息呢。
  1、JSFinder
  工具下載:
  https://github.com/Threezh1/JSFinder
  JSFinder是一款用作快速在網(wǎng)站的js文件中提取URL,子域名的工具。
  個(gè)人覺(jué)得美中不足的地方便是不能對獲取到到URL進(jìn)行一些過(guò)濾,在某些情況下,JS文件中可以爬取非常多的URL,這其中可能大部分是頁(yè)面空或者返回200但是頁(yè)面顯示404的。
  來(lái)自HZ師傅的建議,可以修改一下工具,基于當前的基礎上,檢測獲取的URL是否可以訪(fǎng)問(wèn),訪(fǎng)問(wèn)后的頁(yè)面大小為多少,標題是什么。。。
  思路放這了,找個(gè)時(shí)間改一改?
  #檢測URL狀態(tài)碼#-----------------------#! /usr/bin/env python#coding=utf-8import sysimport requestsurl='xxxx'request = requests.get(url)httpStatusCode = request.status_codeif httpStatusCode == 200: xxxxelse: xxxx
  #檢測URL返回包大小#-----------------------import requestsdef hum_convert(value): units = ["B", "KB", "MB", "GB", "TB", "PB"] size = 1024.0 for i in range(len(units)): if (value / size) < 1: return "%.2f%s" % (value, units[i]) value = value / sizer = requests.get('https://www.baidu.com')r.status_coder.headerslength = len(r.text)print(hum_convert(length))
  #獲取網(wǎng)站標題#-----------------------#!/usr/bin/python#coding=utf-8urllib.requestimport urllib.requestimport reurl = urllib.request.urlopen('http://www.xxx.com')html = url.read().decode('utf-8')title=re.findall('(.+)',html)print (title)
  2、JS文件
  JS文件與HTML、CSS等文件統一作為前端文件,是可以通過(guò)瀏覽器訪(fǎng)問(wèn)到的,相對于HTML和CSS等文件的顯示和美化作用,JS文件將會(huì )能夠將頁(yè)面的功能點(diǎn)進(jìn)行升華。
  
  對于滲透測試來(lái)說(shuō),JS文件不僅僅能夠找到一些URL、內網(wǎng)IP地址、手機號、調用的組件版本等信息,還存在一些接口,因為前端需要,所以一些接口將會(huì )在JS文件中直接或間接呈現。
  下面我將介紹如何發(fā)現這些隱藏的接口。
  1、首先在某個(gè)頁(yè)面中,鼠標右鍵,選擇檢查
  2、點(diǎn)擊Application
  
  3、在Frames->top->Scripts中能夠獲取當前頁(yè)面請求到的所有JS
  
  4、火狐瀏覽器的話(huà),則是在調試中
  
  5、如果你請求的JS文件內容都疊在了前幾行的話(huà),下面這個(gè)鍵可以幫你美化輸出
  6、在JS文件中,可以尤為注意帶有api字眼的文件或內容,例如下面這里我發(fā)現了一個(gè)接口。
  
  小程序、APP
  web端沒(méi)有思路的時(shí)候,可以結合小程序、APP來(lái)進(jìn)行滲透。
  小程序或APP的服務(wù)端其實(shí)可以在一定程度上與web應用的服務(wù)端相聯(lián)系。
  也就是說(shuō),我們在小程序或者APP上,一樣能夠挖掘web端的漏洞如SQL注入、XSS等,并且相對來(lái)說(shuō),這類(lèi)等服務(wù)端安全措施會(huì )相對沒(méi)有那么完備,所以在web端確實(shí)沒(méi)有思路的時(shí)候,可以迂回滲透,從小程序、APP中進(jìn)行。
  #小程序抓包、APP抓包參考鏈接:<br />https://mp.weixin.qq.com/s/xuo ... %3Bbr />https://mp.weixin.qq.com/s/45Y ... %3Bbr />https://mp.weixin.qq.com/s/M5x ... %3Bbr />https://mp.weixin.qq.com/s/Mfkbxtrxv5AvY-n_bMU7ig
  總結
  以上就是我個(gè)人挖掘SRC的一些信息收集思路,挖掘SRC有的時(shí)候真的很看運氣,也許別人對一個(gè)接口簡(jiǎn)單Fuzz,便出了一個(gè)注入,而我們花了幾天,還是一直看到返回內容為404。
  所以有的時(shí)候真的可以換個(gè)站試試,也許就挖到高危甚至嚴重了~
  作為一名SRC小白,希望能夠通過(guò)這篇文章,幫到更多的網(wǎng)絡(luò )安全小白,沒(méi)能幫上大佬們真的很抱歉~后續也會(huì )持續提高自己,將學(xué)到的更多的東西分享給大家。
  聲明:本公眾號所分享內容僅用于網(wǎng)安愛(ài)好者之間的技術(shù)討論,禁止用于違法途徑,所有滲透都需獲取授權!否則需自行承擔,本公眾號及原作者不承擔相應的后果.
  @學(xué)習更多滲透技能!體驗靶場(chǎng)實(shí)戰練習
  <p style="outline: 0px;letter-spacing: 0.544px;">(hack視頻資料及工具)<br style="outline: 0px;" />
  </p>
 ?。ú糠终故荆?br />   往期推薦
  看到這里了,點(diǎn)個(gè)“贊”、“再看”吧 查看全部

  SRC信息收集思路分享,很有用!
  說(shuō)到信息收集,網(wǎng)上已經(jīng)有許多文章進(jìn)行描述了,那么從正常的子域名、端口、旁站、C段等進(jìn)行信息收集的話(huà),對于正常項目已經(jīng)夠用了,但是挖掘SRC的話(huà),在諸多競爭對手的“幫助”下,大家收集到的信息都差不多,挖掘的漏洞也往往存在重復的情況。
  那么現在我就想分享一下平時(shí)自己進(jìn)行SRC挖掘過(guò)程中,主要是如何進(jìn)行入手的。
  確定目標
  無(wú)目標隨便打,有沒(méi)有自己對應的SRC應急響應平臺不說(shuō),還往往會(huì )因為一開(kāi)始沒(méi)有挖掘到漏洞而隨意放棄,這樣往往不能挖掘到深層次的漏洞。挖到的大多數是大家都可以簡(jiǎn)單挖到的漏洞,存在大概率重復可能。
  所以在真的想要花點(diǎn)時(shí)間在SRC漏洞挖掘上的話(huà),建議先選好目標。
  那么目標怎么選呢,考慮到收益回報與付出的比例來(lái)看,建議是從專(zhuān)屬SRC入手,特別在一些活動(dòng)中,可以獲取比平時(shí)更高的收益。關(guān)聯(lián)閱讀:
  微信搜一搜:
  
  百度搜一搜:
  現在有活動(dòng)的src已經(jīng)浮現水面了,那么我們就可以從中選擇自己感興趣的SRC。
  確認測試范圍
  前面說(shuō)到確定測什么SRC,那么下面就要通過(guò)一些方法,獲取這個(gè)SRC的測試范圍,以免測偏。
  1、公眾號
  從公眾號推文入手,活動(dòng)頁(yè)面中可以發(fā)現測試范圍
  2、應急響應官網(wǎng)
  在應急響應官網(wǎng),往往會(huì )有一些活動(dòng)的公告,在里面可以獲取到相應的測試范圍。
  3、愛(ài)企查
  從愛(ài)企查等商業(yè)查詢(xún)平臺獲取公司所屬域名
  
  搜索想要測試等SRC所屬公司名稱(chēng),在知識產(chǎn)權->網(wǎng)站備案中可以獲取測試范圍。
  子域名(oneforall)
  拿到域名之后,下一步我考慮使用oneforall掃描獲取子域名,就像網(wǎng)上信息收集的文章一樣,主域名的站點(diǎn)不是靜態(tài)界面就是安全防護等級極強,不是隨便就能夠發(fā)現漏洞的,我們挖掘SRC也是要從子域名開(kāi)始,從邊緣資產(chǎn)或一般資產(chǎn)中發(fā)現漏洞。
  工具下載:
  https://github.com/shmilylty/OneForAll
  具體用法如下:
  常用的獲取子域名有2種選擇,一種使用--target指定單個(gè)域名,一種使用--targets指定域名文件。
  python3 oneforall.py --target example.com run<br />python3 oneforall.py --targets ./domains.txt run
  其他獲取子域名的工具還有layer子域名挖掘機、Sublist3r、證書(shū)透明度、在線(xiàn)工具等,這里就不一一闡述了,大體思路是一樣等,獲取子域,然后從中篩選邊緣資產(chǎn),安全防護低資產(chǎn)。
  系統指紋探測
  通過(guò)上面的方法,我們可以在/OneForAll-0.4.3/results/路徑下獲取以域名為名字的csv文件。
  里面放入到便是掃描到到所有子域名以及相應信息了。
  下一步便是將收集到到域名全部進(jìn)行一遍指紋探測,從中找出一些明顯使用CMS、OA系統、shiro、Fastjson等的站點(diǎn)。
  下面介紹平時(shí)使用的2款工具:
  1、Ehole
  下載地址:
  https://github.com/EdgeSecurityTeam/EHole
  使用方法:
  ./Ehole-darwin -l url.txt //URL地址需帶上協(xié)議,每行一個(gè)<br />./Ehole-darwin -f 192.168.1.1/24 //支持單IP或IP段,fofa識別需要配置fofa密鑰和郵箱<br />./Ehole-darwin -l url.txt -json export.json //結果輸出至export.json文件
  2、Glass(現在無(wú)法使用,勿下載)
  下載地址:
  https://github.com/s7ckTeam/Glass
  使用方法:
  python3 Glass.py -u http://www.examples.com // 單url測試<br />python3 Glass.py -w domain.txt -o 1.txt // url文件內
  框架型站點(diǎn)漏洞測試
  前面經(jīng)過(guò)了子域名收集以及對收集到的子域名進(jìn)行了指紋信息識別之后,那么對于框架型的站點(diǎn),我們可以?xún)?yōu)先進(jìn)行測試。
  類(lèi)似用友NC、通達OA、藍凌OA等,可以通過(guò)嘗試現有的Nday漏洞進(jìn)行攻擊。
  
  非框架型站點(diǎn)漏洞測試
  前面測試完框架型的站點(diǎn)了,之后就應該往正常網(wǎng)站,或者經(jīng)過(guò)了二開(kāi)未能直接檢測出指紋的站點(diǎn)進(jìn)行滲透了。
  那么對于這類(lèi)站點(diǎn),最經(jīng)常遇到的便是登錄框,在這里,我們便可以開(kāi)始測試了。
  
  1、用戶(hù)名枚舉
  抓包嘗試是否用戶(hù)名存在與不存在的情況,返回結果不同。
  2、驗證碼
  是否存在驗證碼,驗證碼是否可以抓包截斷繞過(guò),驗證碼是否可以為空。
  3、暴力破解
  下面是我收集的集中常見(jiàn)的用戶(hù)名
  1.弱口令用戶(hù)名如admin,test,ceshi等<br />2.員工姓名全拼,員工姓名簡(jiǎn)拼<br />3.公司特征+員工工號/員工姓名<br />4.員工工號+姓名簡(jiǎn)拼<br />5.員工姓名全拼+員工工號<br />6.員工姓名全拼+重復次數,如zhangsan和zhangsan01<br />7.其他
  關(guān)于暴力破解我要扯一句了,就是關(guān)于密碼字典的問(wèn)題。
  經(jīng)常會(huì )聽(tīng)到某人說(shuō)他的字典多么多么的大,有好幾個(gè)G之類(lèi)的,但是在我覺(jué)得,這很沒(méi)有必要,有些密碼是你跑幾天都跑不出來(lái)的,就算字典確實(shí)夠大,也沒(méi)有必要這樣跑,可能影響心情不說(shuō),大規模地暴力破解,很容易讓人覺(jué)得你在拒絕服務(wù)攻擊。
  其實(shí)我的話(huà)一般跑一跑弱口令就差不多了。
  關(guān)于弱口令字典的問(wèn)題,我也想說(shuō)一嘴,你最好看看,你字典里面的admin、123456、password處在什么位置。記得之前玩CTF的時(shí)候,默認密碼123456,但是那個(gè)師傅死活做不出來(lái),后面一看,字典里面居然沒(méi)有123456這個(gè)密碼。。。
  這里推薦一個(gè)字典,個(gè)人感覺(jué)還是挺好用的。當然更多的是需要自己不斷更新。
  https://github.com/fuzz-security/SuperWordlist
  4、工具cupp和cewl
  對于一些情況,密碼不是直接使用弱口令,而是通過(guò)一些公司的特征+個(gè)人信息制作的,那么這個(gè)時(shí)候,我們的字典便不能直接使用了,需要在這之前加上一些特征,例如阿里SRC可能是a;百度SRC可能是bd等。
  下面2款kali自帶等工具,可以通過(guò)收集信息,生成好用的字典,方便滲透。
  說(shuō)真的,在滲透測試過(guò)程中,弱口令,YYDS!
  具體使用說(shuō)明和工具介紹,可以查看文章:
  5、自行注冊
  如果能夠注冊那就好辦了,自己注冊一下賬戶(hù)即可。
  6、小總結
  對于非框架的站點(diǎn),登錄接口一般是必不可少的,可能就在主頁(yè),也可能在某個(gè)路徑下,藏著(zhù)后臺的登錄接口,在嘗試了多種方法成功登錄之后,記得嘗試里面是否存在未授權漏洞、越權等漏洞。
  這里借用來(lái)自WS師傅的建議:可以直接掃描出來(lái)的洞,基本都被交完了,可以更多往邏輯漏洞方面找。登錄后的漏洞重復率,比登錄前的往往會(huì )低很多。
  端口掃描
  前面就是正常的滲透了,那么一個(gè)域名只是在80、443端口才有web服務(wù)嗎?
  不可否認有些時(shí)候真的是,但是絕大多數情況下,類(lèi)似8080、8443、8081、8089、7001等端口,往往會(huì )有驚喜哦~
  端口掃描也算是老生常談了,市面上也有很多介紹端口掃描的工具使用方法,這里也不細說(shuō)了,就放出平時(shí)使用的命令吧。
  sudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v examples.comsudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -p 1-65535 examples.com
  目錄掃描dirsearch
  目錄掃描在滲透測試過(guò)程中我認為是必不可少的,一個(gè)站點(diǎn)在不同目錄下的不同文件,往往可能有驚喜哦。
  個(gè)人是喜歡使用dirserach這款工具,不僅高效、頁(yè)面也好看。市面上還有例如御劍、御劍t00ls版等,也是不錯的選擇。
  dirsearch下載地址:
  https://github.com/maurosoria/dirsearch
  具體使用方法可以查看github介紹,這里我一般是使用如下命令(因為擔心線(xiàn)程太高所以通過(guò)-t參數設置為2)
  python3 dirsearch.py -u www.xxx.com -e * -t 2
  關(guān)鍵的地方是大家都可以下載這款工具,獲取它自帶的字典,那么路徑的話(huà),便是大家都能夠搜得到的了,所以這里我推薦是可以適當整合一些師傅們發(fā)出來(lái)的路徑字典到/dirsearch-0.4.2/db/dicc.txt中。
  例如我的話(huà),是增加了springboot未授權的一些路徑、swagger的路徑以及一些例如vmvare-vcenter的漏洞路徑。
  
  JS信息收集
  在一個(gè)站點(diǎn)掃描了目錄、嘗試登錄失敗并且沒(méi)有自己注冊功能的情況下,我們還可以從JS文件入手,獲取一些URL,也許某個(gè)URL便能夠未授權訪(fǎng)問(wèn)獲取敏感信息呢。
  1、JSFinder
  工具下載:
  https://github.com/Threezh1/JSFinder
  JSFinder是一款用作快速在網(wǎng)站的js文件中提取URL,子域名的工具。
  個(gè)人覺(jué)得美中不足的地方便是不能對獲取到到URL進(jìn)行一些過(guò)濾,在某些情況下,JS文件中可以爬取非常多的URL,這其中可能大部分是頁(yè)面空或者返回200但是頁(yè)面顯示404的。
  來(lái)自HZ師傅的建議,可以修改一下工具,基于當前的基礎上,檢測獲取的URL是否可以訪(fǎng)問(wèn),訪(fǎng)問(wèn)后的頁(yè)面大小為多少,標題是什么。。。
  思路放這了,找個(gè)時(shí)間改一改?
  #檢測URL狀態(tài)碼#-----------------------#! /usr/bin/env python#coding=utf-8import sysimport requestsurl='xxxx'request = requests.get(url)httpStatusCode = request.status_codeif httpStatusCode == 200: xxxxelse: xxxx
  #檢測URL返回包大小#-----------------------import requestsdef hum_convert(value): units = ["B", "KB", "MB", "GB", "TB", "PB"] size = 1024.0 for i in range(len(units)): if (value / size) < 1: return "%.2f%s" % (value, units[i]) value = value / sizer = requests.get('https://www.baidu.com')r.status_coder.headerslength = len(r.text)print(hum_convert(length))
  #獲取網(wǎng)站標題#-----------------------#!/usr/bin/python#coding=utf-8urllib.requestimport urllib.requestimport reurl = urllib.request.urlopen('http://www.xxx.com')html = url.read().decode('utf-8')title=re.findall('(.+)',html)print (title)
  2、JS文件
  JS文件與HTML、CSS等文件統一作為前端文件,是可以通過(guò)瀏覽器訪(fǎng)問(wèn)到的,相對于HTML和CSS等文件的顯示和美化作用,JS文件將會(huì )能夠將頁(yè)面的功能點(diǎn)進(jìn)行升華。
  
  對于滲透測試來(lái)說(shuō),JS文件不僅僅能夠找到一些URL、內網(wǎng)IP地址、手機號、調用的組件版本等信息,還存在一些接口,因為前端需要,所以一些接口將會(huì )在JS文件中直接或間接呈現。
  下面我將介紹如何發(fā)現這些隱藏的接口。
  1、首先在某個(gè)頁(yè)面中,鼠標右鍵,選擇檢查
  2、點(diǎn)擊Application
  
  3、在Frames->top->Scripts中能夠獲取當前頁(yè)面請求到的所有JS
  
  4、火狐瀏覽器的話(huà),則是在調試中
  
  5、如果你請求的JS文件內容都疊在了前幾行的話(huà),下面這個(gè)鍵可以幫你美化輸出
  6、在JS文件中,可以尤為注意帶有api字眼的文件或內容,例如下面這里我發(fā)現了一個(gè)接口。
  
  小程序、APP
  web端沒(méi)有思路的時(shí)候,可以結合小程序、APP來(lái)進(jìn)行滲透。
  小程序或APP的服務(wù)端其實(shí)可以在一定程度上與web應用的服務(wù)端相聯(lián)系。
  也就是說(shuō),我們在小程序或者APP上,一樣能夠挖掘web端的漏洞如SQL注入、XSS等,并且相對來(lái)說(shuō),這類(lèi)等服務(wù)端安全措施會(huì )相對沒(méi)有那么完備,所以在web端確實(shí)沒(méi)有思路的時(shí)候,可以迂回滲透,從小程序、APP中進(jìn)行。
  #小程序抓包、APP抓包參考鏈接:<br />https://mp.weixin.qq.com/s/xuo ... %3Bbr />https://mp.weixin.qq.com/s/45Y ... %3Bbr />https://mp.weixin.qq.com/s/M5x ... %3Bbr />https://mp.weixin.qq.com/s/Mfkbxtrxv5AvY-n_bMU7ig
  總結
  以上就是我個(gè)人挖掘SRC的一些信息收集思路,挖掘SRC有的時(shí)候真的很看運氣,也許別人對一個(gè)接口簡(jiǎn)單Fuzz,便出了一個(gè)注入,而我們花了幾天,還是一直看到返回內容為404。
  所以有的時(shí)候真的可以換個(gè)站試試,也許就挖到高危甚至嚴重了~
  作為一名SRC小白,希望能夠通過(guò)這篇文章,幫到更多的網(wǎng)絡(luò )安全小白,沒(méi)能幫上大佬們真的很抱歉~后續也會(huì )持續提高自己,將學(xué)到的更多的東西分享給大家。
  聲明:本公眾號所分享內容僅用于網(wǎng)安愛(ài)好者之間的技術(shù)討論,禁止用于違法途徑,所有滲透都需獲取授權!否則需自行承擔,本公眾號及原作者不承擔相應的后果.
  @學(xué)習更多滲透技能!體驗靶場(chǎng)實(shí)戰練習
  <p style="outline: 0px;letter-spacing: 0.544px;">(hack視頻資料及工具)<br style="outline: 0px;" />
  </p>
 ?。ú糠终故荆?br />   往期推薦
  看到這里了,點(diǎn)個(gè)“贊”、“再看”吧

文章采集excel模型分析,接下來(lái)進(jìn)行數據的提取、數據可視化相關(guān)內容的學(xué)習和進(jìn)階~

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 156 次瀏覽 ? 2022-06-09 14:19 ? 來(lái)自相關(guān)話(huà)題

  文章采集excel模型分析,接下來(lái)進(jìn)行數據的提取、數據可視化相關(guān)內容的學(xué)習和進(jìn)階~
  文章采集調用excel模型分析,接下來(lái)進(jìn)行數據的提取、數據可視化相關(guān)內容的學(xué)習和進(jìn)階~自動(dòng)刷新banner、喜歡題目后推薦的手游、相應游戲的收入大幅增長(cháng)~歡迎關(guān)注公眾號并與本人交流~【長(cháng)按掃碼加入游戲帝國數據交流群】群名稱(chēng):數據魔方收集題目:哪些網(wǎng)站比百度更懂中國互聯(lián)網(wǎng)?將通過(guò)采集不同維度的數據,去分析中國互聯(lián)網(wǎng)的發(fā)展和中國互聯(lián)網(wǎng)游戲的發(fā)展趨勢。
  這次采集的數據集是智能問(wèn)答領(lǐng)域的相關(guān)數據。數據集量為22631條,包含512個(gè)樣本。每條樣本包含30個(gè)問(wèn)題,554個(gè)標簽。這次的目標是分析一下2010年至今國內大多數站點(diǎn)上的語(yǔ)音通話(huà),也包括大量沒(méi)有開(kāi)放數據的,例如通話(huà)能力不達標準的站點(diǎn)。預計耗時(shí)3-5天。數據集說(shuō)明數據樣本:本數據集已經(jīng)對問(wèn)題,標簽以及語(yǔ)音頻率進(jìn)行了pre處理,每條問(wèn)題后面使用逗號間隔隔開(kāi)。
  讀取數據時(shí)需要選擇以weka或java版本的python庫,手機端的java在數據讀取階段發(fā)生了錯誤,大約耗時(shí)30秒。我嘗試在kaggle的googleanalytics中數據導入,然后再使用kaggle自帶的googleloggersspark套件導入數據集,但最終結果并不好。代碼數據格式:youtu.be/i17alvfog提取方式:以字典格式讀取youtu.be/kgcolor_readerkcbxt提取數據的關(guān)鍵字段fname提取數據所在位置:數據讀取工具:pipinstallpandaspandasimportpandasaspd#讀取獲取數據pandasread_csv('數據集.csv',index=true)#將數據集變換成dataframe格式csv_data=pd.read_csv(pandas.dataframe(fname=fname))#讀取數據集head=csv_data.head(5)#數據集概覽title1=csv_data.title2=csv_data.title3=csv_data.title4=csv_data.title5=csv_data.title6=csv_data.title7=csv_data.title8=csv_data.title9=csv_data.title10=csv_data.title11=csv_data.title12=csv_data.title13=csv_data.title14=csv_data.title15=csv_data.title16=csv_data.title17=csv_data.title18=csv_data.title19=csv_data.title20=csv_data.title21=csv_data.title22=csv_data.title23=csv_data.title24=csv_data.title25=csv_data.title26=csv_data.title27=csv_data.title28=csv_data.title29。 查看全部

  文章采集excel模型分析,接下來(lái)進(jìn)行數據的提取、數據可視化相關(guān)內容的學(xué)習和進(jìn)階~
  文章采集調用excel模型分析,接下來(lái)進(jìn)行數據的提取、數據可視化相關(guān)內容的學(xué)習和進(jìn)階~自動(dòng)刷新banner、喜歡題目后推薦的手游、相應游戲的收入大幅增長(cháng)~歡迎關(guān)注公眾號并與本人交流~【長(cháng)按掃碼加入游戲帝國數據交流群】群名稱(chēng):數據魔方收集題目:哪些網(wǎng)站比百度更懂中國互聯(lián)網(wǎng)?將通過(guò)采集不同維度的數據,去分析中國互聯(lián)網(wǎng)的發(fā)展和中國互聯(lián)網(wǎng)游戲的發(fā)展趨勢。
  這次采集的數據集是智能問(wèn)答領(lǐng)域的相關(guān)數據。數據集量為22631條,包含512個(gè)樣本。每條樣本包含30個(gè)問(wèn)題,554個(gè)標簽。這次的目標是分析一下2010年至今國內大多數站點(diǎn)上的語(yǔ)音通話(huà),也包括大量沒(méi)有開(kāi)放數據的,例如通話(huà)能力不達標準的站點(diǎn)。預計耗時(shí)3-5天。數據集說(shuō)明數據樣本:本數據集已經(jīng)對問(wèn)題,標簽以及語(yǔ)音頻率進(jìn)行了pre處理,每條問(wèn)題后面使用逗號間隔隔開(kāi)。
  讀取數據時(shí)需要選擇以weka或java版本的python庫,手機端的java在數據讀取階段發(fā)生了錯誤,大約耗時(shí)30秒。我嘗試在kaggle的googleanalytics中數據導入,然后再使用kaggle自帶的googleloggersspark套件導入數據集,但最終結果并不好。代碼數據格式:youtu.be/i17alvfog提取方式:以字典格式讀取youtu.be/kgcolor_readerkcbxt提取數據的關(guān)鍵字段fname提取數據所在位置:數據讀取工具:pipinstallpandaspandasimportpandasaspd#讀取獲取數據pandasread_csv('數據集.csv',index=true)#將數據集變換成dataframe格式csv_data=pd.read_csv(pandas.dataframe(fname=fname))#讀取數據集head=csv_data.head(5)#數據集概覽title1=csv_data.title2=csv_data.title3=csv_data.title4=csv_data.title5=csv_data.title6=csv_data.title7=csv_data.title8=csv_data.title9=csv_data.title10=csv_data.title11=csv_data.title12=csv_data.title13=csv_data.title14=csv_data.title15=csv_data.title16=csv_data.title17=csv_data.title18=csv_data.title19=csv_data.title20=csv_data.title21=csv_data.title22=csv_data.title23=csv_data.title24=csv_data.title25=csv_data.title26=csv_data.title27=csv_data.title28=csv_data.title29。

文章采集調用googleapi,估計能免費獲取到文章的幾篇文章

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 126 次瀏覽 ? 2022-05-22 16:06 ? 來(lái)自相關(guān)話(huà)題

  文章采集調用googleapi,估計能免費獲取到文章的幾篇文章
  文章采集調用googleapi,估計能免費獲取到文章的幾篇文章,但注意看好文章?。?!推薦小黑胖收集整理的。那個(gè)都能看到,只是bug多。要不你弄弄這個(gè),一篇文章都能看到。其他平臺應該也行。但能不能一次采集到整個(gè)鏈接,就是另一回事了。需要用到文章收集軟件。把需要收集的文章都收集起來(lái),重點(diǎn)是還是一次性收集好后不能再次收集的,在返回收集文章的軟件就行。
  需要注冊一個(gè)谷歌賬號,
  采集云,一鍵登錄,
  如果是簡(jiǎn)單的博客爬蟲(chóng),可以直接下載相關(guān)api文檔即可。googleapidocs需要翻墻,很多博客爬蟲(chóng)都可以參考。
  googlereader.
  freegradleweekly
  api,github上有很多開(kāi)源的githubapis,都能抓googleapi;scrapy,scrapy是scrapy框架開(kāi)發(fā)的,可以抓googleapi;httplib,可以抓googleapi,不用翻墻;用python爬蟲(chóng)先看googleapi,googleapi沒(méi)有你想象的那么復雜,網(wǎng)上有例子,實(shí)踐下就知道了。
  推薦apifinder。而且目前來(lái)說(shuō),
  googleapidocs
  newmail-weeklyextension
  apifinder
  有使用過(guò),也有之前從網(wǎng)上找到的api,googleapisnewsreader這些,都大同小異。 查看全部

  文章采集調用googleapi,估計能免費獲取到文章的幾篇文章
  文章采集調用googleapi,估計能免費獲取到文章的幾篇文章,但注意看好文章?。?!推薦小黑胖收集整理的。那個(gè)都能看到,只是bug多。要不你弄弄這個(gè),一篇文章都能看到。其他平臺應該也行。但能不能一次采集到整個(gè)鏈接,就是另一回事了。需要用到文章收集軟件。把需要收集的文章都收集起來(lái),重點(diǎn)是還是一次性收集好后不能再次收集的,在返回收集文章的軟件就行。
  需要注冊一個(gè)谷歌賬號,
  采集云,一鍵登錄,
  如果是簡(jiǎn)單的博客爬蟲(chóng),可以直接下載相關(guān)api文檔即可。googleapidocs需要翻墻,很多博客爬蟲(chóng)都可以參考。
  googlereader.
  freegradleweekly
  api,github上有很多開(kāi)源的githubapis,都能抓googleapi;scrapy,scrapy是scrapy框架開(kāi)發(fā)的,可以抓googleapi;httplib,可以抓googleapi,不用翻墻;用python爬蟲(chóng)先看googleapi,googleapi沒(méi)有你想象的那么復雜,網(wǎng)上有例子,實(shí)踐下就知道了。
  推薦apifinder。而且目前來(lái)說(shuō),
  googleapidocs
  newmail-weeklyextension
  apifinder
  有使用過(guò),也有之前從網(wǎng)上找到的api,googleapisnewsreader這些,都大同小異。

【文末贈書(shū)】紅隊攻防之信息收集總結

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 114 次瀏覽 ? 2022-05-15 14:33 ? 來(lái)自相關(guān)話(huà)題

  【文末贈書(shū)】紅隊攻防之信息收集總結
  之前也總結過(guò)類(lèi)似的信息收集相關(guān)的文章,但是每隔一段時(shí)間理解和手法都會(huì )有所不同,本文以hvv或授權但僅提供公司名稱(chēng)域名等情況下滲透測試的視角總結一些自己最近做信息收集的流程套路。
  信息收集一、初始已知信息
  前言中提到的兩種情況,一般初始信息只有公司名稱(chēng)、個(gè)別官網(wǎng)域名、靶標名稱(chēng)等信息,以此為起點(diǎn)進(jìn)行信息收集。
  二、搜尋根域名
  此步驟個(gè)人的經(jīng)驗是,面對大公司優(yōu)先選擇工信部備案查詢(xún),小公司用搜索引擎做起點(diǎn),然后幾種方式都可以過(guò)一遍,查漏補缺,盡量獲取最全的信息。大部分公司根域名都不會(huì )很多,全部過(guò)一遍也不會(huì )用掉多少時(shí)間。
  1.搜索引擎
  搜索引擎直接搜索其公司名稱(chēng),獲取其相關(guān)根域名
  2.天眼查、企查查
  從天眼查、企查查等途徑,輸入公司名,查詢(xún)其域名以及全資控股子公司的域名
  3.工信部備案
  工信部備案查詢(xún)域名/ip地址(需要詳細且正確的公司名稱(chēng),結果也會(huì )很全面)
  #/Integrated/recordQuery
  4.fofa
  fofa查詢(xún)其公司名稱(chēng),獲取相關(guān)域名
  5.站長(cháng)之家
  使用其icp查詢(xún)功能查詢(xún)備案,當我們不知道公司完整名稱(chēng)的時(shí)候也可以使用此網(wǎng)站功能使用已知域名查詢(xún)完整備案公司名稱(chēng)
  6.反查域名
  用已知的某些ip反查域名
  三、子域名
  在子域名收集這步本人一般不喜歡爆破的方式,子域名爆破比較依賴(lài)字典,字典小就收集不全,字典大就很費時(shí)間,所以一般優(yōu)先在各類(lèi)解析記錄的網(wǎng)站查詢(xún)。
  1.各類(lèi)網(wǎng)站查詢(xún)解析記錄
  以bilibili為例:
  類(lèi)似的網(wǎng)站非常多,這兩個(gè)都是免費的,但是第二個(gè)要注冊登錄
  2.子域名爆破
  相關(guān)的工具很多,部分掃描器也自帶子域名爆破功能或可安裝相關(guān)插件。
  subDomainsBrute
  3.fofa、shodan
  利用這類(lèi)工具對域名資產(chǎn)進(jìn)行查詢(xún),如
  fofa語(yǔ)法domain=””
  4.OneForAll
  此工具會(huì )集成多種方式搜集子域名,包括dns查詢(xún)、證書(shū)查詢(xún)等,詳情見(jiàn)其項目中的readme
  安裝
  1<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />3<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />4<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />5<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  git clone https://github.com/shmilylty/OneForAll.git<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />cd OneForAll/<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />python3 -m pip install -U pip setuptools wheel -i https://mirrors.aliyun.com/pypi/simple/<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />python3 oneforall.py --help<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  1<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  python3 oneforall.py --target example.com run<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />python3 oneforall.py --targets ./example.txt run<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  四、ip
  ip列表不完全來(lái)源于域名解析,有一部分ip是直接使用ip地址提供服務(wù)的,需要提前收集這部分信息,另一部分是通過(guò)域名解析過(guò)來(lái)的。
  1.各類(lèi)網(wǎng)站查詢(xún)解析記錄
  同子域名查詢(xún)中的操作,但是需要做的是把ip列表導出
  2.解析域名
  將所有已收集到的子域名通過(guò)腳本批量調用dig或nslookup解析ip
  1<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  nslookup xxx.com<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />dig xxx.com @114.114.114.114<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  編寫(xiě)腳本批量調用dig命令,導出結果
  或將域名列表放在在線(xiàn)解析網(wǎng)站中,導出其解析結果
  這個(gè)步驟中需要額外關(guān)注cdn的情況,繞過(guò)cdn尋找其真實(shí)ip,可參考這篇文檔
  3.c段
  將前面已經(jīng)獲得的ip全部整理好,使用腳本進(jìn)行排序,懶得寫(xiě)腳本也可以使用在線(xiàn)的功能
  如ip地址排序計算器
  得到排序好的ip,可以先自己判斷哪些c段可能屬于目標,再進(jìn)行一些掃描和訪(fǎng)問(wèn),整理更全面的ip列表。
  五、端口
  使用masscan、nmap等工具對端口信息進(jìn)行收集
  六、web服務(wù)
  使用webfinder等工具掃描已整理ip列表的web常用端口,導出形如:port/以及:port/的web服務(wù)列表
  指紋識別
  1.
  2.
  七、漏掃1.主機掃描
  上文整理好的ip列表和域名列表,可以丟入主機掃描相關(guān)的掃描器中,如goby、Nessus等
  2.web掃描
  整理好web服務(wù)列表,可以丟入awvs等工具進(jìn)行掃描,同時(shí)可以聯(lián)動(dòng)xray批量掃描 查看全部

  【文末贈書(shū)】紅隊攻防之信息收集總結
  之前也總結過(guò)類(lèi)似的信息收集相關(guān)的文章,但是每隔一段時(shí)間理解和手法都會(huì )有所不同,本文以hvv或授權但僅提供公司名稱(chēng)域名等情況下滲透測試的視角總結一些自己最近做信息收集的流程套路。
  信息收集一、初始已知信息
  前言中提到的兩種情況,一般初始信息只有公司名稱(chēng)、個(gè)別官網(wǎng)域名、靶標名稱(chēng)等信息,以此為起點(diǎn)進(jìn)行信息收集。
  二、搜尋根域名
  此步驟個(gè)人的經(jīng)驗是,面對大公司優(yōu)先選擇工信部備案查詢(xún),小公司用搜索引擎做起點(diǎn),然后幾種方式都可以過(guò)一遍,查漏補缺,盡量獲取最全的信息。大部分公司根域名都不會(huì )很多,全部過(guò)一遍也不會(huì )用掉多少時(shí)間。
  1.搜索引擎
  搜索引擎直接搜索其公司名稱(chēng),獲取其相關(guān)根域名
  2.天眼查、企查查
  從天眼查、企查查等途徑,輸入公司名,查詢(xún)其域名以及全資控股子公司的域名
  3.工信部備案
  工信部備案查詢(xún)域名/ip地址(需要詳細且正確的公司名稱(chēng),結果也會(huì )很全面)
  #/Integrated/recordQuery
  4.fofa
  fofa查詢(xún)其公司名稱(chēng),獲取相關(guān)域名
  5.站長(cháng)之家
  使用其icp查詢(xún)功能查詢(xún)備案,當我們不知道公司完整名稱(chēng)的時(shí)候也可以使用此網(wǎng)站功能使用已知域名查詢(xún)完整備案公司名稱(chēng)
  6.反查域名
  用已知的某些ip反查域名
  三、子域名
  在子域名收集這步本人一般不喜歡爆破的方式,子域名爆破比較依賴(lài)字典,字典小就收集不全,字典大就很費時(shí)間,所以一般優(yōu)先在各類(lèi)解析記錄的網(wǎng)站查詢(xún)。
  1.各類(lèi)網(wǎng)站查詢(xún)解析記錄
  以bilibili為例:
  類(lèi)似的網(wǎng)站非常多,這兩個(gè)都是免費的,但是第二個(gè)要注冊登錄
  2.子域名爆破
  相關(guān)的工具很多,部分掃描器也自帶子域名爆破功能或可安裝相關(guān)插件。
  subDomainsBrute
  3.fofa、shodan
  利用這類(lèi)工具對域名資產(chǎn)進(jìn)行查詢(xún),如
  fofa語(yǔ)法domain=””
  4.OneForAll
  此工具會(huì )集成多種方式搜集子域名,包括dns查詢(xún)、證書(shū)查詢(xún)等,詳情見(jiàn)其項目中的readme
  安裝
  1<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />3<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />4<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />5<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  git clone https://github.com/shmilylty/OneForAll.git<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />cd OneForAll/<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />python3 -m pip install -U pip setuptools wheel -i https://mirrors.aliyun.com/pypi/simple/<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />python3 oneforall.py --help<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  1<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  python3 oneforall.py --target example.com run<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />python3 oneforall.py --targets ./example.txt run<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  四、ip
  ip列表不完全來(lái)源于域名解析,有一部分ip是直接使用ip地址提供服務(wù)的,需要提前收集這部分信息,另一部分是通過(guò)域名解析過(guò)來(lái)的。
  1.各類(lèi)網(wǎng)站查詢(xún)解析記錄
  同子域名查詢(xún)中的操作,但是需要做的是把ip列表導出
  2.解析域名
  將所有已收集到的子域名通過(guò)腳本批量調用dig或nslookup解析ip
  1<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  nslookup xxx.com<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />dig xxx.com @114.114.114.114<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  編寫(xiě)腳本批量調用dig命令,導出結果
  或將域名列表放在在線(xiàn)解析網(wǎng)站中,導出其解析結果
  這個(gè)步驟中需要額外關(guān)注cdn的情況,繞過(guò)cdn尋找其真實(shí)ip,可參考這篇文檔
  3.c段
  將前面已經(jīng)獲得的ip全部整理好,使用腳本進(jìn)行排序,懶得寫(xiě)腳本也可以使用在線(xiàn)的功能
  如ip地址排序計算器
  得到排序好的ip,可以先自己判斷哪些c段可能屬于目標,再進(jìn)行一些掃描和訪(fǎng)問(wèn),整理更全面的ip列表。
  五、端口
  使用masscan、nmap等工具對端口信息進(jìn)行收集
  六、web服務(wù)
  使用webfinder等工具掃描已整理ip列表的web常用端口,導出形如:port/以及:port/的web服務(wù)列表
  指紋識別
  1.
  2.
  七、漏掃1.主機掃描
  上文整理好的ip列表和域名列表,可以丟入主機掃描相關(guān)的掃描器中,如goby、Nessus等
  2.web掃描
  整理好web服務(wù)列表,可以丟入awvs等工具進(jìn)行掃描,同時(shí)可以聯(lián)動(dòng)xray批量掃描

一篇文章教會(huì )你利用Python網(wǎng)絡(luò )爬蟲(chóng)實(shí)現豆瓣電影采集

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 223 次瀏覽 ? 2022-05-14 11:03 ? 來(lái)自相關(guān)話(huà)題

  一篇文章教會(huì )你利用Python網(wǎng)絡(luò )爬蟲(chóng)實(shí)現豆瓣電影采集
  點(diǎn)擊上方“IT共享之家”,進(jìn)行關(guān)注
  回復“資料”可獲贈Python學(xué)習福利
  【一、項目背景】
  豆瓣電影提供最新的電影介紹及評論包括上映影片的影訊查詢(xún)及購票服務(wù)??梢杂涗浵肟?、在看和看過(guò)的電影電視劇 、順便打分、寫(xiě)影評。極大地方便了人們的生活。
  今天以電視?。绖。槔?,批量爬取對應的電影,寫(xiě)入csv文檔 。用戶(hù)可以通過(guò)評分,更好的選擇自己想要的電影。
  【二、項目目標】
  獲取對應的電影名稱(chēng),評分,詳情鏈接,下載 電影的圖片,保存文檔。
  【三、涉及的庫和網(wǎng)站】
  1、網(wǎng)址如下:
  https://movie.douban.com/j/sea ... rt%3D{}
  2、涉及的庫:requests、fake_useragent、json、csv
  3、軟件:PyCharm
  【四、項目分析】
  1、如何多網(wǎng)頁(yè)請求?
  點(diǎn)擊下一頁(yè)時(shí),每增加一頁(yè)paged自增加20,用{}代替變換的變量,再用for循環(huán)遍歷這網(wǎng)址,實(shí)現多個(gè)網(wǎng)址請求。
  2. 如何獲取真正請求的地址?
  請求數據時(shí),發(fā)現頁(yè)面上并沒(méi)有對應數據。其實(shí)豆瓣網(wǎng)采用javascript動(dòng)態(tài)加載內容,防止采集。
  1)F12右鍵檢查,找到Network,左邊菜單Name , 找到第五個(gè)數據,點(diǎn)擊Preview。
  
  2)點(diǎn)開(kāi)subjects,可以看到 title 就是對應電影名稱(chēng)。rate就是對應評分。通過(guò)js解析subjects字典,找到需要的字段。
  
  3. 如何網(wǎng)頁(yè)訪(fǎng)問(wèn)?
  https://movie.douban.com/j/sea ... %3Bbr />https://movie.douban.com/j/sea ... %3Bbr />https://movie.douban.com/j/sea ... %3Bbr />https://movie.douban.com/j/sea ... %3D60
  當點(diǎn)擊下一頁(yè)時(shí),每增加一頁(yè)page自增加20,用{}代替變換的變量,再用for循環(huán)遍歷這網(wǎng)址,實(shí)現多個(gè)網(wǎng)址請求。
  【五、項目實(shí)施】
  1、我們定義一個(gè)class類(lèi)繼承object,然后定義init方法繼承self,再定義一個(gè)主函數main繼承self。導入需要的庫和請求網(wǎng)址。
  import requests,jsonfrom fake_useragent import UserAgentimport csv<br />class Doban(object): def __init__(self): self.url = "https://movie.douban.com/j/sea ... rt%3D{}"<br /> def main(self): pass<br />if __name__ == '__main__': Siper = Doban() Siper.main()
  2、隨機產(chǎn)生UserAgent,構造請求頭,防止反爬。
   for i in range(1, 50): self.headers = { 'User-Agent': ua.random, }
  3、發(fā)送請求 ,獲取響應,頁(yè)面回調,方便下次請求。
   def get_page(self, url): res = requests.get(url=url, headers=self.headers) html = res.content.decode("utf-8") return html
  4、json解析頁(yè)面數據,獲取對應的字典。
   data = json.loads(html)['subjects'] # print(data[0])
  5、for遍歷,獲取對應的電影名、 評分、下詳情頁(yè)鏈接。
   print(name, goblin_herf) html2 = self.get_page(goblin_herf) # 第二個(gè)發(fā)生請求 parse_html2 = etree.HTML(html2) r = parse_html2.xpath('//div[@class="entry"]/p/text()')
  6、創(chuàng )建csv文件進(jìn)行寫(xiě)入,定義對應的標題頭內容,保存數據 。
   # 創(chuàng )建csv文件進(jìn)行寫(xiě)入 csv_file = open('scr.csv', 'a', encoding='gbk') csv_writer = csv.writer(csv_file) # 寫(xiě)入csv標題頭內容 csv_writerr.writerow(['電影', '評分', "詳情頁(yè)"]) #寫(xiě)入數據 csv_writer.writerow([id, rate, urll])
  7、圖片地址進(jìn)行請求。定義圖片名稱(chēng),保存文檔。
   html2 = requests.get(url=urll, headers=self.headers).content dirname = "./圖/" + id + ".jpg" with open(dirname, 'wb') as f: f.write(html2) print("%s 【下載成功?。。?!】" % id)
  8、調用方法,實(shí)現功能。
   html = self.get_page(url) self.parse_page(html)
  9、項目?jì)?yōu)化:1)設置時(shí)間延時(shí)。
   time.sleep(1.4)
  2)定義一個(gè)變量u, for遍歷,表示爬取的是第幾頁(yè)。(更清晰可觀(guān))。
   u = 0 self.u += 1;
  【六、效果展示】
  1、點(diǎn)擊綠色小三角運行輸入起始頁(yè),終止頁(yè)( 從0頁(yè)開(kāi)始 )。
  
  2、將下載成功信息顯示在控制臺。
  
  3、保存csv文檔。
  
  4、電影圖片展示。
  
  【七、總結】
  1、不建議抓取太多數據,容易對服務(wù)器造成負載,淺嘗輒止即可。
  2、本文章就Python爬取豆瓣網(wǎng),在應用中出現的難點(diǎn)和重點(diǎn),以及如何防止反爬,做出了相對于的解決方案。
  3、希望通過(guò)這個(gè)項目,能夠幫助了解json解析頁(yè)面的基本流程,字符串是如何拼接,format函數如何運用。
  4、本文基于Python網(wǎng)絡(luò )爬蟲(chóng),利用爬蟲(chóng)庫,實(shí)現豆瓣電影及其圖片的獲取。實(shí)現的時(shí)候,總會(huì )有各種各樣的問(wèn)題,切勿眼高手低,勤動(dòng)手,才可以理解的更加深刻。
  5、需要本文源碼的小伙伴,請在下方公眾號后臺回復“豆瓣電影”四個(gè)字,即可獲取。
  看完本文有收獲?請轉發(fā)分享給更多的人
  IT共享之家 查看全部

  一篇文章教會(huì )你利用Python網(wǎng)絡(luò )爬蟲(chóng)實(shí)現豆瓣電影采集
  點(diǎn)擊上方“IT共享之家”,進(jìn)行關(guān)注
  回復“資料”可獲贈Python學(xué)習福利
  【一、項目背景】
  豆瓣電影提供最新的電影介紹及評論包括上映影片的影訊查詢(xún)及購票服務(wù)??梢杂涗浵肟?、在看和看過(guò)的電影電視劇 、順便打分、寫(xiě)影評。極大地方便了人們的生活。
  今天以電視?。绖。槔?,批量爬取對應的電影,寫(xiě)入csv文檔 。用戶(hù)可以通過(guò)評分,更好的選擇自己想要的電影。
  【二、項目目標】
  獲取對應的電影名稱(chēng),評分,詳情鏈接,下載 電影的圖片,保存文檔。
  【三、涉及的庫和網(wǎng)站】
  1、網(wǎng)址如下:
  https://movie.douban.com/j/sea ... rt%3D{}
  2、涉及的庫:requests、fake_useragent、json、csv
  3、軟件:PyCharm
  【四、項目分析】
  1、如何多網(wǎng)頁(yè)請求?
  點(diǎn)擊下一頁(yè)時(shí),每增加一頁(yè)paged自增加20,用{}代替變換的變量,再用for循環(huán)遍歷這網(wǎng)址,實(shí)現多個(gè)網(wǎng)址請求。
  2. 如何獲取真正請求的地址?
  請求數據時(shí),發(fā)現頁(yè)面上并沒(méi)有對應數據。其實(shí)豆瓣網(wǎng)采用javascript動(dòng)態(tài)加載內容,防止采集。
  1)F12右鍵檢查,找到Network,左邊菜單Name , 找到第五個(gè)數據,點(diǎn)擊Preview。
  
  2)點(diǎn)開(kāi)subjects,可以看到 title 就是對應電影名稱(chēng)。rate就是對應評分。通過(guò)js解析subjects字典,找到需要的字段。
  
  3. 如何網(wǎng)頁(yè)訪(fǎng)問(wèn)?
  https://movie.douban.com/j/sea ... %3Bbr />https://movie.douban.com/j/sea ... %3Bbr />https://movie.douban.com/j/sea ... %3Bbr />https://movie.douban.com/j/sea ... %3D60
  當點(diǎn)擊下一頁(yè)時(shí),每增加一頁(yè)page自增加20,用{}代替變換的變量,再用for循環(huán)遍歷這網(wǎng)址,實(shí)現多個(gè)網(wǎng)址請求。
  【五、項目實(shí)施】
  1、我們定義一個(gè)class類(lèi)繼承object,然后定義init方法繼承self,再定義一個(gè)主函數main繼承self。導入需要的庫和請求網(wǎng)址。
  import requests,jsonfrom fake_useragent import UserAgentimport csv<br />class Doban(object): def __init__(self): self.url = "https://movie.douban.com/j/sea ... rt%3D{}"<br /> def main(self): pass<br />if __name__ == '__main__': Siper = Doban() Siper.main()
  2、隨機產(chǎn)生UserAgent,構造請求頭,防止反爬。
   for i in range(1, 50): self.headers = { 'User-Agent': ua.random, }
  3、發(fā)送請求 ,獲取響應,頁(yè)面回調,方便下次請求。
   def get_page(self, url): res = requests.get(url=url, headers=self.headers) html = res.content.decode("utf-8") return html
  4、json解析頁(yè)面數據,獲取對應的字典。
   data = json.loads(html)['subjects'] # print(data[0])
  5、for遍歷,獲取對應的電影名、 評分、下詳情頁(yè)鏈接。
   print(name, goblin_herf) html2 = self.get_page(goblin_herf) # 第二個(gè)發(fā)生請求 parse_html2 = etree.HTML(html2) r = parse_html2.xpath('//div[@class="entry"]/p/text()')
  6、創(chuàng )建csv文件進(jìn)行寫(xiě)入,定義對應的標題頭內容,保存數據 。
   # 創(chuàng )建csv文件進(jìn)行寫(xiě)入 csv_file = open('scr.csv', 'a', encoding='gbk') csv_writer = csv.writer(csv_file) # 寫(xiě)入csv標題頭內容 csv_writerr.writerow(['電影', '評分', "詳情頁(yè)"]) #寫(xiě)入數據 csv_writer.writerow([id, rate, urll])
  7、圖片地址進(jìn)行請求。定義圖片名稱(chēng),保存文檔。
   html2 = requests.get(url=urll, headers=self.headers).content dirname = "./圖/" + id + ".jpg" with open(dirname, 'wb') as f: f.write(html2) print("%s 【下載成功?。。?!】" % id)
  8、調用方法,實(shí)現功能。
   html = self.get_page(url) self.parse_page(html)
  9、項目?jì)?yōu)化:1)設置時(shí)間延時(shí)。
   time.sleep(1.4)
  2)定義一個(gè)變量u, for遍歷,表示爬取的是第幾頁(yè)。(更清晰可觀(guān))。
   u = 0 self.u += 1;
  【六、效果展示】
  1、點(diǎn)擊綠色小三角運行輸入起始頁(yè),終止頁(yè)( 從0頁(yè)開(kāi)始 )。
  
  2、將下載成功信息顯示在控制臺。
  
  3、保存csv文檔。
  
  4、電影圖片展示。
  
  【七、總結】
  1、不建議抓取太多數據,容易對服務(wù)器造成負載,淺嘗輒止即可。
  2、本文章就Python爬取豆瓣網(wǎng),在應用中出現的難點(diǎn)和重點(diǎn),以及如何防止反爬,做出了相對于的解決方案。
  3、希望通過(guò)這個(gè)項目,能夠幫助了解json解析頁(yè)面的基本流程,字符串是如何拼接,format函數如何運用。
  4、本文基于Python網(wǎng)絡(luò )爬蟲(chóng),利用爬蟲(chóng)庫,實(shí)現豆瓣電影及其圖片的獲取。實(shí)現的時(shí)候,總會(huì )有各種各樣的問(wèn)題,切勿眼高手低,勤動(dòng)手,才可以理解的更加深刻。
  5、需要本文源碼的小伙伴,請在下方公眾號后臺回復“豆瓣電影”四個(gè)字,即可獲取。
  看完本文有收獲?請轉發(fā)分享給更多的人
  IT共享之家

文章采集調用 好代碼和壞代碼

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 150 次瀏覽 ? 2022-05-14 06:28 ? 來(lái)自相關(guān)話(huà)題

  文章采集調用 好代碼和壞代碼
  關(guān)注后回復“進(jìn)群”,拉你進(jìn)程序員交流群
  要寫(xiě)出好代碼,首先需要提升品位。
  很多軟件工程師寫(xiě)不好代碼,在評審他人的代碼時(shí)也看不出問(wèn)題,就是因為缺乏對好代碼標準的認識。
  現在還有太多的軟件工程師認為,代碼只要可以正確執行就可以了。這是一種非常低的評價(jià)標準,很多重要的方面都被忽視了。
  好代碼的特性
  好代碼具有以下特性。
  1. 魯棒(Solid and Robust)
  代碼不僅要被正確執行,我們還要考慮對各種錯誤情況的處理,比如各種系統調用和函數調用的異常情況,系統相關(guān)組件的異常和錯誤。
  對很多產(chǎn)品級的程序來(lái)說(shuō),異常和錯誤處理的邏輯占了很大比例。
  2. 高效(Fast)
  程序的運行應使用盡量少的資源。資源不僅僅包括CPU,還可能包括存儲、I/O等。
  設計高效的程序,會(huì )運用到數據結構和算法方面的知識,同時(shí)要考慮到程序運行時(shí)的各種約束條件。
  3. 簡(jiǎn)潔(Maintainable and Simple)
  代碼的邏輯要盡量簡(jiǎn)明易懂,代碼要具有很好的可維護性。對于同樣的目標,能夠使用簡(jiǎn)單清楚的方法達成,就不要使用復雜晦澀的方法。
  “大道至簡(jiǎn)”,能否把復雜的問(wèn)題用簡(jiǎn)單的方式實(shí)現出來(lái),這是一種編程水平的體現。
  4. 簡(jiǎn)短(Small)
  在某種意義上,代碼的復雜度和維護成本是和代碼的規模直接相關(guān)的。在實(shí)現同樣功能的時(shí)候,要盡量將代碼寫(xiě)得簡(jiǎn)短一些。
  簡(jiǎn)潔高于簡(jiǎn)短。這里要注意,某些人為了能把代碼寫(xiě)得簡(jiǎn)短,使用了一些晦澀難懂的描述方式,降低了代碼的可讀性。這種方式是不可取的。
  5. 可測試(Testable)
  代碼的正確性要通過(guò)測試來(lái)保證,尤其是在敏捷的場(chǎng)景下,更需要依賴(lài)可自動(dòng)回歸執行的測試用例。
  在代碼的設計中,要考慮如何使代碼可測、易測。一個(gè)比較好的實(shí)踐是使用TDD(Test-Driven Development,測試驅動(dòng)開(kāi)發(fā))的方法,這樣在編寫(xiě)測試用例的時(shí)候會(huì )很快發(fā)現代碼在可測試性方面的問(wèn)題。
  6. 共享(Re-Usable)
  大量的程序實(shí)際上都使用了類(lèi)似的框架或邏輯。由于目前開(kāi)源代碼的大量普及,很多功能并不需要重復開(kāi)發(fā),只進(jìn)行引用和使用即可。
  在一個(gè)組織內部,應鼓勵共享和重用代碼,這樣可以有效降低代碼研發(fā)的成本,并提升代碼的質(zhì)量。
  實(shí)現代碼的共享,不僅需要在意識方面提升,還需要具有相關(guān)的能力(如編寫(xiě)獨立、高質(zhì)量的代碼庫)及相關(guān)基礎設施的支持(如代碼搜索、代碼引用機制)。
  7. 可移植(Portable)
  某些程序需要在多種操作系統下運行,在這種情況下,代碼的可移植性成為一種必需的能力。
  要讓代碼具有可移植性,需要對所運行的各種操作系統底層有充分的理解和統一抽象。一般會(huì )使用一個(gè)適配層來(lái)屏蔽操作系統底層的差異。
  一些編程語(yǔ)言也提供了多操作系統的可移植性,如很多基于Python語(yǔ)言、Java語(yǔ)言、Go語(yǔ)言編寫(xiě)的程序,都可以跨平臺運行。
  8. 可觀(guān)測(Observable)/ 可監控(Monitorable)
  面對目前大量存在的在線(xiàn)服務(wù)(Online Service)程序,需要具備對程序的運行狀態(tài)進(jìn)行細致而持續監控的能力。
  這要求在程序設計時(shí)就提供相關(guān)的機制,包括程序狀態(tài)的收集、保存和對外輸出。
  9. 可運維(Operational)
  可運維已經(jīng)成為軟件研發(fā)活動(dòng)的重要組成部分,可運維重點(diǎn)關(guān)注成本、效率和穩定性三個(gè)方面。
  程序的可運維性和程序的設計、編寫(xiě)緊密相關(guān),如果在程序設計階段就沒(méi)有考慮可運維性,那么程序運行的運維目標則難以達成。
  10. 可擴展(Scalable andExtensible)
  可擴展包含“容量可擴展”(Scalable)和“功能可擴展”(Extensible)兩方面。
  在互聯(lián)網(wǎng)公司的系統設計中,“容量可擴展”是重要的設計目標之一。系統要盡量支持通過(guò)增加資源來(lái)實(shí)現容量的線(xiàn)性提高。
  快速響應需求的變化,是互聯(lián)網(wǎng)公司的另外一個(gè)重要挑戰??煽紤]使用插件式的程序設計方式,以容納未來(lái)可能新增的功能,也可考慮使用類(lèi)似Protocol Buffer 這樣的工具,支持對協(xié)議新增字段。
  以上十條標準,如果要記住,可能有些困難。我們可以把它們歸納為四個(gè)方面,見(jiàn)表1。
  表1對一流代碼特性的匯總分類(lèi)
  
  壞代碼的例子
  關(guān)于好代碼,上面介紹了一些特性,本節也給出壞代碼(Bad Code)的幾個(gè)例子。關(guān)于壞代碼,本書(shū)沒(méi)有做系統性總結,只是希望通過(guò)以下這些例子的展示讓讀者對壞代碼有直觀(guān)的感覺(jué)。
  1.不好的函數名稱(chēng)(Bad Function Name)
  如do(),這樣的函數名稱(chēng)沒(méi)有多少信息量;又如myFunc(),這樣的函數名稱(chēng),個(gè)人色彩過(guò)于強烈,也沒(méi)有足夠的信息量。
  2.不好的變量名稱(chēng)(Bad Variable Name)
  如a、b、c、i、j、k、temp,這樣的變量名稱(chēng)在很多教科書(shū)中經(jīng)常出現,很多人在上學(xué)期間寫(xiě)代碼時(shí)也會(huì )經(jīng)常這樣用。如果作為局部變量,這樣的名稱(chēng)有時(shí)是可以接受的;但如果作為作用域稍微大的變量,這樣的名稱(chēng)就非常不可取了。
  3.沒(méi)有注釋?zhuān)∟o Comments)
  有寫(xiě)注釋習慣的軟件工程師很少,很多軟件工程師認為寫(xiě)注釋是浪費時(shí)間,是“額外”的工作。但是沒(méi)有注釋的代碼,閱讀的成本會(huì )比較高。
  4.函數不是單一目的(The Function has No Single Purpose)
  如LoadFromFileAndCalculate()。這個(gè)例子是我編造的,但現實(shí)中這樣的函數其實(shí)不少。很多函數在首次寫(xiě)出來(lái)的時(shí)候,就很難表述清楚其用途;還有一些函數隨著(zhù)功能的擴展,變得越來(lái)越龐雜,也就慢慢地說(shuō)不清它的目的了。
  這方面的問(wèn)題可能很多人都沒(méi)有充分地認識到——非單一目的的函數難以維護,也難以復用。
  5.不好的排版(Bad Layout)
  不少人認為,程序可以正常執行就行了,所以一些軟件工程師不重視對代碼的排版,認為這僅僅是一種“形式”。
  沒(méi)有排好版的程序,在閱讀效率方面會(huì )帶來(lái)嚴重問(wèn)題。這里舉一個(gè)極端的例子:對于C語(yǔ)言來(lái)說(shuō),“;”可作為語(yǔ)句的分割符,而“縮進(jìn)”和“換行”對于編譯器來(lái)說(shuō)是無(wú)用的,所以完全可以把一段C語(yǔ)言程序都“壓縮”在一行內。這樣的程序是可以運行的,但是對人來(lái)說(shuō),可讀性非常差。這樣的程序肯定是我們非常不希望看到的。
  6.無(wú)法測試(None Testable)
  程序的正確性要依賴(lài)測試來(lái)保證(雖然測試并不能保證程序完全無(wú)錯)。無(wú)法或不好為之編寫(xiě)測試用例的程序,是很難有質(zhì)量保證的。
  好代碼從哪里來(lái)
  上一節說(shuō)明了好代碼的特性,本節來(lái)分析好代碼是如何產(chǎn)出的。
  ▊ 好代碼不止于編碼
  好代碼從哪里來(lái)?
  對于這個(gè)問(wèn)題,很多讀者肯定會(huì )說(shuō):“好代碼肯定是寫(xiě)出來(lái)的呀?!?
  我曾做過(guò)多次調研,發(fā)現很多軟件工程師日常所讀的書(shū)確實(shí)是和“寫(xiě)代碼”緊密相關(guān)的。
  但是,這里要告訴讀者的是,代碼不只是“寫(xiě)”出來(lái)的。在很多年前,我所讀的軟件工程方面的教科書(shū)就告訴我,編碼的時(shí)間一般只占一個(gè)項目所花時(shí)間的 10%。我曾說(shuō)過(guò)一句比較有趣的話(huà):
  “如果一個(gè)從業(yè)者告訴你,他的大部分時(shí)間都在寫(xiě)代碼,那么他大概率不是一個(gè)高級軟件工程師?!?
  那么,軟件工程師的時(shí)間都花到哪里去了呢?軟件工程師的時(shí)間應該花在哪里呢?
  好的代碼是多個(gè)工作環(huán)節的綜合結果。
 ?。?)在編碼前,需要做好需求分析和系統設計。而這兩項工作是經(jīng)常被大量軟件工程師忽略或輕視的環(huán)節。
 ?。?)在編碼時(shí),需要編寫(xiě)代碼和編寫(xiě)單元測試。對于“編寫(xiě)代碼”,讀者都了解;而對于“編寫(xiě)單元測試”,有些軟件工程師就不認同了,甚至還有人誤以為單元測試是由測試工程師來(lái)編寫(xiě)的。
 ?。?)在編碼后,要做集成測試、上線(xiàn),以及持續運營(yíng)/迭代改進(jìn)。這幾件事情都是要花費不少精力的,比如上線(xiàn),不僅僅要做程序部署,而且要考慮程序是如何被監控的。有時(shí),為了一段程序的上線(xiàn),設計和實(shí)施監控的方案要花費好幾天才能完成。
  因此,一個(gè)好的系統或產(chǎn)品是以上這些環(huán)節持續循環(huán)執行的結果。
  ▊ 需求分析和系統設計
  1.幾種常見(jiàn)的錯誤現象
  相對于編碼工作,需求分析和系統設計是兩個(gè)經(jīng)常被忽視的環(huán)節。在現實(shí)工作中,我們經(jīng)常會(huì )看到以下這些現象。
 ?。?)很多人錯誤地認為,寫(xiě)代碼才是最重要的事情。不少軟件工程師如果一天沒(méi)有寫(xiě)出幾行代碼,就會(huì )認為工作沒(méi)有進(jìn)展;很多管理者也會(huì )以代碼的產(chǎn)出量作為衡量工作結果的主要標準,催促軟件工程師盡早開(kāi)始寫(xiě)代碼。
 ?。?)有太多的從業(yè)者,在沒(méi)有搞清楚項目目標之前就已經(jīng)開(kāi)始編碼了。在很多時(shí)候,項目目標都是通過(guò)并不準確的口頭溝通來(lái)確定的。例如:
  “需要做什么?”
  “就按照×××網(wǎng)站的做一個(gè)吧?!?
 ?。?)有太多的從業(yè)者,在代碼編寫(xiě)基本完成后,才發(fā)現設計思路是有問(wèn)題的。他們在很多項目上花費很少(甚至沒(méi)有花費)時(shí)間進(jìn)行系統設計,對于在設計中所隱藏的問(wèn)題并沒(méi)有仔細思考和求證?;谶@樣的設計投入和設計質(zhì)量,項目出現設計失誤也是很難避免的。而面對一個(gè)已經(jīng)完成了基本編碼的項目,如果要“動(dòng)大手術(shù)”來(lái)修改它,相信每個(gè)有過(guò)類(lèi)似經(jīng)歷的人都一定深知那種感受——越改越亂,越改越著(zhù)急。
  以上這幾種情況,很多讀者是不是都有過(guò)類(lèi)似經(jīng)歷?
  2.研發(fā)前期多投入,收益更大
  關(guān)于軟件研發(fā),首先我們需要建立一個(gè)非常重要的觀(guān)念。
  在研發(fā)前期(需求分析和系統設計)多投入資源,相對于把資源都投入在研發(fā)后期(編碼、測試等),其收益更大。
  這是為什么呢?
  要回答這個(gè)問(wèn)題,需要從軟件研發(fā)全生命周期的角度來(lái)考量軟件研發(fā)的成本。除編碼外,軟件測試、上線(xiàn)、調試等都需要很高成本。如果我們把需求搞錯了,那么與錯誤需求有關(guān)的設計、編碼、測試、上線(xiàn)等成本就都浪費了;如果我們把設計搞錯了,那么與錯誤設計相關(guān)的編碼、測試、上線(xiàn)的成本也就浪費了。
  如果仔細考量那些低效的項目,會(huì )發(fā)現有非常多的類(lèi)似于上面提到的“浪費”的地方。軟件工程師似乎都很忙,但是在錯誤方向上所做的所有努力并不會(huì )產(chǎn)生任何價(jià)值,而大部分的加班實(shí)際上是在做錯誤的事情,或者是為了補救錯誤而努力。在這種情況下,將更多的資源和注意力向研發(fā)前期傾斜會(huì )立刻收到良好的效果。
  3.修改代碼和修改文檔,哪個(gè)成本更高
  很多軟件工程師不愿意做需求分析和系統設計,是因為對“寫(xiě)文檔”有著(zhù)根深蒂固的偏見(jiàn)。這里問(wèn)大家一個(gè)問(wèn)題,如果大家對這個(gè)問(wèn)題能給出正確的回答,那么在“寫(xiě)文檔”的意識方面,一定會(huì )有很大的轉變。
  任何人都不是神仙,無(wú)法一次就把所有事情做對。對于一段程序來(lái)說(shuō),它一定要經(jīng)過(guò)一定周期的修改和迭代。這時(shí)有兩種選擇:
  選擇一:修改文檔。在設計文檔時(shí)完成迭代調整,待沒(méi)有大問(wèn)題后再開(kāi)始編碼。
  選擇二:修改代碼。只有粗略的設計文檔,或者沒(méi)有設計文檔,直接開(kāi)始編碼,所有的迭代調整都在代碼上完成。
  請大家判斷,修改代碼和修改文檔,哪個(gè)成本更高?
  在之前的一些分享交流會(huì )上,對于這個(gè)問(wèn)題,有人會(huì )說(shuō),修改文檔的成本更高。因為在修改文檔后還要修改代碼,多了一道手續。而直接修改代碼,只需要做一次,這樣更直接。
  這個(gè)回答說(shuō)明了回答者沒(méi)有充分理解“先寫(xiě)文檔,后寫(xiě)代碼”的設計方法。如果沒(méi)有充分重視設計文檔的工作,在輸出的設計文檔質(zhì)量不高的情況下就開(kāi)始編碼,確實(shí)會(huì )出現以上提到的問(wèn)題。但是,如果在設計文檔階段就已經(jīng)做了充分考慮,會(huì )減少對代碼的迭代和反復。
  對于同樣的設計修改,“修改代碼”的成本遠高于“修改文檔”。這是因為,在設計文檔中只會(huì )涉及主要的邏輯,那些細小的、顯而易見(jiàn)的邏輯不會(huì )在設計文檔中出現。在修改設計文檔時(shí),也只會(huì )影響到這些主要邏輯。而如果在代碼中做修改,不僅會(huì )涉及這些主要邏輯,而且會(huì )涉及那些在文檔中不會(huì )出現的細小邏輯。對于一段程序來(lái)說(shuō),任何一個(gè)邏輯出現問(wèn)題,程序都是無(wú)法正常運行的。
  4.需求分析和系統設計之間的差別
  很多讀者無(wú)法清楚地區分“需求分析”和“系統設計”之間的差別,于是會(huì )發(fā)現,在寫(xiě)出的文檔中,有些需求分析文檔里出現了系統設計的內容,而有些系統設計文檔里又混雜了需求分析的內容。
  我們用幾句話(huà)可以非常明確地給出二者的差異。
 ?。?)需求分析:定義系統/軟件的黑盒的行為,它是從外部(External)看到的,在說(shuō)明“是什么”(What)。
 ?。?)系統設計:設計系統/軟件的白盒的機制,它是從內部(Internal)看到的,要說(shuō)明“怎么做”(How)和“為什么”(Why)。
  比如,對一輛汽車(chē)來(lái)說(shuō),首先使用者從外部可以看到車(chē)廂、車(chē)輪,坐在車(chē)里可以看到方向盤(pán)、剎車(chē)踏板、油門(mén)踏板等;操作方向盤(pán)可以改變汽車(chē)的行駛方向,腳踩剎車(chē)踏板、油門(mén)踏板可用于減速和加速。以上這些是對汽車(chē)的“需求分析”。
  然后,我們想象汽車(chē)外殼和內部變成了透明的,可以看到汽車(chē)內部的發(fā)動(dòng)機、變速箱、傳動(dòng)桿、與剎車(chē)相關(guān)的內部裝置等。而這些對駕駛者來(lái)說(shuō)是不可見(jiàn)的,它們是對汽車(chē)的“系統設計”。
  -End-
  最近有一些小伙伴,讓我幫忙找一些面試題資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!
  
  點(diǎn)擊卡片,關(guān)注后回復【面試題】即可獲取
  在看點(diǎn)這里
   查看全部

  文章采集調用 好代碼和壞代碼
  關(guān)注后回復“進(jìn)群”,拉你進(jìn)程序員交流群
  要寫(xiě)出好代碼,首先需要提升品位。
  很多軟件工程師寫(xiě)不好代碼,在評審他人的代碼時(shí)也看不出問(wèn)題,就是因為缺乏對好代碼標準的認識。
  現在還有太多的軟件工程師認為,代碼只要可以正確執行就可以了。這是一種非常低的評價(jià)標準,很多重要的方面都被忽視了。
  好代碼的特性
  好代碼具有以下特性。
  1. 魯棒(Solid and Robust)
  代碼不僅要被正確執行,我們還要考慮對各種錯誤情況的處理,比如各種系統調用和函數調用的異常情況,系統相關(guān)組件的異常和錯誤。
  對很多產(chǎn)品級的程序來(lái)說(shuō),異常和錯誤處理的邏輯占了很大比例。
  2. 高效(Fast)
  程序的運行應使用盡量少的資源。資源不僅僅包括CPU,還可能包括存儲、I/O等。
  設計高效的程序,會(huì )運用到數據結構和算法方面的知識,同時(shí)要考慮到程序運行時(shí)的各種約束條件。
  3. 簡(jiǎn)潔(Maintainable and Simple)
  代碼的邏輯要盡量簡(jiǎn)明易懂,代碼要具有很好的可維護性。對于同樣的目標,能夠使用簡(jiǎn)單清楚的方法達成,就不要使用復雜晦澀的方法。
  “大道至簡(jiǎn)”,能否把復雜的問(wèn)題用簡(jiǎn)單的方式實(shí)現出來(lái),這是一種編程水平的體現。
  4. 簡(jiǎn)短(Small)
  在某種意義上,代碼的復雜度和維護成本是和代碼的規模直接相關(guān)的。在實(shí)現同樣功能的時(shí)候,要盡量將代碼寫(xiě)得簡(jiǎn)短一些。
  簡(jiǎn)潔高于簡(jiǎn)短。這里要注意,某些人為了能把代碼寫(xiě)得簡(jiǎn)短,使用了一些晦澀難懂的描述方式,降低了代碼的可讀性。這種方式是不可取的。
  5. 可測試(Testable)
  代碼的正確性要通過(guò)測試來(lái)保證,尤其是在敏捷的場(chǎng)景下,更需要依賴(lài)可自動(dòng)回歸執行的測試用例。
  在代碼的設計中,要考慮如何使代碼可測、易測。一個(gè)比較好的實(shí)踐是使用TDD(Test-Driven Development,測試驅動(dòng)開(kāi)發(fā))的方法,這樣在編寫(xiě)測試用例的時(shí)候會(huì )很快發(fā)現代碼在可測試性方面的問(wèn)題。
  6. 共享(Re-Usable)
  大量的程序實(shí)際上都使用了類(lèi)似的框架或邏輯。由于目前開(kāi)源代碼的大量普及,很多功能并不需要重復開(kāi)發(fā),只進(jìn)行引用和使用即可。
  在一個(gè)組織內部,應鼓勵共享和重用代碼,這樣可以有效降低代碼研發(fā)的成本,并提升代碼的質(zhì)量。
  實(shí)現代碼的共享,不僅需要在意識方面提升,還需要具有相關(guān)的能力(如編寫(xiě)獨立、高質(zhì)量的代碼庫)及相關(guān)基礎設施的支持(如代碼搜索、代碼引用機制)。
  7. 可移植(Portable)
  某些程序需要在多種操作系統下運行,在這種情況下,代碼的可移植性成為一種必需的能力。
  要讓代碼具有可移植性,需要對所運行的各種操作系統底層有充分的理解和統一抽象。一般會(huì )使用一個(gè)適配層來(lái)屏蔽操作系統底層的差異。
  一些編程語(yǔ)言也提供了多操作系統的可移植性,如很多基于Python語(yǔ)言、Java語(yǔ)言、Go語(yǔ)言編寫(xiě)的程序,都可以跨平臺運行。
  8. 可觀(guān)測(Observable)/ 可監控(Monitorable)
  面對目前大量存在的在線(xiàn)服務(wù)(Online Service)程序,需要具備對程序的運行狀態(tài)進(jìn)行細致而持續監控的能力。
  這要求在程序設計時(shí)就提供相關(guān)的機制,包括程序狀態(tài)的收集、保存和對外輸出。
  9. 可運維(Operational)
  可運維已經(jīng)成為軟件研發(fā)活動(dòng)的重要組成部分,可運維重點(diǎn)關(guān)注成本、效率和穩定性三個(gè)方面。
  程序的可運維性和程序的設計、編寫(xiě)緊密相關(guān),如果在程序設計階段就沒(méi)有考慮可運維性,那么程序運行的運維目標則難以達成。
  10. 可擴展(Scalable andExtensible)
  可擴展包含“容量可擴展”(Scalable)和“功能可擴展”(Extensible)兩方面。
  在互聯(lián)網(wǎng)公司的系統設計中,“容量可擴展”是重要的設計目標之一。系統要盡量支持通過(guò)增加資源來(lái)實(shí)現容量的線(xiàn)性提高。
  快速響應需求的變化,是互聯(lián)網(wǎng)公司的另外一個(gè)重要挑戰??煽紤]使用插件式的程序設計方式,以容納未來(lái)可能新增的功能,也可考慮使用類(lèi)似Protocol Buffer 這樣的工具,支持對協(xié)議新增字段。
  以上十條標準,如果要記住,可能有些困難。我們可以把它們歸納為四個(gè)方面,見(jiàn)表1。
  表1對一流代碼特性的匯總分類(lèi)
  
  壞代碼的例子
  關(guān)于好代碼,上面介紹了一些特性,本節也給出壞代碼(Bad Code)的幾個(gè)例子。關(guān)于壞代碼,本書(shū)沒(méi)有做系統性總結,只是希望通過(guò)以下這些例子的展示讓讀者對壞代碼有直觀(guān)的感覺(jué)。
  1.不好的函數名稱(chēng)(Bad Function Name)
  如do(),這樣的函數名稱(chēng)沒(méi)有多少信息量;又如myFunc(),這樣的函數名稱(chēng),個(gè)人色彩過(guò)于強烈,也沒(méi)有足夠的信息量。
  2.不好的變量名稱(chēng)(Bad Variable Name)
  如a、b、c、i、j、k、temp,這樣的變量名稱(chēng)在很多教科書(shū)中經(jīng)常出現,很多人在上學(xué)期間寫(xiě)代碼時(shí)也會(huì )經(jīng)常這樣用。如果作為局部變量,這樣的名稱(chēng)有時(shí)是可以接受的;但如果作為作用域稍微大的變量,這樣的名稱(chēng)就非常不可取了。
  3.沒(méi)有注釋?zhuān)∟o Comments)
  有寫(xiě)注釋習慣的軟件工程師很少,很多軟件工程師認為寫(xiě)注釋是浪費時(shí)間,是“額外”的工作。但是沒(méi)有注釋的代碼,閱讀的成本會(huì )比較高。
  4.函數不是單一目的(The Function has No Single Purpose)
  如LoadFromFileAndCalculate()。這個(gè)例子是我編造的,但現實(shí)中這樣的函數其實(shí)不少。很多函數在首次寫(xiě)出來(lái)的時(shí)候,就很難表述清楚其用途;還有一些函數隨著(zhù)功能的擴展,變得越來(lái)越龐雜,也就慢慢地說(shuō)不清它的目的了。
  這方面的問(wèn)題可能很多人都沒(méi)有充分地認識到——非單一目的的函數難以維護,也難以復用。
  5.不好的排版(Bad Layout)
  不少人認為,程序可以正常執行就行了,所以一些軟件工程師不重視對代碼的排版,認為這僅僅是一種“形式”。
  沒(méi)有排好版的程序,在閱讀效率方面會(huì )帶來(lái)嚴重問(wèn)題。這里舉一個(gè)極端的例子:對于C語(yǔ)言來(lái)說(shuō),“;”可作為語(yǔ)句的分割符,而“縮進(jìn)”和“換行”對于編譯器來(lái)說(shuō)是無(wú)用的,所以完全可以把一段C語(yǔ)言程序都“壓縮”在一行內。這樣的程序是可以運行的,但是對人來(lái)說(shuō),可讀性非常差。這樣的程序肯定是我們非常不希望看到的。
  6.無(wú)法測試(None Testable)
  程序的正確性要依賴(lài)測試來(lái)保證(雖然測試并不能保證程序完全無(wú)錯)。無(wú)法或不好為之編寫(xiě)測試用例的程序,是很難有質(zhì)量保證的。
  好代碼從哪里來(lái)
  上一節說(shuō)明了好代碼的特性,本節來(lái)分析好代碼是如何產(chǎn)出的。
  ▊ 好代碼不止于編碼
  好代碼從哪里來(lái)?
  對于這個(gè)問(wèn)題,很多讀者肯定會(huì )說(shuō):“好代碼肯定是寫(xiě)出來(lái)的呀?!?
  我曾做過(guò)多次調研,發(fā)現很多軟件工程師日常所讀的書(shū)確實(shí)是和“寫(xiě)代碼”緊密相關(guān)的。
  但是,這里要告訴讀者的是,代碼不只是“寫(xiě)”出來(lái)的。在很多年前,我所讀的軟件工程方面的教科書(shū)就告訴我,編碼的時(shí)間一般只占一個(gè)項目所花時(shí)間的 10%。我曾說(shuō)過(guò)一句比較有趣的話(huà):
  “如果一個(gè)從業(yè)者告訴你,他的大部分時(shí)間都在寫(xiě)代碼,那么他大概率不是一個(gè)高級軟件工程師?!?
  那么,軟件工程師的時(shí)間都花到哪里去了呢?軟件工程師的時(shí)間應該花在哪里呢?
  好的代碼是多個(gè)工作環(huán)節的綜合結果。
 ?。?)在編碼前,需要做好需求分析和系統設計。而這兩項工作是經(jīng)常被大量軟件工程師忽略或輕視的環(huán)節。
 ?。?)在編碼時(shí),需要編寫(xiě)代碼和編寫(xiě)單元測試。對于“編寫(xiě)代碼”,讀者都了解;而對于“編寫(xiě)單元測試”,有些軟件工程師就不認同了,甚至還有人誤以為單元測試是由測試工程師來(lái)編寫(xiě)的。
 ?。?)在編碼后,要做集成測試、上線(xiàn),以及持續運營(yíng)/迭代改進(jìn)。這幾件事情都是要花費不少精力的,比如上線(xiàn),不僅僅要做程序部署,而且要考慮程序是如何被監控的。有時(shí),為了一段程序的上線(xiàn),設計和實(shí)施監控的方案要花費好幾天才能完成。
  因此,一個(gè)好的系統或產(chǎn)品是以上這些環(huán)節持續循環(huán)執行的結果。
  ▊ 需求分析和系統設計
  1.幾種常見(jiàn)的錯誤現象
  相對于編碼工作,需求分析和系統設計是兩個(gè)經(jīng)常被忽視的環(huán)節。在現實(shí)工作中,我們經(jīng)常會(huì )看到以下這些現象。
 ?。?)很多人錯誤地認為,寫(xiě)代碼才是最重要的事情。不少軟件工程師如果一天沒(méi)有寫(xiě)出幾行代碼,就會(huì )認為工作沒(méi)有進(jìn)展;很多管理者也會(huì )以代碼的產(chǎn)出量作為衡量工作結果的主要標準,催促軟件工程師盡早開(kāi)始寫(xiě)代碼。
 ?。?)有太多的從業(yè)者,在沒(méi)有搞清楚項目目標之前就已經(jīng)開(kāi)始編碼了。在很多時(shí)候,項目目標都是通過(guò)并不準確的口頭溝通來(lái)確定的。例如:
  “需要做什么?”
  “就按照×××網(wǎng)站的做一個(gè)吧?!?
 ?。?)有太多的從業(yè)者,在代碼編寫(xiě)基本完成后,才發(fā)現設計思路是有問(wèn)題的。他們在很多項目上花費很少(甚至沒(méi)有花費)時(shí)間進(jìn)行系統設計,對于在設計中所隱藏的問(wèn)題并沒(méi)有仔細思考和求證?;谶@樣的設計投入和設計質(zhì)量,項目出現設計失誤也是很難避免的。而面對一個(gè)已經(jīng)完成了基本編碼的項目,如果要“動(dòng)大手術(shù)”來(lái)修改它,相信每個(gè)有過(guò)類(lèi)似經(jīng)歷的人都一定深知那種感受——越改越亂,越改越著(zhù)急。
  以上這幾種情況,很多讀者是不是都有過(guò)類(lèi)似經(jīng)歷?
  2.研發(fā)前期多投入,收益更大
  關(guān)于軟件研發(fā),首先我們需要建立一個(gè)非常重要的觀(guān)念。
  在研發(fā)前期(需求分析和系統設計)多投入資源,相對于把資源都投入在研發(fā)后期(編碼、測試等),其收益更大。
  這是為什么呢?
  要回答這個(gè)問(wèn)題,需要從軟件研發(fā)全生命周期的角度來(lái)考量軟件研發(fā)的成本。除編碼外,軟件測試、上線(xiàn)、調試等都需要很高成本。如果我們把需求搞錯了,那么與錯誤需求有關(guān)的設計、編碼、測試、上線(xiàn)等成本就都浪費了;如果我們把設計搞錯了,那么與錯誤設計相關(guān)的編碼、測試、上線(xiàn)的成本也就浪費了。
  如果仔細考量那些低效的項目,會(huì )發(fā)現有非常多的類(lèi)似于上面提到的“浪費”的地方。軟件工程師似乎都很忙,但是在錯誤方向上所做的所有努力并不會(huì )產(chǎn)生任何價(jià)值,而大部分的加班實(shí)際上是在做錯誤的事情,或者是為了補救錯誤而努力。在這種情況下,將更多的資源和注意力向研發(fā)前期傾斜會(huì )立刻收到良好的效果。
  3.修改代碼和修改文檔,哪個(gè)成本更高
  很多軟件工程師不愿意做需求分析和系統設計,是因為對“寫(xiě)文檔”有著(zhù)根深蒂固的偏見(jiàn)。這里問(wèn)大家一個(gè)問(wèn)題,如果大家對這個(gè)問(wèn)題能給出正確的回答,那么在“寫(xiě)文檔”的意識方面,一定會(huì )有很大的轉變。
  任何人都不是神仙,無(wú)法一次就把所有事情做對。對于一段程序來(lái)說(shuō),它一定要經(jīng)過(guò)一定周期的修改和迭代。這時(shí)有兩種選擇:
  選擇一:修改文檔。在設計文檔時(shí)完成迭代調整,待沒(méi)有大問(wèn)題后再開(kāi)始編碼。
  選擇二:修改代碼。只有粗略的設計文檔,或者沒(méi)有設計文檔,直接開(kāi)始編碼,所有的迭代調整都在代碼上完成。
  請大家判斷,修改代碼和修改文檔,哪個(gè)成本更高?
  在之前的一些分享交流會(huì )上,對于這個(gè)問(wèn)題,有人會(huì )說(shuō),修改文檔的成本更高。因為在修改文檔后還要修改代碼,多了一道手續。而直接修改代碼,只需要做一次,這樣更直接。
  這個(gè)回答說(shuō)明了回答者沒(méi)有充分理解“先寫(xiě)文檔,后寫(xiě)代碼”的設計方法。如果沒(méi)有充分重視設計文檔的工作,在輸出的設計文檔質(zhì)量不高的情況下就開(kāi)始編碼,確實(shí)會(huì )出現以上提到的問(wèn)題。但是,如果在設計文檔階段就已經(jīng)做了充分考慮,會(huì )減少對代碼的迭代和反復。
  對于同樣的設計修改,“修改代碼”的成本遠高于“修改文檔”。這是因為,在設計文檔中只會(huì )涉及主要的邏輯,那些細小的、顯而易見(jiàn)的邏輯不會(huì )在設計文檔中出現。在修改設計文檔時(shí),也只會(huì )影響到這些主要邏輯。而如果在代碼中做修改,不僅會(huì )涉及這些主要邏輯,而且會(huì )涉及那些在文檔中不會(huì )出現的細小邏輯。對于一段程序來(lái)說(shuō),任何一個(gè)邏輯出現問(wèn)題,程序都是無(wú)法正常運行的。
  4.需求分析和系統設計之間的差別
  很多讀者無(wú)法清楚地區分“需求分析”和“系統設計”之間的差別,于是會(huì )發(fā)現,在寫(xiě)出的文檔中,有些需求分析文檔里出現了系統設計的內容,而有些系統設計文檔里又混雜了需求分析的內容。
  我們用幾句話(huà)可以非常明確地給出二者的差異。
 ?。?)需求分析:定義系統/軟件的黑盒的行為,它是從外部(External)看到的,在說(shuō)明“是什么”(What)。
 ?。?)系統設計:設計系統/軟件的白盒的機制,它是從內部(Internal)看到的,要說(shuō)明“怎么做”(How)和“為什么”(Why)。
  比如,對一輛汽車(chē)來(lái)說(shuō),首先使用者從外部可以看到車(chē)廂、車(chē)輪,坐在車(chē)里可以看到方向盤(pán)、剎車(chē)踏板、油門(mén)踏板等;操作方向盤(pán)可以改變汽車(chē)的行駛方向,腳踩剎車(chē)踏板、油門(mén)踏板可用于減速和加速。以上這些是對汽車(chē)的“需求分析”。
  然后,我們想象汽車(chē)外殼和內部變成了透明的,可以看到汽車(chē)內部的發(fā)動(dòng)機、變速箱、傳動(dòng)桿、與剎車(chē)相關(guān)的內部裝置等。而這些對駕駛者來(lái)說(shuō)是不可見(jiàn)的,它們是對汽車(chē)的“系統設計”。
  -End-
  最近有一些小伙伴,讓我幫忙找一些面試題資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!
  
  點(diǎn)擊卡片,關(guān)注后回復【面試題】即可獲取
  在看點(diǎn)這里
  

V8 中的垃圾收集(GC),圖文指南

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 108 次瀏覽 ? 2022-05-14 06:27 ? 來(lái)自相關(guān)話(huà)題

  V8 中的垃圾收集(GC),圖文指南
  原文標題:Garbage collection in V8, an illustrated guide原文鏈接:@_lrlna/garbage-collection-in-v8-an-illustrated-guide-d24a952ee3b8
  本指南與我迄今為止所寫(xiě)的其他指南都不同,我在里面添加了一些草圖。我用草圖描繪了垃圾收集(GC)的整個(gè)概念以及它是如何在 javascript 中被處理的,更確切地說(shuō)是在運行 javascript 的引擎中。順便提一下,這個(gè)指南是面向初學(xué)者的,不包括 V8 內存管理的各個(gè)方面以及 V8 的內部原理。我添加了一些資源,可以幫助你更深入地了解。本指南重點(diǎn)介紹?javascript?,對于某些語(yǔ)言而言,垃圾收集是完全不一樣的,比如 C 語(yǔ)言.
  好的,我們開(kāi)始吧。
  什么是v8?
  V8,是一個(gè) JavaScript 的運行時(shí)引擎,不要與你最喜愛(ài)的番茄汁混淆了,它負責編譯并執行你精美的javascript。V8 帶有分代垃圾收集器,我將在后文解釋。它與 Chrome 一起,而 SpiderMonkey 是 Mozilla 的引擎 Chakra 是微軟的?;旧袭斶\行 javascript 時(shí),您需要一個(gè)引擎來(lái)處理它,而且 V8 是您的選擇之一,無(wú)論是在瀏覽器還是在 node.js 環(huán)境中。(P.S. V8 是? 開(kāi)源的 ?。)
  什么是垃圾收集?
  垃圾收集的重點(diǎn)是通過(guò)使用特定的程序來(lái)管理內存的使用。諸如 C 之類(lèi)的語(yǔ)言通??梢灾苯硬僮鞒绦蛑械膬却?,并在程序的上下文中分配和釋放對象。另一方面,ECMAScript 缺少訪(fǎng)問(wèn)內存管理的特定接口(是的,這意味著(zhù)沒(méi)有API)。這基本上意味著(zhù)程序中的所有內存管理權限都被轉移到了 V8。
  由于我們無(wú)法訪(fǎng)問(wèn)無(wú)限量的內存,因此垃圾收集器的工作是通過(guò)內存中分配的對象來(lái)確定它們是否死亡或是活動(dòng)。那些活著(zhù)的對象會(huì )留在內存中,那些死亡的對象被刪除,內存被分配回堆。
  什么是堆?堆是非結構化區域,堆中的對象占用分配的內存。這種分配是動(dòng)態(tài)的,因為對象的大小/壽命/數量是未知的,所以需要在運行時(shí)分配和釋放。
  如果我們看一下并發(fā)模型,堆直接與調用棧一起工作,因為堆棧中的對象需要進(jìn)行內存分配。它看起來(lái)像這樣:
  
  Dead or alive?
  如何檢查對象的生死,是通過(guò)客戶(hù)機或者程序代碼是否可以到達此對象。您可以想到的最容易達到的對象可能是根范圍中定義的對象。
  一些 C++ 綁定(或客戶(hù)端上的 Web API)也是根的一部分,因此您可以通過(guò)例如 setInterval 直接訪(fǎng)問(wèn)。
  可達性(Reachability)還可以這么理解:另一個(gè)對象或根是否可以獲得它,如果可以的話(huà),該對象所需的內存被保留。
  那么我們怎么可以做到垃圾收集呢?(告訴我!告訴我?。?
  創(chuàng )建新對象或新的“指針”時(shí),V8 會(huì )在堆中分配內存。(javascript 沒(méi)有真正的指針,所以'指針'在技術(shù)上只是復制對原始對象的引用)。堆中的不同類(lèi)型的對象會(huì )占用不同的空間,它將被組織成如下:
  
  為了垃圾回收的目的,V8 將堆分為兩部分:新生區和老生區。當您執行需要 V8 分配內存的操作時(shí),V8 將在新生區中分配空間。當你繼續添加到堆,你最終會(huì )耗盡內存,所以 V8 將不得不運行一個(gè) GC 來(lái)清理。新創(chuàng )建的對象被分配得很快,并且當對象死亡時(shí)被清理(更短和更快的收集)。一旦對象“生存”了一些(確切的說(shuō)是2個(gè)周期)回收掃描周期時(shí),它們被提升到老生區,在一個(gè)單獨的循環(huán)中收集垃圾。
  
  較舊的對象是幸存多于一個(gè)垃圾收集掃描的對象,這意味著(zhù)它們仍然被其他對象引用,并且仍然需要占用該內存。他們通常不引用較年輕的對象,只是引用較舊的對象。大周期進(jìn)行的并不頻繁。一次大周期通常是在移動(dòng)足夠多的對象至老生區后才會(huì )發(fā)生。
  sources.js
  This guide is crossposted from lrlna’s sketchin guide on github ? . 查看全部

  V8 中的垃圾收集(GC),圖文指南
  原文標題:Garbage collection in V8, an illustrated guide原文鏈接:@_lrlna/garbage-collection-in-v8-an-illustrated-guide-d24a952ee3b8
  本指南與我迄今為止所寫(xiě)的其他指南都不同,我在里面添加了一些草圖。我用草圖描繪了垃圾收集(GC)的整個(gè)概念以及它是如何在 javascript 中被處理的,更確切地說(shuō)是在運行 javascript 的引擎中。順便提一下,這個(gè)指南是面向初學(xué)者的,不包括 V8 內存管理的各個(gè)方面以及 V8 的內部原理。我添加了一些資源,可以幫助你更深入地了解。本指南重點(diǎn)介紹?javascript?,對于某些語(yǔ)言而言,垃圾收集是完全不一樣的,比如 C 語(yǔ)言.
  好的,我們開(kāi)始吧。
  什么是v8?
  V8,是一個(gè) JavaScript 的運行時(shí)引擎,不要與你最喜愛(ài)的番茄汁混淆了,它負責編譯并執行你精美的javascript。V8 帶有分代垃圾收集器,我將在后文解釋。它與 Chrome 一起,而 SpiderMonkey 是 Mozilla 的引擎 Chakra 是微軟的?;旧袭斶\行 javascript 時(shí),您需要一個(gè)引擎來(lái)處理它,而且 V8 是您的選擇之一,無(wú)論是在瀏覽器還是在 node.js 環(huán)境中。(P.S. V8 是? 開(kāi)源的 ?。)
  什么是垃圾收集?
  垃圾收集的重點(diǎn)是通過(guò)使用特定的程序來(lái)管理內存的使用。諸如 C 之類(lèi)的語(yǔ)言通??梢灾苯硬僮鞒绦蛑械膬却?,并在程序的上下文中分配和釋放對象。另一方面,ECMAScript 缺少訪(fǎng)問(wèn)內存管理的特定接口(是的,這意味著(zhù)沒(méi)有API)。這基本上意味著(zhù)程序中的所有內存管理權限都被轉移到了 V8。
  由于我們無(wú)法訪(fǎng)問(wèn)無(wú)限量的內存,因此垃圾收集器的工作是通過(guò)內存中分配的對象來(lái)確定它們是否死亡或是活動(dòng)。那些活著(zhù)的對象會(huì )留在內存中,那些死亡的對象被刪除,內存被分配回堆。
  什么是堆?堆是非結構化區域,堆中的對象占用分配的內存。這種分配是動(dòng)態(tài)的,因為對象的大小/壽命/數量是未知的,所以需要在運行時(shí)分配和釋放。
  如果我們看一下并發(fā)模型,堆直接與調用棧一起工作,因為堆棧中的對象需要進(jìn)行內存分配。它看起來(lái)像這樣:
  
  Dead or alive?
  如何檢查對象的生死,是通過(guò)客戶(hù)機或者程序代碼是否可以到達此對象。您可以想到的最容易達到的對象可能是根范圍中定義的對象。
  一些 C++ 綁定(或客戶(hù)端上的 Web API)也是根的一部分,因此您可以通過(guò)例如 setInterval 直接訪(fǎng)問(wèn)。
  可達性(Reachability)還可以這么理解:另一個(gè)對象或根是否可以獲得它,如果可以的話(huà),該對象所需的內存被保留。
  那么我們怎么可以做到垃圾收集呢?(告訴我!告訴我?。?
  創(chuàng )建新對象或新的“指針”時(shí),V8 會(huì )在堆中分配內存。(javascript 沒(méi)有真正的指針,所以'指針'在技術(shù)上只是復制對原始對象的引用)。堆中的不同類(lèi)型的對象會(huì )占用不同的空間,它將被組織成如下:
  
  為了垃圾回收的目的,V8 將堆分為兩部分:新生區和老生區。當您執行需要 V8 分配內存的操作時(shí),V8 將在新生區中分配空間。當你繼續添加到堆,你最終會(huì )耗盡內存,所以 V8 將不得不運行一個(gè) GC 來(lái)清理。新創(chuàng )建的對象被分配得很快,并且當對象死亡時(shí)被清理(更短和更快的收集)。一旦對象“生存”了一些(確切的說(shuō)是2個(gè)周期)回收掃描周期時(shí),它們被提升到老生區,在一個(gè)單獨的循環(huán)中收集垃圾。
  
  較舊的對象是幸存多于一個(gè)垃圾收集掃描的對象,這意味著(zhù)它們仍然被其他對象引用,并且仍然需要占用該內存。他們通常不引用較年輕的對象,只是引用較舊的對象。大周期進(jìn)行的并不頻繁。一次大周期通常是在移動(dòng)足夠多的對象至老生區后才會(huì )發(fā)生。
  sources.js
  This guide is crossposted from lrlna’s sketchin guide on github ? .

文章采集調用 好代碼和壞代碼

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 177 次瀏覽 ? 2022-05-10 05:21 ? 來(lái)自相關(guān)話(huà)題

  文章采集調用 好代碼和壞代碼
  關(guān)注后回復“進(jìn)群”,拉你進(jìn)程序員交流群
  要寫(xiě)出好代碼,首先需要提升品位。
  很多軟件工程師寫(xiě)不好代碼,在評審他人的代碼時(shí)也看不出問(wèn)題,就是因為缺乏對好代碼標準的認識。
  現在還有太多的軟件工程師認為,代碼只要可以正確執行就可以了。這是一種非常低的評價(jià)標準,很多重要的方面都被忽視了。
  好代碼的特性
  好代碼具有以下特性。
  1. 魯棒(Solid and Robust)
  代碼不僅要被正確執行,我們還要考慮對各種錯誤情況的處理,比如各種系統調用和函數調用的異常情況,系統相關(guān)組件的異常和錯誤。
  對很多產(chǎn)品級的程序來(lái)說(shuō),異常和錯誤處理的邏輯占了很大比例。
  2. 高效(Fast)
  程序的運行應使用盡量少的資源。資源不僅僅包括CPU,還可能包括存儲、I/O等。
  設計高效的程序,會(huì )運用到數據結構和算法方面的知識,同時(shí)要考慮到程序運行時(shí)的各種約束條件。
  3. 簡(jiǎn)潔(Maintainable and Simple)
  代碼的邏輯要盡量簡(jiǎn)明易懂,代碼要具有很好的可維護性。對于同樣的目標,能夠使用簡(jiǎn)單清楚的方法達成,就不要使用復雜晦澀的方法。
  “大道至簡(jiǎn)”,能否把復雜的問(wèn)題用簡(jiǎn)單的方式實(shí)現出來(lái),這是一種編程水平的體現。
  4. 簡(jiǎn)短(Small)
  在某種意義上,代碼的復雜度和維護成本是和代碼的規模直接相關(guān)的。在實(shí)現同樣功能的時(shí)候,要盡量將代碼寫(xiě)得簡(jiǎn)短一些。
  簡(jiǎn)潔高于簡(jiǎn)短。這里要注意,某些人為了能把代碼寫(xiě)得簡(jiǎn)短,使用了一些晦澀難懂的描述方式,降低了代碼的可讀性。這種方式是不可取的。
  5. 可測試(Testable)
  代碼的正確性要通過(guò)測試來(lái)保證,尤其是在敏捷的場(chǎng)景下,更需要依賴(lài)可自動(dòng)回歸執行的測試用例。
  在代碼的設計中,要考慮如何使代碼可測、易測。一個(gè)比較好的實(shí)踐是使用TDD(Test-Driven Development,測試驅動(dòng)開(kāi)發(fā))的方法,這樣在編寫(xiě)測試用例的時(shí)候會(huì )很快發(fā)現代碼在可測試性方面的問(wèn)題。
  6. 共享(Re-Usable)
  大量的程序實(shí)際上都使用了類(lèi)似的框架或邏輯。由于目前開(kāi)源代碼的大量普及,很多功能并不需要重復開(kāi)發(fā),只進(jìn)行引用和使用即可。
  在一個(gè)組織內部,應鼓勵共享和重用代碼,這樣可以有效降低代碼研發(fā)的成本,并提升代碼的質(zhì)量。
  實(shí)現代碼的共享,不僅需要在意識方面提升,還需要具有相關(guān)的能力(如編寫(xiě)獨立、高質(zhì)量的代碼庫)及相關(guān)基礎設施的支持(如代碼搜索、代碼引用機制)。
  7. 可移植(Portable)
  某些程序需要在多種操作系統下運行,在這種情況下,代碼的可移植性成為一種必需的能力。
  要讓代碼具有可移植性,需要對所運行的各種操作系統底層有充分的理解和統一抽象。一般會(huì )使用一個(gè)適配層來(lái)屏蔽操作系統底層的差異。
  一些編程語(yǔ)言也提供了多操作系統的可移植性,如很多基于Python語(yǔ)言、Java語(yǔ)言、Go語(yǔ)言編寫(xiě)的程序,都可以跨平臺運行。
  8. 可觀(guān)測(Observable)/ 可監控(Monitorable)
  面對目前大量存在的在線(xiàn)服務(wù)(Online Service)程序,需要具備對程序的運行狀態(tài)進(jìn)行細致而持續監控的能力。
  這要求在程序設計時(shí)就提供相關(guān)的機制,包括程序狀態(tài)的收集、保存和對外輸出。
  9. 可運維(Operational)
  可運維已經(jīng)成為軟件研發(fā)活動(dòng)的重要組成部分,可運維重點(diǎn)關(guān)注成本、效率和穩定性三個(gè)方面。
  程序的可運維性和程序的設計、編寫(xiě)緊密相關(guān),如果在程序設計階段就沒(méi)有考慮可運維性,那么程序運行的運維目標則難以達成。
  10. 可擴展(Scalable andExtensible)
  可擴展包含“容量可擴展”(Scalable)和“功能可擴展”(Extensible)兩方面。
  在互聯(lián)網(wǎng)公司的系統設計中,“容量可擴展”是重要的設計目標之一。系統要盡量支持通過(guò)增加資源來(lái)實(shí)現容量的線(xiàn)性提高。
  快速響應需求的變化,是互聯(lián)網(wǎng)公司的另外一個(gè)重要挑戰??煽紤]使用插件式的程序設計方式,以容納未來(lái)可能新增的功能,也可考慮使用類(lèi)似Protocol Buffer 這樣的工具,支持對協(xié)議新增字段。
  以上十條標準,如果要記住,可能有些困難。我們可以把它們歸納為四個(gè)方面,見(jiàn)表1。
  表1對一流代碼特性的匯總分類(lèi)
  
  壞代碼的例子
  關(guān)于好代碼,上面介紹了一些特性,本節也給出壞代碼(Bad Code)的幾個(gè)例子。關(guān)于壞代碼,本書(shū)沒(méi)有做系統性總結,只是希望通過(guò)以下這些例子的展示讓讀者對壞代碼有直觀(guān)的感覺(jué)。
  1.不好的函數名稱(chēng)(Bad Function Name)
  如do(),這樣的函數名稱(chēng)沒(méi)有多少信息量;又如myFunc(),這樣的函數名稱(chēng),個(gè)人色彩過(guò)于強烈,也沒(méi)有足夠的信息量。
  2.不好的變量名稱(chēng)(Bad Variable Name)
  如a、b、c、i、j、k、temp,這樣的變量名稱(chēng)在很多教科書(shū)中經(jīng)常出現,很多人在上學(xué)期間寫(xiě)代碼時(shí)也會(huì )經(jīng)常這樣用。如果作為局部變量,這樣的名稱(chēng)有時(shí)是可以接受的;但如果作為作用域稍微大的變量,這樣的名稱(chēng)就非常不可取了。
  3.沒(méi)有注釋?zhuān)∟o Comments)
  有寫(xiě)注釋習慣的軟件工程師很少,很多軟件工程師認為寫(xiě)注釋是浪費時(shí)間,是“額外”的工作。但是沒(méi)有注釋的代碼,閱讀的成本會(huì )比較高。
  4.函數不是單一目的(The Function has No Single Purpose)
  如LoadFromFileAndCalculate()。這個(gè)例子是我編造的,但現實(shí)中這樣的函數其實(shí)不少。很多函數在首次寫(xiě)出來(lái)的時(shí)候,就很難表述清楚其用途;還有一些函數隨著(zhù)功能的擴展,變得越來(lái)越龐雜,也就慢慢地說(shuō)不清它的目的了。
  這方面的問(wèn)題可能很多人都沒(méi)有充分地認識到——非單一目的的函數難以維護,也難以復用。
  5.不好的排版(Bad Layout)
  不少人認為,程序可以正常執行就行了,所以一些軟件工程師不重視對代碼的排版,認為這僅僅是一種“形式”。
  沒(méi)有排好版的程序,在閱讀效率方面會(huì )帶來(lái)嚴重問(wèn)題。這里舉一個(gè)極端的例子:對于C語(yǔ)言來(lái)說(shuō),“;”可作為語(yǔ)句的分割符,而“縮進(jìn)”和“換行”對于編譯器來(lái)說(shuō)是無(wú)用的,所以完全可以把一段C語(yǔ)言程序都“壓縮”在一行內。這樣的程序是可以運行的,但是對人來(lái)說(shuō),可讀性非常差。這樣的程序肯定是我們非常不希望看到的。
  6.無(wú)法測試(None Testable)
  程序的正確性要依賴(lài)測試來(lái)保證(雖然測試并不能保證程序完全無(wú)錯)。無(wú)法或不好為之編寫(xiě)測試用例的程序,是很難有質(zhì)量保證的。
  好代碼從哪里來(lái)
  上一節說(shuō)明了好代碼的特性,本節來(lái)分析好代碼是如何產(chǎn)出的。
  ▊ 好代碼不止于編碼
  好代碼從哪里來(lái)?
  對于這個(gè)問(wèn)題,很多讀者肯定會(huì )說(shuō):“好代碼肯定是寫(xiě)出來(lái)的呀?!?
  我曾做過(guò)多次調研,發(fā)現很多軟件工程師日常所讀的書(shū)確實(shí)是和“寫(xiě)代碼”緊密相關(guān)的。
  但是,這里要告訴讀者的是,代碼不只是“寫(xiě)”出來(lái)的。在很多年前,我所讀的軟件工程方面的教科書(shū)就告訴我,編碼的時(shí)間一般只占一個(gè)項目所花時(shí)間的 10%。我曾說(shuō)過(guò)一句比較有趣的話(huà):
  “如果一個(gè)從業(yè)者告訴你,他的大部分時(shí)間都在寫(xiě)代碼,那么他大概率不是一個(gè)高級軟件工程師?!?
  那么,軟件工程師的時(shí)間都花到哪里去了呢?軟件工程師的時(shí)間應該花在哪里呢?
  好的代碼是多個(gè)工作環(huán)節的綜合結果。
 ?。?)在編碼前,需要做好需求分析和系統設計。而這兩項工作是經(jīng)常被大量軟件工程師忽略或輕視的環(huán)節。
 ?。?)在編碼時(shí),需要編寫(xiě)代碼和編寫(xiě)單元測試。對于“編寫(xiě)代碼”,讀者都了解;而對于“編寫(xiě)單元測試”,有些軟件工程師就不認同了,甚至還有人誤以為單元測試是由測試工程師來(lái)編寫(xiě)的。
 ?。?)在編碼后,要做集成測試、上線(xiàn),以及持續運營(yíng)/迭代改進(jìn)。這幾件事情都是要花費不少精力的,比如上線(xiàn),不僅僅要做程序部署,而且要考慮程序是如何被監控的。有時(shí),為了一段程序的上線(xiàn),設計和實(shí)施監控的方案要花費好幾天才能完成。
  因此,一個(gè)好的系統或產(chǎn)品是以上這些環(huán)節持續循環(huán)執行的結果。
  ▊ 需求分析和系統設計
  1.幾種常見(jiàn)的錯誤現象
  相對于編碼工作,需求分析和系統設計是兩個(gè)經(jīng)常被忽視的環(huán)節。在現實(shí)工作中,我們經(jīng)常會(huì )看到以下這些現象。
 ?。?)很多人錯誤地認為,寫(xiě)代碼才是最重要的事情。不少軟件工程師如果一天沒(méi)有寫(xiě)出幾行代碼,就會(huì )認為工作沒(méi)有進(jìn)展;很多管理者也會(huì )以代碼的產(chǎn)出量作為衡量工作結果的主要標準,催促軟件工程師盡早開(kāi)始寫(xiě)代碼。
 ?。?)有太多的從業(yè)者,在沒(méi)有搞清楚項目目標之前就已經(jīng)開(kāi)始編碼了。在很多時(shí)候,項目目標都是通過(guò)并不準確的口頭溝通來(lái)確定的。例如:
  “需要做什么?”
  “就按照×××網(wǎng)站的做一個(gè)吧?!?
 ?。?)有太多的從業(yè)者,在代碼編寫(xiě)基本完成后,才發(fā)現設計思路是有問(wèn)題的。他們在很多項目上花費很少(甚至沒(méi)有花費)時(shí)間進(jìn)行系統設計,對于在設計中所隱藏的問(wèn)題并沒(méi)有仔細思考和求證?;谶@樣的設計投入和設計質(zhì)量,項目出現設計失誤也是很難避免的。而面對一個(gè)已經(jīng)完成了基本編碼的項目,如果要“動(dòng)大手術(shù)”來(lái)修改它,相信每個(gè)有過(guò)類(lèi)似經(jīng)歷的人都一定深知那種感受——越改越亂,越改越著(zhù)急。
  以上這幾種情況,很多讀者是不是都有過(guò)類(lèi)似經(jīng)歷?
  2.研發(fā)前期多投入,收益更大
  關(guān)于軟件研發(fā),首先我們需要建立一個(gè)非常重要的觀(guān)念。
  在研發(fā)前期(需求分析和系統設計)多投入資源,相對于把資源都投入在研發(fā)后期(編碼、測試等),其收益更大。
  這是為什么呢?
  要回答這個(gè)問(wèn)題,需要從軟件研發(fā)全生命周期的角度來(lái)考量軟件研發(fā)的成本。除編碼外,軟件測試、上線(xiàn)、調試等都需要很高成本。如果我們把需求搞錯了,那么與錯誤需求有關(guān)的設計、編碼、測試、上線(xiàn)等成本就都浪費了;如果我們把設計搞錯了,那么與錯誤設計相關(guān)的編碼、測試、上線(xiàn)的成本也就浪費了。
  如果仔細考量那些低效的項目,會(huì )發(fā)現有非常多的類(lèi)似于上面提到的“浪費”的地方。軟件工程師似乎都很忙,但是在錯誤方向上所做的所有努力并不會(huì )產(chǎn)生任何價(jià)值,而大部分的加班實(shí)際上是在做錯誤的事情,或者是為了補救錯誤而努力。在這種情況下,將更多的資源和注意力向研發(fā)前期傾斜會(huì )立刻收到良好的效果。
  3.修改代碼和修改文檔,哪個(gè)成本更高
  很多軟件工程師不愿意做需求分析和系統設計,是因為對“寫(xiě)文檔”有著(zhù)根深蒂固的偏見(jiàn)。這里問(wèn)大家一個(gè)問(wèn)題,如果大家對這個(gè)問(wèn)題能給出正確的回答,那么在“寫(xiě)文檔”的意識方面,一定會(huì )有很大的轉變。
  任何人都不是神仙,無(wú)法一次就把所有事情做對。對于一段程序來(lái)說(shuō),它一定要經(jīng)過(guò)一定周期的修改和迭代。這時(shí)有兩種選擇:
  選擇一:修改文檔。在設計文檔時(shí)完成迭代調整,待沒(méi)有大問(wèn)題后再開(kāi)始編碼。
  選擇二:修改代碼。只有粗略的設計文檔,或者沒(méi)有設計文檔,直接開(kāi)始編碼,所有的迭代調整都在代碼上完成。
  請大家判斷,修改代碼和修改文檔,哪個(gè)成本更高?
  在之前的一些分享交流會(huì )上,對于這個(gè)問(wèn)題,有人會(huì )說(shuō),修改文檔的成本更高。因為在修改文檔后還要修改代碼,多了一道手續。而直接修改代碼,只需要做一次,這樣更直接。
  這個(gè)回答說(shuō)明了回答者沒(méi)有充分理解“先寫(xiě)文檔,后寫(xiě)代碼”的設計方法。如果沒(méi)有充分重視設計文檔的工作,在輸出的設計文檔質(zhì)量不高的情況下就開(kāi)始編碼,確實(shí)會(huì )出現以上提到的問(wèn)題。但是,如果在設計文檔階段就已經(jīng)做了充分考慮,會(huì )減少對代碼的迭代和反復。
  對于同樣的設計修改,“修改代碼”的成本遠高于“修改文檔”。這是因為,在設計文檔中只會(huì )涉及主要的邏輯,那些細小的、顯而易見(jiàn)的邏輯不會(huì )在設計文檔中出現。在修改設計文檔時(shí),也只會(huì )影響到這些主要邏輯。而如果在代碼中做修改,不僅會(huì )涉及這些主要邏輯,而且會(huì )涉及那些在文檔中不會(huì )出現的細小邏輯。對于一段程序來(lái)說(shuō),任何一個(gè)邏輯出現問(wèn)題,程序都是無(wú)法正常運行的。
  4.需求分析和系統設計之間的差別
  很多讀者無(wú)法清楚地區分“需求分析”和“系統設計”之間的差別,于是會(huì )發(fā)現,在寫(xiě)出的文檔中,有些需求分析文檔里出現了系統設計的內容,而有些系統設計文檔里又混雜了需求分析的內容。
  我們用幾句話(huà)可以非常明確地給出二者的差異。
 ?。?)需求分析:定義系統/軟件的黑盒的行為,它是從外部(External)看到的,在說(shuō)明“是什么”(What)。
 ?。?)系統設計:設計系統/軟件的白盒的機制,它是從內部(Internal)看到的,要說(shuō)明“怎么做”(How)和“為什么”(Why)。
  比如,對一輛汽車(chē)來(lái)說(shuō),首先使用者從外部可以看到車(chē)廂、車(chē)輪,坐在車(chē)里可以看到方向盤(pán)、剎車(chē)踏板、油門(mén)踏板等;操作方向盤(pán)可以改變汽車(chē)的行駛方向,腳踩剎車(chē)踏板、油門(mén)踏板可用于減速和加速。以上這些是對汽車(chē)的“需求分析”。
  然后,我們想象汽車(chē)外殼和內部變成了透明的,可以看到汽車(chē)內部的發(fā)動(dòng)機、變速箱、傳動(dòng)桿、與剎車(chē)相關(guān)的內部裝置等。而這些對駕駛者來(lái)說(shuō)是不可見(jiàn)的,它們是對汽車(chē)的“系統設計”。
  -End-
  最近有一些小伙伴,讓我幫忙找一些面試題資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!
  
  點(diǎn)擊卡片,關(guān)注后回復【面試題】即可獲取
  在看點(diǎn)這里
   查看全部

  文章采集調用 好代碼和壞代碼
  關(guān)注后回復“進(jìn)群”,拉你進(jìn)程序員交流群
  要寫(xiě)出好代碼,首先需要提升品位。
  很多軟件工程師寫(xiě)不好代碼,在評審他人的代碼時(shí)也看不出問(wèn)題,就是因為缺乏對好代碼標準的認識。
  現在還有太多的軟件工程師認為,代碼只要可以正確執行就可以了。這是一種非常低的評價(jià)標準,很多重要的方面都被忽視了。
  好代碼的特性
  好代碼具有以下特性。
  1. 魯棒(Solid and Robust)
  代碼不僅要被正確執行,我們還要考慮對各種錯誤情況的處理,比如各種系統調用和函數調用的異常情況,系統相關(guān)組件的異常和錯誤。
  對很多產(chǎn)品級的程序來(lái)說(shuō),異常和錯誤處理的邏輯占了很大比例。
  2. 高效(Fast)
  程序的運行應使用盡量少的資源。資源不僅僅包括CPU,還可能包括存儲、I/O等。
  設計高效的程序,會(huì )運用到數據結構和算法方面的知識,同時(shí)要考慮到程序運行時(shí)的各種約束條件。
  3. 簡(jiǎn)潔(Maintainable and Simple)
  代碼的邏輯要盡量簡(jiǎn)明易懂,代碼要具有很好的可維護性。對于同樣的目標,能夠使用簡(jiǎn)單清楚的方法達成,就不要使用復雜晦澀的方法。
  “大道至簡(jiǎn)”,能否把復雜的問(wèn)題用簡(jiǎn)單的方式實(shí)現出來(lái),這是一種編程水平的體現。
  4. 簡(jiǎn)短(Small)
  在某種意義上,代碼的復雜度和維護成本是和代碼的規模直接相關(guān)的。在實(shí)現同樣功能的時(shí)候,要盡量將代碼寫(xiě)得簡(jiǎn)短一些。
  簡(jiǎn)潔高于簡(jiǎn)短。這里要注意,某些人為了能把代碼寫(xiě)得簡(jiǎn)短,使用了一些晦澀難懂的描述方式,降低了代碼的可讀性。這種方式是不可取的。
  5. 可測試(Testable)
  代碼的正確性要通過(guò)測試來(lái)保證,尤其是在敏捷的場(chǎng)景下,更需要依賴(lài)可自動(dòng)回歸執行的測試用例。
  在代碼的設計中,要考慮如何使代碼可測、易測。一個(gè)比較好的實(shí)踐是使用TDD(Test-Driven Development,測試驅動(dòng)開(kāi)發(fā))的方法,這樣在編寫(xiě)測試用例的時(shí)候會(huì )很快發(fā)現代碼在可測試性方面的問(wèn)題。
  6. 共享(Re-Usable)
  大量的程序實(shí)際上都使用了類(lèi)似的框架或邏輯。由于目前開(kāi)源代碼的大量普及,很多功能并不需要重復開(kāi)發(fā),只進(jìn)行引用和使用即可。
  在一個(gè)組織內部,應鼓勵共享和重用代碼,這樣可以有效降低代碼研發(fā)的成本,并提升代碼的質(zhì)量。
  實(shí)現代碼的共享,不僅需要在意識方面提升,還需要具有相關(guān)的能力(如編寫(xiě)獨立、高質(zhì)量的代碼庫)及相關(guān)基礎設施的支持(如代碼搜索、代碼引用機制)。
  7. 可移植(Portable)
  某些程序需要在多種操作系統下運行,在這種情況下,代碼的可移植性成為一種必需的能力。
  要讓代碼具有可移植性,需要對所運行的各種操作系統底層有充分的理解和統一抽象。一般會(huì )使用一個(gè)適配層來(lái)屏蔽操作系統底層的差異。
  一些編程語(yǔ)言也提供了多操作系統的可移植性,如很多基于Python語(yǔ)言、Java語(yǔ)言、Go語(yǔ)言編寫(xiě)的程序,都可以跨平臺運行。
  8. 可觀(guān)測(Observable)/ 可監控(Monitorable)
  面對目前大量存在的在線(xiàn)服務(wù)(Online Service)程序,需要具備對程序的運行狀態(tài)進(jìn)行細致而持續監控的能力。
  這要求在程序設計時(shí)就提供相關(guān)的機制,包括程序狀態(tài)的收集、保存和對外輸出。
  9. 可運維(Operational)
  可運維已經(jīng)成為軟件研發(fā)活動(dòng)的重要組成部分,可運維重點(diǎn)關(guān)注成本、效率和穩定性三個(gè)方面。
  程序的可運維性和程序的設計、編寫(xiě)緊密相關(guān),如果在程序設計階段就沒(méi)有考慮可運維性,那么程序運行的運維目標則難以達成。
  10. 可擴展(Scalable andExtensible)
  可擴展包含“容量可擴展”(Scalable)和“功能可擴展”(Extensible)兩方面。
  在互聯(lián)網(wǎng)公司的系統設計中,“容量可擴展”是重要的設計目標之一。系統要盡量支持通過(guò)增加資源來(lái)實(shí)現容量的線(xiàn)性提高。
  快速響應需求的變化,是互聯(lián)網(wǎng)公司的另外一個(gè)重要挑戰??煽紤]使用插件式的程序設計方式,以容納未來(lái)可能新增的功能,也可考慮使用類(lèi)似Protocol Buffer 這樣的工具,支持對協(xié)議新增字段。
  以上十條標準,如果要記住,可能有些困難。我們可以把它們歸納為四個(gè)方面,見(jiàn)表1。
  表1對一流代碼特性的匯總分類(lèi)
  
  壞代碼的例子
  關(guān)于好代碼,上面介紹了一些特性,本節也給出壞代碼(Bad Code)的幾個(gè)例子。關(guān)于壞代碼,本書(shū)沒(méi)有做系統性總結,只是希望通過(guò)以下這些例子的展示讓讀者對壞代碼有直觀(guān)的感覺(jué)。
  1.不好的函數名稱(chēng)(Bad Function Name)
  如do(),這樣的函數名稱(chēng)沒(méi)有多少信息量;又如myFunc(),這樣的函數名稱(chēng),個(gè)人色彩過(guò)于強烈,也沒(méi)有足夠的信息量。
  2.不好的變量名稱(chēng)(Bad Variable Name)
  如a、b、c、i、j、k、temp,這樣的變量名稱(chēng)在很多教科書(shū)中經(jīng)常出現,很多人在上學(xué)期間寫(xiě)代碼時(shí)也會(huì )經(jīng)常這樣用。如果作為局部變量,這樣的名稱(chēng)有時(shí)是可以接受的;但如果作為作用域稍微大的變量,這樣的名稱(chēng)就非常不可取了。
  3.沒(méi)有注釋?zhuān)∟o Comments)
  有寫(xiě)注釋習慣的軟件工程師很少,很多軟件工程師認為寫(xiě)注釋是浪費時(shí)間,是“額外”的工作。但是沒(méi)有注釋的代碼,閱讀的成本會(huì )比較高。
  4.函數不是單一目的(The Function has No Single Purpose)
  如LoadFromFileAndCalculate()。這個(gè)例子是我編造的,但現實(shí)中這樣的函數其實(shí)不少。很多函數在首次寫(xiě)出來(lái)的時(shí)候,就很難表述清楚其用途;還有一些函數隨著(zhù)功能的擴展,變得越來(lái)越龐雜,也就慢慢地說(shuō)不清它的目的了。
  這方面的問(wèn)題可能很多人都沒(méi)有充分地認識到——非單一目的的函數難以維護,也難以復用。
  5.不好的排版(Bad Layout)
  不少人認為,程序可以正常執行就行了,所以一些軟件工程師不重視對代碼的排版,認為這僅僅是一種“形式”。
  沒(méi)有排好版的程序,在閱讀效率方面會(huì )帶來(lái)嚴重問(wèn)題。這里舉一個(gè)極端的例子:對于C語(yǔ)言來(lái)說(shuō),“;”可作為語(yǔ)句的分割符,而“縮進(jìn)”和“換行”對于編譯器來(lái)說(shuō)是無(wú)用的,所以完全可以把一段C語(yǔ)言程序都“壓縮”在一行內。這樣的程序是可以運行的,但是對人來(lái)說(shuō),可讀性非常差。這樣的程序肯定是我們非常不希望看到的。
  6.無(wú)法測試(None Testable)
  程序的正確性要依賴(lài)測試來(lái)保證(雖然測試并不能保證程序完全無(wú)錯)。無(wú)法或不好為之編寫(xiě)測試用例的程序,是很難有質(zhì)量保證的。
  好代碼從哪里來(lái)
  上一節說(shuō)明了好代碼的特性,本節來(lái)分析好代碼是如何產(chǎn)出的。
  ▊ 好代碼不止于編碼
  好代碼從哪里來(lái)?
  對于這個(gè)問(wèn)題,很多讀者肯定會(huì )說(shuō):“好代碼肯定是寫(xiě)出來(lái)的呀?!?
  我曾做過(guò)多次調研,發(fā)現很多軟件工程師日常所讀的書(shū)確實(shí)是和“寫(xiě)代碼”緊密相關(guān)的。
  但是,這里要告訴讀者的是,代碼不只是“寫(xiě)”出來(lái)的。在很多年前,我所讀的軟件工程方面的教科書(shū)就告訴我,編碼的時(shí)間一般只占一個(gè)項目所花時(shí)間的 10%。我曾說(shuō)過(guò)一句比較有趣的話(huà):
  “如果一個(gè)從業(yè)者告訴你,他的大部分時(shí)間都在寫(xiě)代碼,那么他大概率不是一個(gè)高級軟件工程師?!?
  那么,軟件工程師的時(shí)間都花到哪里去了呢?軟件工程師的時(shí)間應該花在哪里呢?
  好的代碼是多個(gè)工作環(huán)節的綜合結果。
 ?。?)在編碼前,需要做好需求分析和系統設計。而這兩項工作是經(jīng)常被大量軟件工程師忽略或輕視的環(huán)節。
 ?。?)在編碼時(shí),需要編寫(xiě)代碼和編寫(xiě)單元測試。對于“編寫(xiě)代碼”,讀者都了解;而對于“編寫(xiě)單元測試”,有些軟件工程師就不認同了,甚至還有人誤以為單元測試是由測試工程師來(lái)編寫(xiě)的。
 ?。?)在編碼后,要做集成測試、上線(xiàn),以及持續運營(yíng)/迭代改進(jìn)。這幾件事情都是要花費不少精力的,比如上線(xiàn),不僅僅要做程序部署,而且要考慮程序是如何被監控的。有時(shí),為了一段程序的上線(xiàn),設計和實(shí)施監控的方案要花費好幾天才能完成。
  因此,一個(gè)好的系統或產(chǎn)品是以上這些環(huán)節持續循環(huán)執行的結果。
  ▊ 需求分析和系統設計
  1.幾種常見(jiàn)的錯誤現象
  相對于編碼工作,需求分析和系統設計是兩個(gè)經(jīng)常被忽視的環(huán)節。在現實(shí)工作中,我們經(jīng)常會(huì )看到以下這些現象。
 ?。?)很多人錯誤地認為,寫(xiě)代碼才是最重要的事情。不少軟件工程師如果一天沒(méi)有寫(xiě)出幾行代碼,就會(huì )認為工作沒(méi)有進(jìn)展;很多管理者也會(huì )以代碼的產(chǎn)出量作為衡量工作結果的主要標準,催促軟件工程師盡早開(kāi)始寫(xiě)代碼。
 ?。?)有太多的從業(yè)者,在沒(méi)有搞清楚項目目標之前就已經(jīng)開(kāi)始編碼了。在很多時(shí)候,項目目標都是通過(guò)并不準確的口頭溝通來(lái)確定的。例如:
  “需要做什么?”
  “就按照×××網(wǎng)站的做一個(gè)吧?!?
 ?。?)有太多的從業(yè)者,在代碼編寫(xiě)基本完成后,才發(fā)現設計思路是有問(wèn)題的。他們在很多項目上花費很少(甚至沒(méi)有花費)時(shí)間進(jìn)行系統設計,對于在設計中所隱藏的問(wèn)題并沒(méi)有仔細思考和求證?;谶@樣的設計投入和設計質(zhì)量,項目出現設計失誤也是很難避免的。而面對一個(gè)已經(jīng)完成了基本編碼的項目,如果要“動(dòng)大手術(shù)”來(lái)修改它,相信每個(gè)有過(guò)類(lèi)似經(jīng)歷的人都一定深知那種感受——越改越亂,越改越著(zhù)急。
  以上這幾種情況,很多讀者是不是都有過(guò)類(lèi)似經(jīng)歷?
  2.研發(fā)前期多投入,收益更大
  關(guān)于軟件研發(fā),首先我們需要建立一個(gè)非常重要的觀(guān)念。
  在研發(fā)前期(需求分析和系統設計)多投入資源,相對于把資源都投入在研發(fā)后期(編碼、測試等),其收益更大。
  這是為什么呢?
  要回答這個(gè)問(wèn)題,需要從軟件研發(fā)全生命周期的角度來(lái)考量軟件研發(fā)的成本。除編碼外,軟件測試、上線(xiàn)、調試等都需要很高成本。如果我們把需求搞錯了,那么與錯誤需求有關(guān)的設計、編碼、測試、上線(xiàn)等成本就都浪費了;如果我們把設計搞錯了,那么與錯誤設計相關(guān)的編碼、測試、上線(xiàn)的成本也就浪費了。
  如果仔細考量那些低效的項目,會(huì )發(fā)現有非常多的類(lèi)似于上面提到的“浪費”的地方。軟件工程師似乎都很忙,但是在錯誤方向上所做的所有努力并不會(huì )產(chǎn)生任何價(jià)值,而大部分的加班實(shí)際上是在做錯誤的事情,或者是為了補救錯誤而努力。在這種情況下,將更多的資源和注意力向研發(fā)前期傾斜會(huì )立刻收到良好的效果。
  3.修改代碼和修改文檔,哪個(gè)成本更高
  很多軟件工程師不愿意做需求分析和系統設計,是因為對“寫(xiě)文檔”有著(zhù)根深蒂固的偏見(jiàn)。這里問(wèn)大家一個(gè)問(wèn)題,如果大家對這個(gè)問(wèn)題能給出正確的回答,那么在“寫(xiě)文檔”的意識方面,一定會(huì )有很大的轉變。
  任何人都不是神仙,無(wú)法一次就把所有事情做對。對于一段程序來(lái)說(shuō),它一定要經(jīng)過(guò)一定周期的修改和迭代。這時(shí)有兩種選擇:
  選擇一:修改文檔。在設計文檔時(shí)完成迭代調整,待沒(méi)有大問(wèn)題后再開(kāi)始編碼。
  選擇二:修改代碼。只有粗略的設計文檔,或者沒(méi)有設計文檔,直接開(kāi)始編碼,所有的迭代調整都在代碼上完成。
  請大家判斷,修改代碼和修改文檔,哪個(gè)成本更高?
  在之前的一些分享交流會(huì )上,對于這個(gè)問(wèn)題,有人會(huì )說(shuō),修改文檔的成本更高。因為在修改文檔后還要修改代碼,多了一道手續。而直接修改代碼,只需要做一次,這樣更直接。
  這個(gè)回答說(shuō)明了回答者沒(méi)有充分理解“先寫(xiě)文檔,后寫(xiě)代碼”的設計方法。如果沒(méi)有充分重視設計文檔的工作,在輸出的設計文檔質(zhì)量不高的情況下就開(kāi)始編碼,確實(shí)會(huì )出現以上提到的問(wèn)題。但是,如果在設計文檔階段就已經(jīng)做了充分考慮,會(huì )減少對代碼的迭代和反復。
  對于同樣的設計修改,“修改代碼”的成本遠高于“修改文檔”。這是因為,在設計文檔中只會(huì )涉及主要的邏輯,那些細小的、顯而易見(jiàn)的邏輯不會(huì )在設計文檔中出現。在修改設計文檔時(shí),也只會(huì )影響到這些主要邏輯。而如果在代碼中做修改,不僅會(huì )涉及這些主要邏輯,而且會(huì )涉及那些在文檔中不會(huì )出現的細小邏輯。對于一段程序來(lái)說(shuō),任何一個(gè)邏輯出現問(wèn)題,程序都是無(wú)法正常運行的。
  4.需求分析和系統設計之間的差別
  很多讀者無(wú)法清楚地區分“需求分析”和“系統設計”之間的差別,于是會(huì )發(fā)現,在寫(xiě)出的文檔中,有些需求分析文檔里出現了系統設計的內容,而有些系統設計文檔里又混雜了需求分析的內容。
  我們用幾句話(huà)可以非常明確地給出二者的差異。
 ?。?)需求分析:定義系統/軟件的黑盒的行為,它是從外部(External)看到的,在說(shuō)明“是什么”(What)。
 ?。?)系統設計:設計系統/軟件的白盒的機制,它是從內部(Internal)看到的,要說(shuō)明“怎么做”(How)和“為什么”(Why)。
  比如,對一輛汽車(chē)來(lái)說(shuō),首先使用者從外部可以看到車(chē)廂、車(chē)輪,坐在車(chē)里可以看到方向盤(pán)、剎車(chē)踏板、油門(mén)踏板等;操作方向盤(pán)可以改變汽車(chē)的行駛方向,腳踩剎車(chē)踏板、油門(mén)踏板可用于減速和加速。以上這些是對汽車(chē)的“需求分析”。
  然后,我們想象汽車(chē)外殼和內部變成了透明的,可以看到汽車(chē)內部的發(fā)動(dòng)機、變速箱、傳動(dòng)桿、與剎車(chē)相關(guān)的內部裝置等。而這些對駕駛者來(lái)說(shuō)是不可見(jiàn)的,它們是對汽車(chē)的“系統設計”。
  -End-
  最近有一些小伙伴,讓我幫忙找一些面試題資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!
  
  點(diǎn)擊卡片,關(guān)注后回復【面試題】即可獲取
  在看點(diǎn)這里
  

Spring Boot 如何監控 SQL 運行情況?

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 112 次瀏覽 ? 2022-05-09 12:17 ? 來(lái)自相關(guān)話(huà)題

  Spring Boot 如何監控 SQL 運行情況?
  關(guān)注后回復“進(jìn)群”,拉你進(jìn)程序員交流群
  今天想和大家聊一聊 Druid 中的監控功能。
  Druid 數據庫連接池相信很多小伙伴都用過(guò),個(gè)人感覺(jué) Druid 是阿里比較成功的開(kāi)源項目了,不像 Fastjson 那么多槽點(diǎn),Druid 各方面一直都比較出色,功能齊全,使用也方便,基本的用法就不說(shuō)了,今天我們來(lái)看看 Druid 中的監控功能。
  1. 準備工作
  首先我們來(lái)創(chuàng )建一個(gè) Spring Boot 工程,引入 MyBatis 等,如下:
  
  選一下 MyBatis 和 MySQL 驅動(dòng),做一個(gè)簡(jiǎn)單的測試案例。
  先來(lái)連接一下數據庫:
  spring.datasource.username=root<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.password=123<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.url=jdbc:mysql:///test05?serverTimezone=Asia/Shanghai<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  創(chuàng )建一個(gè) User 實(shí)體類(lèi),做一個(gè)簡(jiǎn)單的查詢(xún)案例,如下:
  public?class?User?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?Integer?id;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?username;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?address;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?password;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?email;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????//省略?getter/setter<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@Mapper<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?interface?UserMapper?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????List?getUserByUsername(String?username);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@Service<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?UserService?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@Autowired<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????UserMapper?userMapper;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?List?getUserByUsername(String?username){<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????return?userMapper.getUserByUsername(username);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@RestController<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?UserController?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@Autowired<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????UserService?userService;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@GetMapping("/user")<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?List?getUser(String?username)?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????return?userService.getUserByUsername(username);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  UserMapper.xml 如下:
  <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????select?*?from?user?where?username=#{username}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  一個(gè)很簡(jiǎn)單的測試,沒(méi)啥好說(shuō)的。
  這個(gè)環(huán)境搭建大家隨意,如果你已經(jīng)有持久化的案例了,那就直接看第二小節引入 Druid。
  現在這個(gè)工程默認的使用的數據庫連接池是 HikariDataSource,這是 Spring Boot 中默認的一個(gè)數據庫連接池,其實(shí)這個(gè)也還不錯。
  2. 引入 Druid
  接下來(lái)我們引入 Druid:
  <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????com.alibaba<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????druid-spring-boot-starter<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????1.2.8<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  注意,Spring Boot 引入的 Druid 是上面這個(gè),這個(gè)將來(lái)配置監控的時(shí)候方便一些。
  接下來(lái)我們在 application.properties 中配置 WebStatFilter,WebStatFilter 用于采集 web-jdbc 關(guān)聯(lián)監控的數據:
  # 啟用 WebStatFilter<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.enabled=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 配置攔截規則<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.url-pattern=/*<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 排除一些不必要的 url,這些 URL 不會(huì )涉及到 SQL 查詢(xún)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 開(kāi)啟 session 統計功能<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.session-stat-enable=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 缺省 sessionStatMaxCount 是 1000 個(gè),我們可以按需要進(jìn)行配置<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.session-stat-max-count=1000<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 配置 principalSessionName,使得 druid 能夠知道當前的 session 的用戶(hù)是誰(shuí)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 根據需要,這個(gè)參數的值是 user 信息保存在 session 中的 sessionName<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />#spring.datasource.druid.web-stat-filter.principal-session-name=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 下面這個(gè)配置的作用和上面配置的作用類(lèi)似,這個(gè)是通過(guò) Cookie 來(lái)識別用戶(hù)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />#spring.datasource.druid.web-stat-filter.principal-cookie-name=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 開(kāi)啟 profile 后就能夠監控單個(gè) URL 地址調用列表<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />#spring.datasource.druid.web-stat-filter.profile-enable=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  我們配置前面五個(gè)就可以了,后面三個(gè)可以不用配置,各項配置的含義松哥已經(jīng)在代碼中列出來(lái)了。
  接下來(lái)開(kāi)啟 StatViewServlet 的配置,如下:
  # 啟用內置的監控頁(yè)面<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.enabled=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 內置監控頁(yè)面的地址<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 開(kāi)啟 Reset All 功能<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.reset-enable=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 設置登錄用戶(hù)名<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.login-username=javaboy<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 設置登錄密碼<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.login-password=123<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 白名單(如果allow沒(méi)有配置或者為空,則允許所有訪(fǎng)問(wèn))<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.allow=127.0.0.1<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 黑名單(deny 優(yōu)先于 allow,如果在 deny 列表中,就算在 allow 列表中,也會(huì )被拒絕)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.deny=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  配置一下頁(yè)面地址,配置一下黑白名單即可。
  需要注意的是,reset-enable 屬性即使設置為 false,重置按鈕也會(huì )顯示,只是點(diǎn)擊該按鈕并不會(huì )重置而已。
  好啦,這就完事了。
  3. 測試
  好啦,接下來(lái)我們啟動(dòng) Spring Boot 項目進(jìn)行測試。
  Spring Boot 項目啟動(dòng)成功后,首先訪(fǎng)問(wèn)如下地址:
  此時(shí)我們會(huì )看到登錄認證頁(yè)面,如下:
  
  輸入我們前面配置的用戶(hù)名/密碼(javaboy/123)進(jìn)行登錄,登錄成功后,可以看到如下頁(yè)面:
  
  從標題欄就可以看到,數據源、SQL 監控、SQL 防火墻等功能都是一應俱全。
  接下來(lái)我們訪(fǎng)問(wèn):8080/user?username=aaa地址,執行一條 SQL,執行完成后,我們來(lái)查看 SQL 監控:
  
  可以看到,此時(shí)就有 SQL 執行的監控記錄了。
  其他的監控數據也都可以看到,我就不一一列舉了。如果小伙伴們覺(jué)得這里展示的數據不直觀(guān),想自己畫(huà) HTML 頁(yè)面,那也是可以的,點(diǎn)擊最后面的 JSON API,可以看到每一個(gè)監控項的 JSON 地址,拿著(zhù) JSON 自己想怎么顯示就怎么顯示。
  4. 去廣告
  如果想直接用這個(gè)監控頁(yè)面,這個(gè)上面有阿里的廣告,如下圖,公司用的話(huà)就特別別扭:
  
  我們可能想去掉這個(gè)廣告,這也很容易。
  首先,經(jīng)過(guò)分析,我們發(fā)現廣告是由一個(gè)叫做 common.js 的文件構建出來(lái)的,該文件位于druid-1.2.8.jar!/support/http/resources/js/common.js這里,common.js 文件中有如下幾行:
  init?:?function()?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?this.buildFooter();<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?druid.lang.init();<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />},<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />buildFooter?:?function()?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?var?html?='';<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?$(document.body).append(html);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />},<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  大概邏輯就是上面這樣,buildFooter 方法負責構建頁(yè)面末尾的廣告,在 init 方法中完成對 buildFooter 方法的調用。
  那么想要去除廣告,就別調用 buildFooter 方法就行了。
  所以我們的去廣告思路也很簡(jiǎn)單,寫(xiě)一個(gè)過(guò)濾器,攔截下對 common.js 的請求,然后做一點(diǎn)點(diǎn)修改,如下:
  @WebFilter(urlPatterns?=?"/druid/js/common.js")<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?RemoveAdFilter?implements?Filter?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@Override<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?void?doFilter(ServletRequest?servletRequest,?ServletResponse?servletResponse,?FilterChain?filterChain)?throws?IOException,?ServletException?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????String?text?=?Utils.readFromResource("support/http/resources/js/common.js");<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????text?=?text.replace("this.buildFooter();",?"");<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????servletResponse.getWriter().write(text);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  可以看到,這個(gè)過(guò)濾器就是攔截/druid/js/common.js請求,攔截到之后,自己去文件中讀取 common.js 文件,然后手動(dòng)替換掉this.buildFooter();這一句就行了,最后再把文件寫(xiě)出去就行了。
  當然,記得在啟動(dòng)類(lèi)中掃描 Filter,如下:
  @SpringBootApplication<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@ServletComponentScan("org.javaboy.druid_monitor.filter")<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?DruidMonitorApplication?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?static?void?main(String[]?args)?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????SpringApplication.run(DruidMonitorApplication.class,?args);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  好啦,這就可以啦,有了這個(gè)過(guò)濾器,廣告就沒(méi)了。
  -End-
  最近有一些小伙伴,讓我幫忙找一些面試題資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!
  
  點(diǎn)擊卡片,關(guān)注后回復【面試題】即可獲取
  在看點(diǎn)這里
   查看全部

  Spring Boot 如何監控 SQL 運行情況?
  關(guān)注后回復“進(jìn)群”,拉你進(jìn)程序員交流群
  今天想和大家聊一聊 Druid 中的監控功能。
  Druid 數據庫連接池相信很多小伙伴都用過(guò),個(gè)人感覺(jué) Druid 是阿里比較成功的開(kāi)源項目了,不像 Fastjson 那么多槽點(diǎn),Druid 各方面一直都比較出色,功能齊全,使用也方便,基本的用法就不說(shuō)了,今天我們來(lái)看看 Druid 中的監控功能。
  1. 準備工作
  首先我們來(lái)創(chuàng )建一個(gè) Spring Boot 工程,引入 MyBatis 等,如下:
  
  選一下 MyBatis 和 MySQL 驅動(dòng),做一個(gè)簡(jiǎn)單的測試案例。
  先來(lái)連接一下數據庫:
  spring.datasource.username=root<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.password=123<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.url=jdbc:mysql:///test05?serverTimezone=Asia/Shanghai<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  創(chuàng )建一個(gè) User 實(shí)體類(lèi),做一個(gè)簡(jiǎn)單的查詢(xún)案例,如下:
  public?class?User?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?Integer?id;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?username;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?address;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?password;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?email;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????//省略?getter/setter<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@Mapper<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?interface?UserMapper?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????List?getUserByUsername(String?username);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@Service<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?UserService?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@Autowired<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????UserMapper?userMapper;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?List?getUserByUsername(String?username){<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????return?userMapper.getUserByUsername(username);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@RestController<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?UserController?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@Autowired<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????UserService?userService;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@GetMapping("/user")<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?List?getUser(String?username)?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????return?userService.getUserByUsername(username);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  UserMapper.xml 如下:
  <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????select?*?from?user?where?username=#{username}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  一個(gè)很簡(jiǎn)單的測試,沒(méi)啥好說(shuō)的。
  這個(gè)環(huán)境搭建大家隨意,如果你已經(jīng)有持久化的案例了,那就直接看第二小節引入 Druid。
  現在這個(gè)工程默認的使用的數據庫連接池是 HikariDataSource,這是 Spring Boot 中默認的一個(gè)數據庫連接池,其實(shí)這個(gè)也還不錯。
  2. 引入 Druid
  接下來(lái)我們引入 Druid:
  <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????com.alibaba<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????druid-spring-boot-starter<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????1.2.8<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  注意,Spring Boot 引入的 Druid 是上面這個(gè),這個(gè)將來(lái)配置監控的時(shí)候方便一些。
  接下來(lái)我們在 application.properties 中配置 WebStatFilter,WebStatFilter 用于采集 web-jdbc 關(guān)聯(lián)監控的數據:
  # 啟用 WebStatFilter<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.enabled=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 配置攔截規則<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.url-pattern=/*<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 排除一些不必要的 url,這些 URL 不會(huì )涉及到 SQL 查詢(xún)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 開(kāi)啟 session 統計功能<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.session-stat-enable=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 缺省 sessionStatMaxCount 是 1000 個(gè),我們可以按需要進(jìn)行配置<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.session-stat-max-count=1000<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 配置 principalSessionName,使得 druid 能夠知道當前的 session 的用戶(hù)是誰(shuí)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 根據需要,這個(gè)參數的值是 user 信息保存在 session 中的 sessionName<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />#spring.datasource.druid.web-stat-filter.principal-session-name=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 下面這個(gè)配置的作用和上面配置的作用類(lèi)似,這個(gè)是通過(guò) Cookie 來(lái)識別用戶(hù)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />#spring.datasource.druid.web-stat-filter.principal-cookie-name=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 開(kāi)啟 profile 后就能夠監控單個(gè) URL 地址調用列表<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />#spring.datasource.druid.web-stat-filter.profile-enable=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  我們配置前面五個(gè)就可以了,后面三個(gè)可以不用配置,各項配置的含義松哥已經(jīng)在代碼中列出來(lái)了。
  接下來(lái)開(kāi)啟 StatViewServlet 的配置,如下:
  # 啟用內置的監控頁(yè)面<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.enabled=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 內置監控頁(yè)面的地址<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 開(kāi)啟 Reset All 功能<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.reset-enable=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 設置登錄用戶(hù)名<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.login-username=javaboy<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 設置登錄密碼<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.login-password=123<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 白名單(如果allow沒(méi)有配置或者為空,則允許所有訪(fǎng)問(wèn))<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.allow=127.0.0.1<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 黑名單(deny 優(yōu)先于 allow,如果在 deny 列表中,就算在 allow 列表中,也會(huì )被拒絕)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.deny=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  配置一下頁(yè)面地址,配置一下黑白名單即可。
  需要注意的是,reset-enable 屬性即使設置為 false,重置按鈕也會(huì )顯示,只是點(diǎn)擊該按鈕并不會(huì )重置而已。
  好啦,這就完事了。
  3. 測試
  好啦,接下來(lái)我們啟動(dòng) Spring Boot 項目進(jìn)行測試。
  Spring Boot 項目啟動(dòng)成功后,首先訪(fǎng)問(wèn)如下地址:
  此時(shí)我們會(huì )看到登錄認證頁(yè)面,如下:
  
  輸入我們前面配置的用戶(hù)名/密碼(javaboy/123)進(jìn)行登錄,登錄成功后,可以看到如下頁(yè)面:
  
  從標題欄就可以看到,數據源、SQL 監控、SQL 防火墻等功能都是一應俱全。
  接下來(lái)我們訪(fǎng)問(wèn):8080/user?username=aaa地址,執行一條 SQL,執行完成后,我們來(lái)查看 SQL 監控:
  
  可以看到,此時(shí)就有 SQL 執行的監控記錄了。
  其他的監控數據也都可以看到,我就不一一列舉了。如果小伙伴們覺(jué)得這里展示的數據不直觀(guān),想自己畫(huà) HTML 頁(yè)面,那也是可以的,點(diǎn)擊最后面的 JSON API,可以看到每一個(gè)監控項的 JSON 地址,拿著(zhù) JSON 自己想怎么顯示就怎么顯示。
  4. 去廣告
  如果想直接用這個(gè)監控頁(yè)面,這個(gè)上面有阿里的廣告,如下圖,公司用的話(huà)就特別別扭:
  
  我們可能想去掉這個(gè)廣告,這也很容易。
  首先,經(jīng)過(guò)分析,我們發(fā)現廣告是由一個(gè)叫做 common.js 的文件構建出來(lái)的,該文件位于druid-1.2.8.jar!/support/http/resources/js/common.js這里,common.js 文件中有如下幾行:
  init?:?function()?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?this.buildFooter();<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?druid.lang.init();<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />},<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />buildFooter?:?function()?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?var?html?='';<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?$(document.body).append(html);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />},<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  大概邏輯就是上面這樣,buildFooter 方法負責構建頁(yè)面末尾的廣告,在 init 方法中完成對 buildFooter 方法的調用。
  那么想要去除廣告,就別調用 buildFooter 方法就行了。
  所以我們的去廣告思路也很簡(jiǎn)單,寫(xiě)一個(gè)過(guò)濾器,攔截下對 common.js 的請求,然后做一點(diǎn)點(diǎn)修改,如下:
  @WebFilter(urlPatterns?=?"/druid/js/common.js")<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?RemoveAdFilter?implements?Filter?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@Override<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?void?doFilter(ServletRequest?servletRequest,?ServletResponse?servletResponse,?FilterChain?filterChain)?throws?IOException,?ServletException?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????String?text?=?Utils.readFromResource("support/http/resources/js/common.js");<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????text?=?text.replace("this.buildFooter();",?"");<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????servletResponse.getWriter().write(text);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  可以看到,這個(gè)過(guò)濾器就是攔截/druid/js/common.js請求,攔截到之后,自己去文件中讀取 common.js 文件,然后手動(dòng)替換掉this.buildFooter();這一句就行了,最后再把文件寫(xiě)出去就行了。
  當然,記得在啟動(dòng)類(lèi)中掃描 Filter,如下:
  @SpringBootApplication<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@ServletComponentScan("org.javaboy.druid_monitor.filter")<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?DruidMonitorApplication?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?static?void?main(String[]?args)?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????SpringApplication.run(DruidMonitorApplication.class,?args);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  好啦,這就可以啦,有了這個(gè)過(guò)濾器,廣告就沒(méi)了。
  -End-
  最近有一些小伙伴,讓我幫忙找一些面試題資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!
  
  點(diǎn)擊卡片,關(guān)注后回復【面試題】即可獲取
  在看點(diǎn)這里
  

一文詳解JVM垃圾收集機制,動(dòng)圖幫你輕松理解大廠(chǎng)面試難點(diǎn)

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 120 次瀏覽 ? 2022-05-09 12:05 ? 來(lái)自相關(guān)話(huà)題

  一文詳解JVM垃圾收集機制,動(dòng)圖幫你輕松理解大廠(chǎng)面試難點(diǎn)
  前言
  上篇文章已經(jīng)給大家介紹了 JVM 的架構和運行時(shí)數據區 (內存區域),本篇文章將給大家介紹 JVM 的重點(diǎn)內容——垃圾收集。眾所周知,相比 C / C++ 等語(yǔ)言,Java 可以省去手動(dòng)管理內存的繁瑣操作,很大程度上解放了 Java 程序員的生產(chǎn)力,而這正是得益于 JVM 的垃圾收集機制和內存分配策略。我們平時(shí)寫(xiě)程序時(shí)并感知不到這一點(diǎn),但是如果是在生產(chǎn)環(huán)境中,JVM 的不同配置對于服務(wù)器性能的影響是非常大的,所以掌握 JVM 調優(yōu)是高級 Java 工程師的必備技能。正所謂“基礎不牢,地動(dòng)山搖”,在這之前我們先來(lái)了解一下底層的 JVM 垃圾收集機制。
  既然要介紹垃圾收集機制,就要搞清楚以下幾個(gè)問(wèn)題:
  哪些內存區域需要進(jìn)行垃圾收集?
  如何判斷對象是否可回收?
  新的對象是如何進(jìn)行內存分配的?
  如何進(jìn)行垃圾收集?
  本文將按以下行文結構展開(kāi),對上述問(wèn)題一一解答。
  需要進(jìn)行垃圾收集的內存區域;
  判斷對象是否可回收的方法;
  主流的垃圾收集算法介紹;
  JVM 的內存分配與垃圾收集機制。
  下面開(kāi)始正文,還是圖文并茂的老配方,走起。
  一、需要進(jìn)行垃圾收集的內存區域
  先來(lái)回顧一下 JVM 的運行時(shí)數據區:
  
  JVM 運行時(shí)數據區
  其中程序計數器、Java 虛擬機棧和本地方法棧都是線(xiàn)程私有的,與其對應的線(xiàn)程是共生關(guān)系,隨線(xiàn)程而生,隨線(xiàn)程而滅,棧中的棧幀也隨著(zhù)方法的進(jìn)入和退出井然有序地進(jìn)行入棧和出棧操作。所以這幾個(gè)區域的內存分配和回收都是有很大確定性的,在方法結束或線(xiàn)程結束時(shí),內存也會(huì )隨之釋放,因此也就不需要考慮這幾個(gè)區域的內存回收問(wèn)題了。
  而堆和方法區就不一樣了,Java 的對象幾乎都是在堆上創(chuàng )建出來(lái)的,方法區則存儲了被虛擬機加載的類(lèi)型信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼緩存等數據,方法區中的運行時(shí)常量池則存放了各種字面量與符號引用,上述的這些數據大部分都是在運行時(shí)才能確定的,所以需要進(jìn)行動(dòng)態(tài)的內存管理。
  還要說(shuō)明一點(diǎn),JVM 中的垃圾收集器的最主要的關(guān)注對象是 Java 堆,因為這里進(jìn)行垃圾收集的“性?xún)r(jià)比”是最高的,尤其是在新生代 (后文對分代算法進(jìn)行介紹) 中的垃圾收集,一次就可以回收 70% - 99% 的內存。而方法區由于垃圾收集判定條件,尤其是類(lèi)型卸載的判定條件相當苛刻,其回收性?xún)r(jià)比是非常低的,因此有些垃圾收集器就干脆不支持或不完全支持方法區的垃圾收集,比如 JDK 11 中的 ZGC 收集器就不支持類(lèi)型卸載。
  二、判斷對象是否可回收的方法2.1 引用計數法
  引用計數法的實(shí)現很簡(jiǎn)單,在對象中添加一個(gè)引用計數器,每當有一個(gè)地方引用它時(shí),計數器值就加一;當引用失效時(shí),計數器值就減一;任何時(shí)刻計數器為零的對象就是不可能再被使用的。大部分情況下這個(gè)方法是可以發(fā)揮作用的,但是在存在循環(huán)引用的情況下,引用計數法就無(wú)能為力了。比如下面這種情況:
  public class Student {<br /> // friend 字段<br /> public Student friend = null;<br /> <br /> public static void test() {<br /> Student a = new Student();<br /> Student b = new Student();<br /> a.friend = b;<br /> b.friend = a;<br /> a = null;<br /> b = null;<br /> System.gc();<br /> }<br />}
  上述代碼創(chuàng )建了 a 和 b 兩個(gè) Student 實(shí)例,并把它們各自的 friend 字段賦值為對方,除此之外,這兩個(gè)對象再無(wú)任何引用,然后將它們都賦值為 null,在這種情況下,這兩個(gè)對象已經(jīng)不可能再被訪(fǎng)問(wèn),但是它們因為互相引用著(zhù)對方,導致它們的引用計數都不為零,引用計數算法也就無(wú)法回收它們。如下圖所示:
  
  循環(huán)引用
  但是在 Java 程序中,a 和 b 是可以被回收的,因為 JVM 并沒(méi)有使用引用計數法判定對象是否可回收,而是采用了可達性分析法。
  2.2 可達性分析法
  這個(gè)算法的基本思路就是通過(guò)一系列稱(chēng)為“GC Roots”的根對象作為起始節點(diǎn)集 (GC Root Set),從這些節點(diǎn)開(kāi)始,根據引用關(guān)系向下搜索,搜索過(guò)程所走過(guò)的路徑稱(chēng)為“引用鏈” (Reference Chain),如果某個(gè)對象到GC Roots間沒(méi)有任何引用鏈相連,則說(shuō)明此對象不再被使用,也就可以被回收了。要進(jìn)行可達性分析就需要先枚舉根節點(diǎn) (GC Roots),在枚舉根節點(diǎn)過(guò)程中,為防止對象的引用關(guān)系發(fā)生變化,需要暫停所有用戶(hù)線(xiàn)程 (垃圾收集之外的線(xiàn)程),這種暫停全部用戶(hù)線(xiàn)程的行為被稱(chēng)為 (Stop The World)??蛇_性分析法如下圖所示:
  
  可達性分析法
  圖中綠色的都是位于 GC Root Set 中的 GC Roots,所有與其有關(guān)聯(lián)的對象都是可達的,被標記為藍色,而所有與其沒(méi)有任何關(guān)聯(lián)的對象都是不可達的,被標記為灰色。即使是不可達對象,也并非一定會(huì )被回收,如果該對象同時(shí)滿(mǎn)足以下幾個(gè)條件,那么它仍有“逃生”的可能:
  該對象有重寫(xiě)的finalize()方法 (Object 類(lèi)中的方法);
  finalize()方法中將其自身鏈接到了引用鏈上;
  JVM 此前沒(méi)有調用過(guò)該對象的finalize()方法 (因為 JVM 在收集可回收對象時(shí)會(huì )調用且僅調用一次該對象的finalize()方法)。
  不過(guò)由于finalize()方法的運行代價(jià)高昂,不確定性大,且無(wú)法保證各個(gè)對象的調用順序,所以并不推薦使用。那么 GC Roots 又是何方神圣呢?在 Java 語(yǔ)言中,固定可作為GC Roots的對象包括以下幾種:
  在虛擬機棧 (棧幀中的本地變量表) 中引用的對象,比如各個(gè)線(xiàn)程被調用的方法堆棧中使用到的參數、局部變量、臨時(shí)變量等。
  在方法區中類(lèi)靜態(tài)屬性引用的對象,比如Java類(lèi)的引用類(lèi)型靜態(tài)變量。
  在方法區中常量引用的對象,比如字符串常量池(String Table)里的引用。
  在本地方法棧中JNI (即通常所說(shuō)的Native方法) 引用的對象。
  Java虛擬機內部的引用,如基本數據類(lèi)型對應的Class對象,一些常見(jiàn)的異常對象 (比如
  NullPointExcepiton、OutOfMemoryError) 等,還有系統類(lèi)加載器。
  所有被同步鎖 (synchronized關(guān)鍵字) 持有的對象。
  反映Java虛擬機內部情況的 JM XBean、JVM TI 中注冊的回調、本地代碼緩存等。
  三、垃圾收集算法介紹3.1 標記-清除算法
  標記-清除算法的思想很簡(jiǎn)單,顧名思義,該算法的過(guò)程分為標記和清除兩個(gè)階段:首先標記出所有需要回收的對象,其中標記過(guò)程就是使用可達性分析法判斷對象是否屬于垃圾的過(guò)程。在標記完成后,統一回收掉所有被標記的對象,也可以反過(guò)來(lái),標記存活的對象,統一回收所有未被標記的對象。示意圖如下:
  
  標記清除算法
  這個(gè)算法雖然很簡(jiǎn)單,但是有兩個(gè)明顯的缺點(diǎn):
  執行效率不穩定。如果 Java 堆中包含大量對象,而且其中大部分是需要被回收的,這時(shí)必須進(jìn)行大量標記和清除的動(dòng)作,導致標記和清除兩個(gè)過(guò)程的執行效率都隨對象數量增長(cháng)而降低;
  導致內存空間碎片化。標記、清除之后會(huì )產(chǎn)生大量不連續的內存碎片,空間碎片太多可能會(huì )導致當以后在程序運行過(guò)程中需要分配較大對象時(shí)無(wú)法找到足夠的連續內存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作,非常影響程序運行效率。
  3.2 標記-復制算法
  標記-復制算法常簡(jiǎn)稱(chēng)復制算法,這一算法正好解決了標記-清除算法在面對大量可回收對象時(shí)執行效率低下的問(wèn)題。其實(shí)現方法也很易懂:在可用內存中劃分出兩塊大小相同的區域,每次只使用其中一塊,另一塊保持空閑狀態(tài),第一塊用完的時(shí)候,就把存活的對象全部復制到第二塊區域,然后把第一塊全部清空。如下圖所示:
  
  標記-復制算法
  這個(gè)算法很適合用于對象存活率低的情況,因為它只關(guān)注存活對象而無(wú)需理會(huì )可回收對象,所以 JVM 中新生代的垃圾收集正是采用的這一算法。但是其缺點(diǎn)也很明顯,每次都要浪費一半的內存,未免太過(guò)奢侈,不過(guò) JVM 中的新生代有更精細的內存劃分,比較好地解決了這個(gè)問(wèn)題,見(jiàn)下文。
  3.3 標記-整理算法
  這個(gè)算法完美解決了標記-清除算法的空間碎片化問(wèn)題,其標記過(guò)程與“標記-清除”算法一樣,但后續步驟不是直接對可回收對象進(jìn)行清理,而是讓所有存活的對象都向內存空間一端移動(dòng),然后直接清理掉邊界以外的內存。
  
  標記整理算法
  這個(gè)算法雖然可以很好地解決空間碎片化問(wèn)題,但是每次垃圾回收都要移動(dòng)存活的對象,還要對引用這些對象的地方進(jìn)行更新,對象移動(dòng)的操作也需要全程暫停用戶(hù)線(xiàn)程 (Stop The World)。
  3.4 分代收集算法
  與其說(shuō)是算法,不如說(shuō)是理論。如今大多數虛擬機的實(shí)現版本都遵循了“分代收集”的理論進(jìn)行設計,這個(gè)理論可以看作是經(jīng)驗之談,因為開(kāi)發(fā)人員在開(kāi)發(fā)過(guò)程中發(fā)現了 JVM 中存活對象的數量和它們的年齡之間有著(zhù)某種規律,如下圖:
  
  JVM 中存活對象數量與年齡之間的關(guān)系
  在此基礎上,人們提出了以下假說(shuō):
  絕大多數對象都是朝生夕滅的。
  熬過(guò)越多次垃圾收集過(guò)程的對象就越難以消亡。
  根據這兩個(gè)假說(shuō),可以把 JVM 的堆內存大致分為新生代和老年代,新生代對象大多存活時(shí)間短,每次回收時(shí)只關(guān)注如何保留少量存活而不是去標記那些大量將要被回收的對象,就能以較低代價(jià)回收到大量的空間,所以這一區域一般采用標記-復制算法進(jìn)行垃圾收集,頻率比較高。而老年代則是一些難以消亡的對象,可以采用標記-清除和標記整理算法進(jìn)行垃圾收集,頻率可以低一些。
  按照 Hotspot 虛擬機的實(shí)現,針對新生代和老年代的垃圾收集又分為不同的類(lèi)型,也有不同的名詞,如下:
  部分收集 (Partial GC):指目標不是完整收集整個(gè)Java堆的垃圾收集,其中又分為:新生代收集 (Minor GC / Young GC):指目標只是新生代的垃圾收集。老年代收集 (Major GC / Old GC):指目標只是老年代的垃圾收集,目前只有CMS收集器的并發(fā)收集階段是單獨收集老年代的行為?;旌鲜占?(Mixed GC):指目標是收集整個(gè)新生代以及部分老年代的垃圾收集,目前只有G1收集器會(huì )有這種行為。
  整堆收集 (Full GC):收集整個(gè)Java堆和方法區的垃圾收集。
  人們經(jīng)常會(huì )混淆 Major GC 和 Full GC,不過(guò)這也有情可原,因為這兩種 GC 行為都包含了老年代的垃圾收集,而單獨的老年代收集 (Major GC) 又比較少見(jiàn),大多數情況下只要包含老年代收集,就會(huì )是整堆收集 (Full GC),不過(guò)還是分得清楚一點(diǎn)比較好哈。
  四、JVM 的內存分配和垃圾收集機制
  經(jīng)過(guò)前面的鋪墊,現在終于可以一窺 JVM 的內存分配和垃圾收集機制的真面目了。
  4.1 JVM 堆內存的劃分
  
  JVM 堆內存劃分,從Java 8開(kāi)始不再有永久代
  Java 堆是 JVM 所管理的內存中最大的一塊,也是垃圾收集器的管理區域。大多數垃圾收集器都會(huì )將堆內存劃分為上圖所示的幾個(gè)區域,整體分為新生代和老年代,比例為 1 : 2,新生代又進(jìn)一步分為 Eden、From Survivor 和 To Survivor,默認比例為 8 : 1 : 1,請注意,可通過(guò) SurvivorRatio 參數進(jìn)行設置。請注意,從 JDK 8 開(kāi)始,JVM 中已經(jīng)不再有永久代的概念了。Java 堆上的無(wú)論哪個(gè)區域,存儲的都只能是對象的實(shí)例,將Java 堆細分的目的只是為了更好地回收內存,或者更快地分配內存。
  4.2 分代收集原理4.2.1 新生代中對象的分配與回收
  大多數情況下,對象優(yōu)先在新生代 Eden 區中分配,當 Eden 區沒(méi)有足夠空間進(jìn)行分配時(shí),虛擬機將發(fā)起一次 Minor GC。Eden、From Survivor 和 To Survivor 的比例為 8 : 1 : 1,之所以按這個(gè)比例是因為絕大多數對象都是朝生夕滅的,垃圾收集時(shí) Eden 存活的對象數量不會(huì )太多,Survivor 空間小一點(diǎn)也足以容納,每次新生代中可用內存空間為整個(gè)新生代容量的90% (Eden 的 80% 加上 To Survivor 的 10%),只有From Survivor 空間,即 10% 的新生代是會(huì )被“浪費”的。不會(huì )像原始的標記-復制算法那樣浪費一半的內存空間。From Survivor 和 To Survivor 的空間并不是固定的,而是在 S0 和 S1 之間動(dòng)態(tài)轉換的,第一次 Minor GC 時(shí)會(huì )選擇 S1 作為 To Survivor,并將 Eden 中存活的對象復制到其中,并將對象的年齡加1,注意新生代使用的垃圾收集算法是標記-復制算法的改良版。下面是示意圖,請注意其中第一步的變色是為了醒目,虛擬機只做了標記存活對象的操作。
  第一次 Minor GC 示意圖
  在后續的 Minor GC 中,S0 和 S1會(huì )交替轉化為 From Survivor 和 To Survivor,Eden 和 From Survivor 中的存活對象會(huì )復制到 To Survivor 中,并將年齡加大 1。如下圖所示:
  
  后續 Minor GC 示意圖
  4.2.2 對象晉升老年代
  在以下這些情況下,對象會(huì )晉升到老年代。
  長(cháng)期存活對象將進(jìn)入老年代
  對象在 Survivor 區中每熬過(guò)一次Minor GC,年齡就增加1歲,當它的年齡增加到一定程度 (默認為15),就會(huì )被晉升到老年代中。對象晉升老年代的年齡閾值,可以通過(guò)參數 -XX:MaxTenuringThreshold 設置,這個(gè)參數的最大值是15,因為對象年齡信息儲存在對象頭中,占4個(gè)比特 (bit)的內存,所能表示最大數字就是15。
  長(cháng)期存活對象晉升老年代示意圖
  2.大對象可以直接進(jìn)入老年代
  對于大對象,尤其是很長(cháng)的字符串,或者元素數量很多的數組,如果分配在 Eden 中,會(huì )很容易過(guò)早占滿(mǎn) Eden 空間導致 Minor GC,而且大對象在 Eden 和兩個(gè) Survivor 之間的來(lái)回復制也還會(huì )有很大的內存復制開(kāi)銷(xiāo)。所以我們可以通過(guò)設置 -XX:PretenureSizeThreshold 的虛擬機參數讓大對象直接進(jìn)入老年代。
  3.動(dòng)態(tài)對象年齡判斷
  為了能更好地適應不同程序的內存狀況,HotSpot 虛擬機并不是永遠要求對象的年齡必須達到 -XX:MaxTenuringThreshold 才能晉升老年代,如果在 Survivor 空間中相同年齡所有對象大小的總和大于 Survivor 空間的一半,年齡大于或等于該年齡的對象就可以直接進(jìn)入老年代,無(wú)須等到 -XX:MaxTenuringThreshold 中要求的年齡。
  4.空間分配擔保 (Handle Promotion)
  當 Survivor 空間不足以容納一次 Minor GC 之后存活的對象時(shí),就需要依賴(lài)其他內存區域 (實(shí)際上大多數情況下就是老年代) 進(jìn)行分配擔保。在發(fā)生 Minor GC 之前,虛擬機必須先檢查老年代最大可用的連續空間是否大于新生代所有對象總空間,如果這個(gè)條件成立,那這一次 Minor GC 可以確保是安全的。如果不成立,則虛擬機會(huì )先查看 - XX:HandlePromotionFailure 參數的設置值是否允許擔保失敗 (Handle Promotion Failure);如果允許,那會(huì )繼續檢查老年代最大可用的連續空間是否大于歷次晉升到老年代對象的平均大小,如果大于,將嘗試進(jìn)行一次 Minor GC,盡管這次 Minor GC 是有風(fēng)險的;如果小于,或者-XX: HandlePromotionFailure設置不允許冒險,那這時(shí)就要改為進(jìn)行一次 Full GC。 查看全部

  一文詳解JVM垃圾收集機制,動(dòng)圖幫你輕松理解大廠(chǎng)面試難點(diǎn)
  前言
  上篇文章已經(jīng)給大家介紹了 JVM 的架構和運行時(shí)數據區 (內存區域),本篇文章將給大家介紹 JVM 的重點(diǎn)內容——垃圾收集。眾所周知,相比 C / C++ 等語(yǔ)言,Java 可以省去手動(dòng)管理內存的繁瑣操作,很大程度上解放了 Java 程序員的生產(chǎn)力,而這正是得益于 JVM 的垃圾收集機制和內存分配策略。我們平時(shí)寫(xiě)程序時(shí)并感知不到這一點(diǎn),但是如果是在生產(chǎn)環(huán)境中,JVM 的不同配置對于服務(wù)器性能的影響是非常大的,所以掌握 JVM 調優(yōu)是高級 Java 工程師的必備技能。正所謂“基礎不牢,地動(dòng)山搖”,在這之前我們先來(lái)了解一下底層的 JVM 垃圾收集機制。
  既然要介紹垃圾收集機制,就要搞清楚以下幾個(gè)問(wèn)題:
  哪些內存區域需要進(jìn)行垃圾收集?
  如何判斷對象是否可回收?
  新的對象是如何進(jìn)行內存分配的?
  如何進(jìn)行垃圾收集?
  本文將按以下行文結構展開(kāi),對上述問(wèn)題一一解答。
  需要進(jìn)行垃圾收集的內存區域;
  判斷對象是否可回收的方法;
  主流的垃圾收集算法介紹;
  JVM 的內存分配與垃圾收集機制。
  下面開(kāi)始正文,還是圖文并茂的老配方,走起。
  一、需要進(jìn)行垃圾收集的內存區域
  先來(lái)回顧一下 JVM 的運行時(shí)數據區:
  
  JVM 運行時(shí)數據區
  其中程序計數器、Java 虛擬機棧和本地方法棧都是線(xiàn)程私有的,與其對應的線(xiàn)程是共生關(guān)系,隨線(xiàn)程而生,隨線(xiàn)程而滅,棧中的棧幀也隨著(zhù)方法的進(jìn)入和退出井然有序地進(jìn)行入棧和出棧操作。所以這幾個(gè)區域的內存分配和回收都是有很大確定性的,在方法結束或線(xiàn)程結束時(shí),內存也會(huì )隨之釋放,因此也就不需要考慮這幾個(gè)區域的內存回收問(wèn)題了。
  而堆和方法區就不一樣了,Java 的對象幾乎都是在堆上創(chuàng )建出來(lái)的,方法區則存儲了被虛擬機加載的類(lèi)型信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼緩存等數據,方法區中的運行時(shí)常量池則存放了各種字面量與符號引用,上述的這些數據大部分都是在運行時(shí)才能確定的,所以需要進(jìn)行動(dòng)態(tài)的內存管理。
  還要說(shuō)明一點(diǎn),JVM 中的垃圾收集器的最主要的關(guān)注對象是 Java 堆,因為這里進(jìn)行垃圾收集的“性?xún)r(jià)比”是最高的,尤其是在新生代 (后文對分代算法進(jìn)行介紹) 中的垃圾收集,一次就可以回收 70% - 99% 的內存。而方法區由于垃圾收集判定條件,尤其是類(lèi)型卸載的判定條件相當苛刻,其回收性?xún)r(jià)比是非常低的,因此有些垃圾收集器就干脆不支持或不完全支持方法區的垃圾收集,比如 JDK 11 中的 ZGC 收集器就不支持類(lèi)型卸載。
  二、判斷對象是否可回收的方法2.1 引用計數法
  引用計數法的實(shí)現很簡(jiǎn)單,在對象中添加一個(gè)引用計數器,每當有一個(gè)地方引用它時(shí),計數器值就加一;當引用失效時(shí),計數器值就減一;任何時(shí)刻計數器為零的對象就是不可能再被使用的。大部分情況下這個(gè)方法是可以發(fā)揮作用的,但是在存在循環(huán)引用的情況下,引用計數法就無(wú)能為力了。比如下面這種情況:
  public class Student {<br /> // friend 字段<br /> public Student friend = null;<br /> <br /> public static void test() {<br /> Student a = new Student();<br /> Student b = new Student();<br /> a.friend = b;<br /> b.friend = a;<br /> a = null;<br /> b = null;<br /> System.gc();<br /> }<br />}
  上述代碼創(chuàng )建了 a 和 b 兩個(gè) Student 實(shí)例,并把它們各自的 friend 字段賦值為對方,除此之外,這兩個(gè)對象再無(wú)任何引用,然后將它們都賦值為 null,在這種情況下,這兩個(gè)對象已經(jīng)不可能再被訪(fǎng)問(wèn),但是它們因為互相引用著(zhù)對方,導致它們的引用計數都不為零,引用計數算法也就無(wú)法回收它們。如下圖所示:
  
  循環(huán)引用
  但是在 Java 程序中,a 和 b 是可以被回收的,因為 JVM 并沒(méi)有使用引用計數法判定對象是否可回收,而是采用了可達性分析法。
  2.2 可達性分析法
  這個(gè)算法的基本思路就是通過(guò)一系列稱(chēng)為“GC Roots”的根對象作為起始節點(diǎn)集 (GC Root Set),從這些節點(diǎn)開(kāi)始,根據引用關(guān)系向下搜索,搜索過(guò)程所走過(guò)的路徑稱(chēng)為“引用鏈” (Reference Chain),如果某個(gè)對象到GC Roots間沒(méi)有任何引用鏈相連,則說(shuō)明此對象不再被使用,也就可以被回收了。要進(jìn)行可達性分析就需要先枚舉根節點(diǎn) (GC Roots),在枚舉根節點(diǎn)過(guò)程中,為防止對象的引用關(guān)系發(fā)生變化,需要暫停所有用戶(hù)線(xiàn)程 (垃圾收集之外的線(xiàn)程),這種暫停全部用戶(hù)線(xiàn)程的行為被稱(chēng)為 (Stop The World)??蛇_性分析法如下圖所示:
  
  可達性分析法
  圖中綠色的都是位于 GC Root Set 中的 GC Roots,所有與其有關(guān)聯(lián)的對象都是可達的,被標記為藍色,而所有與其沒(méi)有任何關(guān)聯(lián)的對象都是不可達的,被標記為灰色。即使是不可達對象,也并非一定會(huì )被回收,如果該對象同時(shí)滿(mǎn)足以下幾個(gè)條件,那么它仍有“逃生”的可能:
  該對象有重寫(xiě)的finalize()方法 (Object 類(lèi)中的方法);
  finalize()方法中將其自身鏈接到了引用鏈上;
  JVM 此前沒(méi)有調用過(guò)該對象的finalize()方法 (因為 JVM 在收集可回收對象時(shí)會(huì )調用且僅調用一次該對象的finalize()方法)。
  不過(guò)由于finalize()方法的運行代價(jià)高昂,不確定性大,且無(wú)法保證各個(gè)對象的調用順序,所以并不推薦使用。那么 GC Roots 又是何方神圣呢?在 Java 語(yǔ)言中,固定可作為GC Roots的對象包括以下幾種:
  在虛擬機棧 (棧幀中的本地變量表) 中引用的對象,比如各個(gè)線(xiàn)程被調用的方法堆棧中使用到的參數、局部變量、臨時(shí)變量等。
  在方法區中類(lèi)靜態(tài)屬性引用的對象,比如Java類(lèi)的引用類(lèi)型靜態(tài)變量。
  在方法區中常量引用的對象,比如字符串常量池(String Table)里的引用。
  在本地方法棧中JNI (即通常所說(shuō)的Native方法) 引用的對象。
  Java虛擬機內部的引用,如基本數據類(lèi)型對應的Class對象,一些常見(jiàn)的異常對象 (比如
  NullPointExcepiton、OutOfMemoryError) 等,還有系統類(lèi)加載器。
  所有被同步鎖 (synchronized關(guān)鍵字) 持有的對象。
  反映Java虛擬機內部情況的 JM XBean、JVM TI 中注冊的回調、本地代碼緩存等。
  三、垃圾收集算法介紹3.1 標記-清除算法
  標記-清除算法的思想很簡(jiǎn)單,顧名思義,該算法的過(guò)程分為標記和清除兩個(gè)階段:首先標記出所有需要回收的對象,其中標記過(guò)程就是使用可達性分析法判斷對象是否屬于垃圾的過(guò)程。在標記完成后,統一回收掉所有被標記的對象,也可以反過(guò)來(lái),標記存活的對象,統一回收所有未被標記的對象。示意圖如下:
  
  標記清除算法
  這個(gè)算法雖然很簡(jiǎn)單,但是有兩個(gè)明顯的缺點(diǎn):
  執行效率不穩定。如果 Java 堆中包含大量對象,而且其中大部分是需要被回收的,這時(shí)必須進(jìn)行大量標記和清除的動(dòng)作,導致標記和清除兩個(gè)過(guò)程的執行效率都隨對象數量增長(cháng)而降低;
  導致內存空間碎片化。標記、清除之后會(huì )產(chǎn)生大量不連續的內存碎片,空間碎片太多可能會(huì )導致當以后在程序運行過(guò)程中需要分配較大對象時(shí)無(wú)法找到足夠的連續內存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作,非常影響程序運行效率。
  3.2 標記-復制算法
  標記-復制算法常簡(jiǎn)稱(chēng)復制算法,這一算法正好解決了標記-清除算法在面對大量可回收對象時(shí)執行效率低下的問(wèn)題。其實(shí)現方法也很易懂:在可用內存中劃分出兩塊大小相同的區域,每次只使用其中一塊,另一塊保持空閑狀態(tài),第一塊用完的時(shí)候,就把存活的對象全部復制到第二塊區域,然后把第一塊全部清空。如下圖所示:
  
  標記-復制算法
  這個(gè)算法很適合用于對象存活率低的情況,因為它只關(guān)注存活對象而無(wú)需理會(huì )可回收對象,所以 JVM 中新生代的垃圾收集正是采用的這一算法。但是其缺點(diǎn)也很明顯,每次都要浪費一半的內存,未免太過(guò)奢侈,不過(guò) JVM 中的新生代有更精細的內存劃分,比較好地解決了這個(gè)問(wèn)題,見(jiàn)下文。
  3.3 標記-整理算法
  這個(gè)算法完美解決了標記-清除算法的空間碎片化問(wèn)題,其標記過(guò)程與“標記-清除”算法一樣,但后續步驟不是直接對可回收對象進(jìn)行清理,而是讓所有存活的對象都向內存空間一端移動(dòng),然后直接清理掉邊界以外的內存。
  
  標記整理算法
  這個(gè)算法雖然可以很好地解決空間碎片化問(wèn)題,但是每次垃圾回收都要移動(dòng)存活的對象,還要對引用這些對象的地方進(jìn)行更新,對象移動(dòng)的操作也需要全程暫停用戶(hù)線(xiàn)程 (Stop The World)。
  3.4 分代收集算法
  與其說(shuō)是算法,不如說(shuō)是理論。如今大多數虛擬機的實(shí)現版本都遵循了“分代收集”的理論進(jìn)行設計,這個(gè)理論可以看作是經(jīng)驗之談,因為開(kāi)發(fā)人員在開(kāi)發(fā)過(guò)程中發(fā)現了 JVM 中存活對象的數量和它們的年齡之間有著(zhù)某種規律,如下圖:
  
  JVM 中存活對象數量與年齡之間的關(guān)系
  在此基礎上,人們提出了以下假說(shuō):
  絕大多數對象都是朝生夕滅的。
  熬過(guò)越多次垃圾收集過(guò)程的對象就越難以消亡。
  根據這兩個(gè)假說(shuō),可以把 JVM 的堆內存大致分為新生代和老年代,新生代對象大多存活時(shí)間短,每次回收時(shí)只關(guān)注如何保留少量存活而不是去標記那些大量將要被回收的對象,就能以較低代價(jià)回收到大量的空間,所以這一區域一般采用標記-復制算法進(jìn)行垃圾收集,頻率比較高。而老年代則是一些難以消亡的對象,可以采用標記-清除和標記整理算法進(jìn)行垃圾收集,頻率可以低一些。
  按照 Hotspot 虛擬機的實(shí)現,針對新生代和老年代的垃圾收集又分為不同的類(lèi)型,也有不同的名詞,如下:
  部分收集 (Partial GC):指目標不是完整收集整個(gè)Java堆的垃圾收集,其中又分為:新生代收集 (Minor GC / Young GC):指目標只是新生代的垃圾收集。老年代收集 (Major GC / Old GC):指目標只是老年代的垃圾收集,目前只有CMS收集器的并發(fā)收集階段是單獨收集老年代的行為?;旌鲜占?(Mixed GC):指目標是收集整個(gè)新生代以及部分老年代的垃圾收集,目前只有G1收集器會(huì )有這種行為。
  整堆收集 (Full GC):收集整個(gè)Java堆和方法區的垃圾收集。
  人們經(jīng)常會(huì )混淆 Major GC 和 Full GC,不過(guò)這也有情可原,因為這兩種 GC 行為都包含了老年代的垃圾收集,而單獨的老年代收集 (Major GC) 又比較少見(jiàn),大多數情況下只要包含老年代收集,就會(huì )是整堆收集 (Full GC),不過(guò)還是分得清楚一點(diǎn)比較好哈。
  四、JVM 的內存分配和垃圾收集機制
  經(jīng)過(guò)前面的鋪墊,現在終于可以一窺 JVM 的內存分配和垃圾收集機制的真面目了。
  4.1 JVM 堆內存的劃分
  
  JVM 堆內存劃分,從Java 8開(kāi)始不再有永久代
  Java 堆是 JVM 所管理的內存中最大的一塊,也是垃圾收集器的管理區域。大多數垃圾收集器都會(huì )將堆內存劃分為上圖所示的幾個(gè)區域,整體分為新生代和老年代,比例為 1 : 2,新生代又進(jìn)一步分為 Eden、From Survivor 和 To Survivor,默認比例為 8 : 1 : 1,請注意,可通過(guò) SurvivorRatio 參數進(jìn)行設置。請注意,從 JDK 8 開(kāi)始,JVM 中已經(jīng)不再有永久代的概念了。Java 堆上的無(wú)論哪個(gè)區域,存儲的都只能是對象的實(shí)例,將Java 堆細分的目的只是為了更好地回收內存,或者更快地分配內存。
  4.2 分代收集原理4.2.1 新生代中對象的分配與回收
  大多數情況下,對象優(yōu)先在新生代 Eden 區中分配,當 Eden 區沒(méi)有足夠空間進(jìn)行分配時(shí),虛擬機將發(fā)起一次 Minor GC。Eden、From Survivor 和 To Survivor 的比例為 8 : 1 : 1,之所以按這個(gè)比例是因為絕大多數對象都是朝生夕滅的,垃圾收集時(shí) Eden 存活的對象數量不會(huì )太多,Survivor 空間小一點(diǎn)也足以容納,每次新生代中可用內存空間為整個(gè)新生代容量的90% (Eden 的 80% 加上 To Survivor 的 10%),只有From Survivor 空間,即 10% 的新生代是會(huì )被“浪費”的。不會(huì )像原始的標記-復制算法那樣浪費一半的內存空間。From Survivor 和 To Survivor 的空間并不是固定的,而是在 S0 和 S1 之間動(dòng)態(tài)轉換的,第一次 Minor GC 時(shí)會(huì )選擇 S1 作為 To Survivor,并將 Eden 中存活的對象復制到其中,并將對象的年齡加1,注意新生代使用的垃圾收集算法是標記-復制算法的改良版。下面是示意圖,請注意其中第一步的變色是為了醒目,虛擬機只做了標記存活對象的操作。
  第一次 Minor GC 示意圖
  在后續的 Minor GC 中,S0 和 S1會(huì )交替轉化為 From Survivor 和 To Survivor,Eden 和 From Survivor 中的存活對象會(huì )復制到 To Survivor 中,并將年齡加大 1。如下圖所示:
  
  后續 Minor GC 示意圖
  4.2.2 對象晉升老年代
  在以下這些情況下,對象會(huì )晉升到老年代。
  長(cháng)期存活對象將進(jìn)入老年代
  對象在 Survivor 區中每熬過(guò)一次Minor GC,年齡就增加1歲,當它的年齡增加到一定程度 (默認為15),就會(huì )被晉升到老年代中。對象晉升老年代的年齡閾值,可以通過(guò)參數 -XX:MaxTenuringThreshold 設置,這個(gè)參數的最大值是15,因為對象年齡信息儲存在對象頭中,占4個(gè)比特 (bit)的內存,所能表示最大數字就是15。
  長(cháng)期存活對象晉升老年代示意圖
  2.大對象可以直接進(jìn)入老年代
  對于大對象,尤其是很長(cháng)的字符串,或者元素數量很多的數組,如果分配在 Eden 中,會(huì )很容易過(guò)早占滿(mǎn) Eden 空間導致 Minor GC,而且大對象在 Eden 和兩個(gè) Survivor 之間的來(lái)回復制也還會(huì )有很大的內存復制開(kāi)銷(xiāo)。所以我們可以通過(guò)設置 -XX:PretenureSizeThreshold 的虛擬機參數讓大對象直接進(jìn)入老年代。
  3.動(dòng)態(tài)對象年齡判斷
  為了能更好地適應不同程序的內存狀況,HotSpot 虛擬機并不是永遠要求對象的年齡必須達到 -XX:MaxTenuringThreshold 才能晉升老年代,如果在 Survivor 空間中相同年齡所有對象大小的總和大于 Survivor 空間的一半,年齡大于或等于該年齡的對象就可以直接進(jìn)入老年代,無(wú)須等到 -XX:MaxTenuringThreshold 中要求的年齡。
  4.空間分配擔保 (Handle Promotion)
  當 Survivor 空間不足以容納一次 Minor GC 之后存活的對象時(shí),就需要依賴(lài)其他內存區域 (實(shí)際上大多數情況下就是老年代) 進(jìn)行分配擔保。在發(fā)生 Minor GC 之前,虛擬機必須先檢查老年代最大可用的連續空間是否大于新生代所有對象總空間,如果這個(gè)條件成立,那這一次 Minor GC 可以確保是安全的。如果不成立,則虛擬機會(huì )先查看 - XX:HandlePromotionFailure 參數的設置值是否允許擔保失敗 (Handle Promotion Failure);如果允許,那會(huì )繼續檢查老年代最大可用的連續空間是否大于歷次晉升到老年代對象的平均大小,如果大于,將嘗試進(jìn)行一次 Minor GC,盡管這次 Minor GC 是有風(fēng)險的;如果小于,或者-XX: HandlePromotionFailure設置不允許冒險,那這時(shí)就要改為進(jìn)行一次 Full GC。

如何有效的采集微信公眾號文章

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 157 次瀏覽 ? 2022-05-05 05:10 ? 來(lái)自相關(guān)話(huà)題

  如何有效的采集微信公眾號文章
  有個(gè)人公眾號的用戶(hù)一般也有其他自媒體賬號,在花費了很長(cháng)時(shí)間寫(xiě)好了一篇文章以后終于可以放松一下,但是想著(zhù)還有好幾個(gè)其他平臺需要復制粘貼著(zhù)實(shí)頭大,這個(gè)工作既沒(méi)有技術(shù)含量也提現不出文筆。我們下面分享幾種獲取公眾號的方式。
  第一種:人工-手動(dòng)復制粘貼
  這也是最笨的一種方法,手動(dòng)切換平臺登錄,然后打開(kāi)編輯器復制粘貼,然后手動(dòng)發(fā)布。
  優(yōu)勢:如果有錯誤一目了然,不同的平臺有不同的限制,比如標題字數,封面圖片等。
  劣勢:手動(dòng)操作浪費人力,效率低下。
  第二種:手動(dòng)-用數據采集工具
  下載數據采集工具,手動(dòng)輸入鏈接可以下載文章內容,然后導出成word或者其他格式。
  優(yōu)勢:無(wú)需技術(shù)配合
  劣勢:需要手動(dòng)操作,先獲取內容,然后導出,然后登錄自己平臺手動(dòng)導入。
  第三種:技術(shù)-抓?。ㄗ咚压罚?br />   可以在程序中做定時(shí)獲取固定公眾號同步文章操作,通過(guò)搜狗瀏覽器搜索微信號,得到文章列表,然后文章列表中有文章的url,得到url后通過(guò)程序下載文章內容然后可以繼續后續操作。
  優(yōu)勢:減少人工操作,可實(shí)現半自動(dòng)化自動(dòng)發(fā)布
  劣勢:如果公眾號很多,請求太頻繁會(huì )有驗證碼出現,程序很難自動(dòng)識別,一旦驗證碼還需要手動(dòng)驗證。而且無(wú)法實(shí)時(shí)同步,即公眾號發(fā)完后不會(huì )立馬同步。具體延時(shí)多少根據自己的請求頻率決定。還有一點(diǎn)就是獲取的鏈接是臨時(shí)鏈接,必須在有效時(shí)間范圍內把文章內容請求下來(lái)。
  第四種:技術(shù)-抓?。ㄗ呶⑿藕笈_)
  在微信后臺文章編輯中有一個(gè)插入超鏈接的功能。通過(guò)這個(gè)接口可以搜索公眾號,然后得到歷史文章,得到文章鏈接后可以通過(guò)后臺程序下載下來(lái)。
  優(yōu)勢:減少人工操作,可實(shí)現半自動(dòng)化自動(dòng)發(fā)布。此鏈接是永久鏈接,任何時(shí)候都可用。
  劣勢:同第二種如果請求太頻繁也會(huì )有攔截導致接口無(wú)法調用。
  第五種:技術(shù)-通過(guò)一鍵建站推送
  你只需要在一鍵建站平臺中開(kāi)通數據采集功能,配置好你接收數據的接口,一旦公眾號中有新的文章發(fā)布將給你推送最新的內容
  優(yōu)勢:延時(shí)時(shí)間短。
  操作簡(jiǎn)單代碼量少再也不用擔心技術(shù)無(wú)法實(shí)現。
  真正實(shí)現完全托管,全自動(dòng)化。
  劣勢:付費版,免費額度很少。但是價(jià)格也好像不貴幾分錢(qián)一條吧。
  以上幾種方式都已經(jīng)親測過(guò)了,如果有更多更好的方式記得聯(lián)系我,我也好去試試,如果有需要幫助或者技術(shù)不明白的可以加我溝通交流。 查看全部

  如何有效的采集微信公眾號文章
  有個(gè)人公眾號的用戶(hù)一般也有其他自媒體賬號,在花費了很長(cháng)時(shí)間寫(xiě)好了一篇文章以后終于可以放松一下,但是想著(zhù)還有好幾個(gè)其他平臺需要復制粘貼著(zhù)實(shí)頭大,這個(gè)工作既沒(méi)有技術(shù)含量也提現不出文筆。我們下面分享幾種獲取公眾號的方式。
  第一種:人工-手動(dòng)復制粘貼
  這也是最笨的一種方法,手動(dòng)切換平臺登錄,然后打開(kāi)編輯器復制粘貼,然后手動(dòng)發(fā)布。
  優(yōu)勢:如果有錯誤一目了然,不同的平臺有不同的限制,比如標題字數,封面圖片等。
  劣勢:手動(dòng)操作浪費人力,效率低下。
  第二種:手動(dòng)-用數據采集工具
  下載數據采集工具,手動(dòng)輸入鏈接可以下載文章內容,然后導出成word或者其他格式。
  優(yōu)勢:無(wú)需技術(shù)配合
  劣勢:需要手動(dòng)操作,先獲取內容,然后導出,然后登錄自己平臺手動(dòng)導入。
  第三種:技術(shù)-抓?。ㄗ咚压罚?br />   可以在程序中做定時(shí)獲取固定公眾號同步文章操作,通過(guò)搜狗瀏覽器搜索微信號,得到文章列表,然后文章列表中有文章的url,得到url后通過(guò)程序下載文章內容然后可以繼續后續操作。
  優(yōu)勢:減少人工操作,可實(shí)現半自動(dòng)化自動(dòng)發(fā)布
  劣勢:如果公眾號很多,請求太頻繁會(huì )有驗證碼出現,程序很難自動(dòng)識別,一旦驗證碼還需要手動(dòng)驗證。而且無(wú)法實(shí)時(shí)同步,即公眾號發(fā)完后不會(huì )立馬同步。具體延時(shí)多少根據自己的請求頻率決定。還有一點(diǎn)就是獲取的鏈接是臨時(shí)鏈接,必須在有效時(shí)間范圍內把文章內容請求下來(lái)。
  第四種:技術(shù)-抓?。ㄗ呶⑿藕笈_)
  在微信后臺文章編輯中有一個(gè)插入超鏈接的功能。通過(guò)這個(gè)接口可以搜索公眾號,然后得到歷史文章,得到文章鏈接后可以通過(guò)后臺程序下載下來(lái)。
  優(yōu)勢:減少人工操作,可實(shí)現半自動(dòng)化自動(dòng)發(fā)布。此鏈接是永久鏈接,任何時(shí)候都可用。
  劣勢:同第二種如果請求太頻繁也會(huì )有攔截導致接口無(wú)法調用。
  第五種:技術(shù)-通過(guò)一鍵建站推送
  你只需要在一鍵建站平臺中開(kāi)通數據采集功能,配置好你接收數據的接口,一旦公眾號中有新的文章發(fā)布將給你推送最新的內容
  優(yōu)勢:延時(shí)時(shí)間短。
  操作簡(jiǎn)單代碼量少再也不用擔心技術(shù)無(wú)法實(shí)現。
  真正實(shí)現完全托管,全自動(dòng)化。
  劣勢:付費版,免費額度很少。但是價(jià)格也好像不貴幾分錢(qián)一條吧。
  以上幾種方式都已經(jīng)親測過(guò)了,如果有更多更好的方式記得聯(lián)系我,我也好去試試,如果有需要幫助或者技術(shù)不明白的可以加我溝通交流。

網(wǎng)絡(luò )爬蟲(chóng)實(shí)例系列 —— 搜狗微信文章采集方案

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 277 次瀏覽 ? 2022-05-05 04:05 ? 來(lái)自相關(guān)話(huà)題

  網(wǎng)絡(luò )爬蟲(chóng)實(shí)例系列 —— 搜狗微信文章采集方案
  微信和搜狗合作推出微信搜索后,讓各家媒體檢測平臺如獲珍寶,終于可以獲取到微信的文章數據了;但好景不長(cháng),由于大家基本只有這一個(gè)接口來(lái)獲取微信的數據,導致搜狗的壓力倍增,先后出現減少返回搜索結果條數,反爬蟲(chóng)輸入驗證碼的情況,導致大家又都很難抓取微信數據;不過(guò)后來(lái)搜狗又針對登錄用戶(hù)推出了關(guān)鍵字訂閱功能,結合這些情況,下面梳理了采集搜狗微信文章的兩個(gè)方案,僅供參考。
  方案一:利用搜狗微信文章搜索接口
  調用如下的URL便可訪(fǎng)問(wèn)搜狗微信關(guān)鍵字搜索文章的結果,其中參數query為搜索關(guān)鍵字,值為UTF-8編碼的字符串。該URL返回發(fā)結果為10條文章的搜索結果,可以直接解析到搜索出的文章名、文章URL、公眾號及摘要等信息。修改page參數的值,便可以進(jìn)行翻頁(yè),得到更多的搜索結果。
  %E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB&sut=13695&lkt=0%2C0%2C0&type=2&sst0=68&page=1&ie=utf8&w=01019900&dr=1
  優(yōu)點(diǎn):
  實(shí)現簡(jiǎn)單,搜索的結果和關(guān)鍵字匹配程度比較高
  缺點(diǎn):
  最多只能翻10頁(yè),而且網(wǎng)站反爬蟲(chóng)導致屏蔽比較嚴重
  方案二:利用搜狗微信提供的關(guān)鍵字訂閱接口
  在搜狗微信中,對于登錄的用戶(hù),搜索的關(guān)鍵字可以添加到訂閱列表中,下次可以直接查看自己訂閱關(guān)鍵字相關(guān)的文章。該功能限制每個(gè)登錄用戶(hù)最多可以添加20個(gè)訂閱關(guān)鍵字。
  A. 下面這個(gè)URL為獲取登錄用戶(hù)已經(jīng)訂閱關(guān)鍵字的列表,其中包括關(guān)鍵字和對應的wordId,該ID可以用于后續訪(fǎng)問(wèn)該關(guān)鍵字在訂閱中相關(guān)的文章。其中登錄用戶(hù)的標識是注冊的帳號,一參數uid傳入。
  uid=yongzhong15%&_=28
  B. 下面這個(gè)URL為指定的帳號添加訂閱關(guān)鍵字的接口,通過(guò)該接口,可以填加帳號訂閱的關(guān)鍵字。
  uid=yongzhong15%&word=%E5%9B%BE%E8%AE%BA&_=71
  C. 下面這個(gè)URL為指定的帳號刪除指定的關(guān)鍵字,其中關(guān)鍵字是指訂閱關(guān)鍵字列表中對應的wordId來(lái)指定
  uid=yongzhong15%&id=49529&_=72
  D. 下面這個(gè)URL返回指定用戶(hù)訂閱的其中一個(gè)關(guān)鍵字的文章列表,其中文件列表根據start參數進(jìn)行翻頁(yè),關(guān)鍵字是使用wordid來(lái)指定。
  uid=yongzhong15%&start=0&num=10&wordid=49528&clear=1&_=41
  根據以上A/B/C/D四類(lèi)API接口,便可以查詢(xún)注冊的帳號下的訂閱關(guān)鍵字列表,添加、刪除帳號下的關(guān)鍵字,并可以獲取到各個(gè)訂閱關(guān)鍵字對應的文章信息。
  優(yōu)點(diǎn):
  訂閱API由搜狗官方免費提供,不會(huì )被屏蔽
  缺點(diǎn):
  每個(gè)帳號最多只能訂閱20個(gè)關(guān)鍵字,使用不方便;而且對于每個(gè)關(guān)鍵字,返回的文章個(gè)數遠少于直接從搜索接口搜索到的相關(guān)文章數,即獲取到的數據不全。 查看全部

  網(wǎng)絡(luò )爬蟲(chóng)實(shí)例系列 —— 搜狗微信文章采集方案
  微信和搜狗合作推出微信搜索后,讓各家媒體檢測平臺如獲珍寶,終于可以獲取到微信的文章數據了;但好景不長(cháng),由于大家基本只有這一個(gè)接口來(lái)獲取微信的數據,導致搜狗的壓力倍增,先后出現減少返回搜索結果條數,反爬蟲(chóng)輸入驗證碼的情況,導致大家又都很難抓取微信數據;不過(guò)后來(lái)搜狗又針對登錄用戶(hù)推出了關(guān)鍵字訂閱功能,結合這些情況,下面梳理了采集搜狗微信文章的兩個(gè)方案,僅供參考。
  方案一:利用搜狗微信文章搜索接口
  調用如下的URL便可訪(fǎng)問(wèn)搜狗微信關(guān)鍵字搜索文章的結果,其中參數query為搜索關(guān)鍵字,值為UTF-8編碼的字符串。該URL返回發(fā)結果為10條文章的搜索結果,可以直接解析到搜索出的文章名、文章URL、公眾號及摘要等信息。修改page參數的值,便可以進(jìn)行翻頁(yè),得到更多的搜索結果。
  %E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB&sut=13695&lkt=0%2C0%2C0&type=2&sst0=68&page=1&ie=utf8&w=01019900&dr=1
  優(yōu)點(diǎn):
  實(shí)現簡(jiǎn)單,搜索的結果和關(guān)鍵字匹配程度比較高
  缺點(diǎn):
  最多只能翻10頁(yè),而且網(wǎng)站反爬蟲(chóng)導致屏蔽比較嚴重
  方案二:利用搜狗微信提供的關(guān)鍵字訂閱接口
  在搜狗微信中,對于登錄的用戶(hù),搜索的關(guān)鍵字可以添加到訂閱列表中,下次可以直接查看自己訂閱關(guān)鍵字相關(guān)的文章。該功能限制每個(gè)登錄用戶(hù)最多可以添加20個(gè)訂閱關(guān)鍵字。
  A. 下面這個(gè)URL為獲取登錄用戶(hù)已經(jīng)訂閱關(guān)鍵字的列表,其中包括關(guān)鍵字和對應的wordId,該ID可以用于后續訪(fǎng)問(wèn)該關(guān)鍵字在訂閱中相關(guān)的文章。其中登錄用戶(hù)的標識是注冊的帳號,一參數uid傳入。
  uid=yongzhong15%&_=28
  B. 下面這個(gè)URL為指定的帳號添加訂閱關(guān)鍵字的接口,通過(guò)該接口,可以填加帳號訂閱的關(guān)鍵字。
  uid=yongzhong15%&word=%E5%9B%BE%E8%AE%BA&_=71
  C. 下面這個(gè)URL為指定的帳號刪除指定的關(guān)鍵字,其中關(guān)鍵字是指訂閱關(guān)鍵字列表中對應的wordId來(lái)指定
  uid=yongzhong15%&id=49529&_=72
  D. 下面這個(gè)URL返回指定用戶(hù)訂閱的其中一個(gè)關(guān)鍵字的文章列表,其中文件列表根據start參數進(jìn)行翻頁(yè),關(guān)鍵字是使用wordid來(lái)指定。
  uid=yongzhong15%&start=0&num=10&wordid=49528&clear=1&_=41
  根據以上A/B/C/D四類(lèi)API接口,便可以查詢(xún)注冊的帳號下的訂閱關(guān)鍵字列表,添加、刪除帳號下的關(guān)鍵字,并可以獲取到各個(gè)訂閱關(guān)鍵字對應的文章信息。
  優(yōu)點(diǎn):
  訂閱API由搜狗官方免費提供,不會(huì )被屏蔽
  缺點(diǎn):
  每個(gè)帳號最多只能訂閱20個(gè)關(guān)鍵字,使用不方便;而且對于每個(gè)關(guān)鍵字,返回的文章個(gè)數遠少于直接從搜索接口搜索到的相關(guān)文章數,即獲取到的數據不全。

連載|淺談紅隊中的外網(wǎng)信息收集(一)

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 102 次瀏覽 ? 2022-05-01 14:10 ? 來(lái)自相關(guān)話(huà)題

  連載|淺談紅隊中的外網(wǎng)信息收集(一)
  前言
  最近在對以往所學(xué)習的有關(guān)紅隊的知識點(diǎn)進(jìn)行梳理總結,這里主要參考了 ATT&amp;CK 矩陣模型,不過(guò)對其進(jìn)行了簡(jiǎn)化,同時(shí)加入了一些國內特有的情況放了進(jìn)去。
  大體上會(huì )按照外網(wǎng)信息收集、打點(diǎn)、權限維持、提權、內網(wǎng)信息收集、橫向移動(dòng)、痕跡清理這樣的順序展開(kāi)。
  
  因為是梳理總結性的文章,所以文章的側重點(diǎn)在于「面」而不在于具體的某個(gè)「點(diǎn)」,因此文中的具體技術(shù)細節不會(huì )展開(kāi)去談,不然內容會(huì )很多。
  想了解具體細節的讀者可以去看看我的個(gè)人公眾號 TeamsSix 里的歷史文章,里面會(huì )有針對一些點(diǎn)展開(kāi)描述的文章。
  受限于個(gè)人水平,文中難免會(huì )出現錯誤或者描述不當的地方,還望在評論處指出,望諒解。
  確定目標
  當開(kāi)始做信息收集之前,肯定是要先確定目標的,在紅隊項目或者 HW 項目中,一般目標都是一個(gè)公司的名稱(chēng),然后通過(guò)這個(gè)公司的名稱(chēng)獲取各種信息,接著(zhù)開(kāi)展外網(wǎng)打點(diǎn)、內網(wǎng)滲透等等工作。
  在我們得知目標公司名稱(chēng)后,就可以開(kāi)展信息收集的工作了。
  外網(wǎng)信息收集
  我這里梳理了大概以下這些信息收集的方式,當然肯定是不全的,歡迎大家在評論區一起補充討論:
  
  1、組織股權結構
  拿到公司名稱(chēng)后,先不用急著(zhù)查備案、找域名,而是先看看這家公司的股權構成,因為一般一家公司的子公司也是可以作為目標去打的,不過(guò)有時(shí)是要求 50% 持股或者 100% 持股,這個(gè)就要看具體實(shí)際情況了。
  比較常見(jiàn)的查詢(xún)公司組織股權結構的網(wǎng)站有天眼查、企查查、愛(ài)企查、小藍本、釘釘企典等等。
  如果目標持股公司不多,可以直接看股權穿透圖,比較直觀(guān);如果持股公司比較多,股權穿透圖看著(zhù)就比較費力了。
  
  除了股權穿透之外,還可以看它的對外投資信息
  
  這兩個(gè)地方都可以查到有目標持股的公司。
  如果目標比較少一個(gè)一個(gè)子公司的去看還好,但如果目標很多,那這效率就很低了,好在現在也有了現成的工具。
  ENScanGo
  ENScanGo 是現有開(kāi)源項目 ENScan 的升級版本,工具地址:
  
  這是一款由狼組安全團隊的 Keac 師傅寫(xiě)的專(zhuān)門(mén)用來(lái)解決企業(yè)信息收集難的問(wèn)題的工具,可以一鍵收集目標及其控股公司的 ICP 備案、APP、小程序、微信公眾號等信息然后聚合導出。
  例如我這里搜集「北京百度網(wǎng)訊科技有限公司」以及他持股了 50% 的公司信息。
  enscan -n 北京百度網(wǎng)訊科技有限公司 -invest-num 50
  收集后的結果如下:
  
  這樣一來(lái),直接省去了收集 ICP 備案的步驟,一鍵獲得了目標公司及其子公司的公司名稱(chēng)、app、微信公眾號、ICP 備案等信息。
  2、主域名查詢(xún)
  主域名查詢(xún)可以分為備案域名查詢(xún)和未備案域名查詢(xún)。
  備案域名查詢(xún)
  除了上面從企業(yè)信息查詢(xún)網(wǎng)站中獲取到備案信息外,最全也是最準確的方法就是去國家的備案信息查詢(xún)網(wǎng)站里查詢(xún)了,地址為:。
  除了官方的渠道外,還有一些第三方的備案域名查詢(xún)站點(diǎn),比如站長(cháng)之家等等。
  未備案域名查詢(xún)
  有些企業(yè)會(huì )把自己的其他業(yè)務(wù)站點(diǎn)放在網(wǎng)站尾部,里面也許會(huì )包含未備案的站點(diǎn)。
  
  
  3、子域獲取
  比較常見(jiàn)的工具就是 OneForAll,除此之外還有 amass、subfinder、xray、ksubdomain 等等。
  如果提前知道目標,還可以提前收集一波子域,然后項目快開(kāi)始的時(shí)候,再收集一波子域,將兩次收集的結果做下對比,優(yōu)先打新增子域。
  4、端口掃描
  一般比較常見(jiàn)的可能就是 nmap 和 masscan 了,這里分享一個(gè) nmap 快速掃描全部端口的命令。
  nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -p 1-65535 -iL ip.txt -oX output.xml
  不過(guò)除了這些方法外,fscan 其實(shí)也可以拿來(lái)做外網(wǎng)的端口掃描,而且速度更快。
  比如用 fscan 只掃描端口,不做漏洞掃描
  fscan -hf hosts.txt --nopoc -t 100
  fscan 默認線(xiàn)程是 600,因為是外網(wǎng)掃描 600 的線(xiàn)程就比較大了,所以這里設置了 100,如果感覺(jué)掃描結果不理想,線(xiàn)程還可以設置的再小點(diǎn)。
  端口掃描可以結合空間搜索引擎的結果,如果通過(guò)空間搜索引擎發(fā)現目標存在很多的高位端口,那么在進(jìn)行端口掃描的時(shí)候就要記得也把這些高位端口加上。
  5、指紋識別
  指紋識別是我個(gè)人覺(jué)著(zhù)非常重要的一點(diǎn),因為指紋識別的結果對打點(diǎn)的幫助是很大的,可以讓打點(diǎn)更有針對性,同時(shí)也會(huì )節省很多時(shí)間。
  比較常見(jiàn)的在線(xiàn)指紋查詢(xún)網(wǎng)站有 godeye 和云悉等,工具有 observer_ward 和 Ehole 等。
  
  6、空間搜索引擎
  擅用空間搜索引擎,有時(shí)可以在最初剛拿確定目標的時(shí)候就發(fā)現目標弱點(diǎn)。
  目前比較常見(jiàn)的空間搜索引擎有 Fofa、Shodan、360 夸克、奇安信全球鷹、知道創(chuàng )宇 ZoomEye 等等。
  常見(jiàn)的工具有 Fofa_Viewer、FofaX、Kunyu,其中 Fofa_Viewer 為圖形化界面,使用友好。
  下載地址:
  
  FofaX 為命令行界面,FofaX 可以結合其他工具聯(lián)動(dòng)使用,除了這兩個(gè)工具調用 Fofa 接口外,上面提到的 Ehole 也可以調用 Fofa 查詢(xún)。
  Kunyu 調用的是 ZoomEye 的接口,工具下載地址:
  
  7、api 接口
  獲取 api 接口常用的工具有 jsinfo、findsomething、jsfinder、BurpJSLinkFinder 等等。
  如果找到了一些未授權接口,也許就可以搞到一些高價(jià)值信息,比如大量敏感信息泄露之類(lèi)的。
  另外在翻 js 文件的時(shí)候,可以關(guān)注下有沒(méi)有以 runtime 命名的 js 文件,因為在這種 js 文件中會(huì )包含其他 js 文件的名稱(chēng)(包括當前頁(yè)面沒(méi)有加載的 js 文件),這樣利用 runtime js 文件就發(fā)現了更多的 js 文件,使得找到 api 接口的概率又大了些。
  
  8、目錄獲取
  目錄掃描比較常用的工具有 dirsearch、ffuf
  ffuf 更側重于 FFUZ,不過(guò)不管是目錄掃描還是 FFUZ ,掃描的結果都在于字典,Github 上 4k 多個(gè) star 的字典:
  9、郵箱地址獲取
  郵箱地址比較常用的方法有直接通過(guò)搜索引擎找網(wǎng)上公開(kāi)的郵箱信息,這種往往會(huì )指向目標的網(wǎng)站中,比如目標某個(gè)網(wǎng)頁(yè)的附件中包含有郵箱等信息。
  之外還可以使用 Github 搜索目標公司開(kāi)發(fā)者在代碼中注釋的郵箱信息,其實(shí)不太明白為什么開(kāi)發(fā)者都喜歡把自己的郵箱注釋到代碼里。
  也可以通過(guò)領(lǐng)英找到目標公司的員工姓名,通過(guò)「拼音+@公司域名」的方法去構造員工郵箱。
  也有一些網(wǎng)站可以查詢(xún)郵箱,這種比較方便,比如以下網(wǎng)站:
  另外如果收集到了目標的 outlook 站點(diǎn),也可以嘗試去爆破郵箱用戶(hù)名。
  10、網(wǎng)盤(pán)信息
  網(wǎng)盤(pán)信息里有時(shí)也會(huì )發(fā)現不少好東西,這類(lèi)網(wǎng)站也很多,可以在愛(ài)達雜貨鋪導航站里找到很多網(wǎng)盤(pán)搜索類(lèi)站點(diǎn)。
  11、其他信息
  其他的信息比如 app、小程序、供應商、外包合作商、公眾號等,或多或少都可以從上面的組織股權架構類(lèi)網(wǎng)站中查詢(xún)到,或者使用 ENScan 也可以。
  其中比較值得注意是的供應商和外包合作商,如果拿下供應商也許可以直接進(jìn)入目標內網(wǎng),如果拿下外包合作商則可以借助這一層關(guān)系進(jìn)行社工或者嘗試進(jìn)入目標內網(wǎng)等操作。
  后記
  紅隊中的信息收集當然遠不止上面說(shuō)到的,其他比較常用的還有資產(chǎn)監控平臺、社工庫、自動(dòng)化信息收集工具(比如 ShuiZe)以及各種內部紅隊平臺等等,這里篇幅有限就不再展開(kāi)了。 查看全部

  連載|淺談紅隊中的外網(wǎng)信息收集(一)
  前言
  最近在對以往所學(xué)習的有關(guān)紅隊的知識點(diǎn)進(jìn)行梳理總結,這里主要參考了 ATT&amp;CK 矩陣模型,不過(guò)對其進(jìn)行了簡(jiǎn)化,同時(shí)加入了一些國內特有的情況放了進(jìn)去。
  大體上會(huì )按照外網(wǎng)信息收集、打點(diǎn)、權限維持、提權、內網(wǎng)信息收集、橫向移動(dòng)、痕跡清理這樣的順序展開(kāi)。
  
  因為是梳理總結性的文章,所以文章的側重點(diǎn)在于「面」而不在于具體的某個(gè)「點(diǎn)」,因此文中的具體技術(shù)細節不會(huì )展開(kāi)去談,不然內容會(huì )很多。
  想了解具體細節的讀者可以去看看我的個(gè)人公眾號 TeamsSix 里的歷史文章,里面會(huì )有針對一些點(diǎn)展開(kāi)描述的文章。
  受限于個(gè)人水平,文中難免會(huì )出現錯誤或者描述不當的地方,還望在評論處指出,望諒解。
  確定目標
  當開(kāi)始做信息收集之前,肯定是要先確定目標的,在紅隊項目或者 HW 項目中,一般目標都是一個(gè)公司的名稱(chēng),然后通過(guò)這個(gè)公司的名稱(chēng)獲取各種信息,接著(zhù)開(kāi)展外網(wǎng)打點(diǎn)、內網(wǎng)滲透等等工作。
  在我們得知目標公司名稱(chēng)后,就可以開(kāi)展信息收集的工作了。
  外網(wǎng)信息收集
  我這里梳理了大概以下這些信息收集的方式,當然肯定是不全的,歡迎大家在評論區一起補充討論:
  
  1、組織股權結構
  拿到公司名稱(chēng)后,先不用急著(zhù)查備案、找域名,而是先看看這家公司的股權構成,因為一般一家公司的子公司也是可以作為目標去打的,不過(guò)有時(shí)是要求 50% 持股或者 100% 持股,這個(gè)就要看具體實(shí)際情況了。
  比較常見(jiàn)的查詢(xún)公司組織股權結構的網(wǎng)站有天眼查、企查查、愛(ài)企查、小藍本、釘釘企典等等。
  如果目標持股公司不多,可以直接看股權穿透圖,比較直觀(guān);如果持股公司比較多,股權穿透圖看著(zhù)就比較費力了。
  
  除了股權穿透之外,還可以看它的對外投資信息
  
  這兩個(gè)地方都可以查到有目標持股的公司。
  如果目標比較少一個(gè)一個(gè)子公司的去看還好,但如果目標很多,那這效率就很低了,好在現在也有了現成的工具。
  ENScanGo
  ENScanGo 是現有開(kāi)源項目 ENScan 的升級版本,工具地址:
  
  這是一款由狼組安全團隊的 Keac 師傅寫(xiě)的專(zhuān)門(mén)用來(lái)解決企業(yè)信息收集難的問(wèn)題的工具,可以一鍵收集目標及其控股公司的 ICP 備案、APP、小程序、微信公眾號等信息然后聚合導出。
  例如我這里搜集「北京百度網(wǎng)訊科技有限公司」以及他持股了 50% 的公司信息。
  enscan -n 北京百度網(wǎng)訊科技有限公司 -invest-num 50
  收集后的結果如下:
  
  這樣一來(lái),直接省去了收集 ICP 備案的步驟,一鍵獲得了目標公司及其子公司的公司名稱(chēng)、app、微信公眾號、ICP 備案等信息。
  2、主域名查詢(xún)
  主域名查詢(xún)可以分為備案域名查詢(xún)和未備案域名查詢(xún)。
  備案域名查詢(xún)
  除了上面從企業(yè)信息查詢(xún)網(wǎng)站中獲取到備案信息外,最全也是最準確的方法就是去國家的備案信息查詢(xún)網(wǎng)站里查詢(xún)了,地址為:。
  除了官方的渠道外,還有一些第三方的備案域名查詢(xún)站點(diǎn),比如站長(cháng)之家等等。
  未備案域名查詢(xún)
  有些企業(yè)會(huì )把自己的其他業(yè)務(wù)站點(diǎn)放在網(wǎng)站尾部,里面也許會(huì )包含未備案的站點(diǎn)。
  
  
  3、子域獲取
  比較常見(jiàn)的工具就是 OneForAll,除此之外還有 amass、subfinder、xray、ksubdomain 等等。
  如果提前知道目標,還可以提前收集一波子域,然后項目快開(kāi)始的時(shí)候,再收集一波子域,將兩次收集的結果做下對比,優(yōu)先打新增子域。
  4、端口掃描
  一般比較常見(jiàn)的可能就是 nmap 和 masscan 了,這里分享一個(gè) nmap 快速掃描全部端口的命令。
  nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -p 1-65535 -iL ip.txt -oX output.xml
  不過(guò)除了這些方法外,fscan 其實(shí)也可以拿來(lái)做外網(wǎng)的端口掃描,而且速度更快。
  比如用 fscan 只掃描端口,不做漏洞掃描
  fscan -hf hosts.txt --nopoc -t 100
  fscan 默認線(xiàn)程是 600,因為是外網(wǎng)掃描 600 的線(xiàn)程就比較大了,所以這里設置了 100,如果感覺(jué)掃描結果不理想,線(xiàn)程還可以設置的再小點(diǎn)。
  端口掃描可以結合空間搜索引擎的結果,如果通過(guò)空間搜索引擎發(fā)現目標存在很多的高位端口,那么在進(jìn)行端口掃描的時(shí)候就要記得也把這些高位端口加上。
  5、指紋識別
  指紋識別是我個(gè)人覺(jué)著(zhù)非常重要的一點(diǎn),因為指紋識別的結果對打點(diǎn)的幫助是很大的,可以讓打點(diǎn)更有針對性,同時(shí)也會(huì )節省很多時(shí)間。
  比較常見(jiàn)的在線(xiàn)指紋查詢(xún)網(wǎng)站有 godeye 和云悉等,工具有 observer_ward 和 Ehole 等。
  
  6、空間搜索引擎
  擅用空間搜索引擎,有時(shí)可以在最初剛拿確定目標的時(shí)候就發(fā)現目標弱點(diǎn)。
  目前比較常見(jiàn)的空間搜索引擎有 Fofa、Shodan、360 夸克、奇安信全球鷹、知道創(chuàng )宇 ZoomEye 等等。
  常見(jiàn)的工具有 Fofa_Viewer、FofaX、Kunyu,其中 Fofa_Viewer 為圖形化界面,使用友好。
  下載地址:
  
  FofaX 為命令行界面,FofaX 可以結合其他工具聯(lián)動(dòng)使用,除了這兩個(gè)工具調用 Fofa 接口外,上面提到的 Ehole 也可以調用 Fofa 查詢(xún)。
  Kunyu 調用的是 ZoomEye 的接口,工具下載地址:
  
  7、api 接口
  獲取 api 接口常用的工具有 jsinfo、findsomething、jsfinder、BurpJSLinkFinder 等等。
  如果找到了一些未授權接口,也許就可以搞到一些高價(jià)值信息,比如大量敏感信息泄露之類(lèi)的。
  另外在翻 js 文件的時(shí)候,可以關(guān)注下有沒(méi)有以 runtime 命名的 js 文件,因為在這種 js 文件中會(huì )包含其他 js 文件的名稱(chēng)(包括當前頁(yè)面沒(méi)有加載的 js 文件),這樣利用 runtime js 文件就發(fā)現了更多的 js 文件,使得找到 api 接口的概率又大了些。
  
  8、目錄獲取
  目錄掃描比較常用的工具有 dirsearch、ffuf
  ffuf 更側重于 FFUZ,不過(guò)不管是目錄掃描還是 FFUZ ,掃描的結果都在于字典,Github 上 4k 多個(gè) star 的字典:
  9、郵箱地址獲取
  郵箱地址比較常用的方法有直接通過(guò)搜索引擎找網(wǎng)上公開(kāi)的郵箱信息,這種往往會(huì )指向目標的網(wǎng)站中,比如目標某個(gè)網(wǎng)頁(yè)的附件中包含有郵箱等信息。
  之外還可以使用 Github 搜索目標公司開(kāi)發(fā)者在代碼中注釋的郵箱信息,其實(shí)不太明白為什么開(kāi)發(fā)者都喜歡把自己的郵箱注釋到代碼里。
  也可以通過(guò)領(lǐng)英找到目標公司的員工姓名,通過(guò)「拼音+@公司域名」的方法去構造員工郵箱。
  也有一些網(wǎng)站可以查詢(xún)郵箱,這種比較方便,比如以下網(wǎng)站:
  另外如果收集到了目標的 outlook 站點(diǎn),也可以嘗試去爆破郵箱用戶(hù)名。
  10、網(wǎng)盤(pán)信息
  網(wǎng)盤(pán)信息里有時(shí)也會(huì )發(fā)現不少好東西,這類(lèi)網(wǎng)站也很多,可以在愛(ài)達雜貨鋪導航站里找到很多網(wǎng)盤(pán)搜索類(lèi)站點(diǎn)。
  11、其他信息
  其他的信息比如 app、小程序、供應商、外包合作商、公眾號等,或多或少都可以從上面的組織股權架構類(lèi)網(wǎng)站中查詢(xún)到,或者使用 ENScan 也可以。
  其中比較值得注意是的供應商和外包合作商,如果拿下供應商也許可以直接進(jìn)入目標內網(wǎng),如果拿下外包合作商則可以借助這一層關(guān)系進(jìn)行社工或者嘗試進(jìn)入目標內網(wǎng)等操作。
  后記
  紅隊中的信息收集當然遠不止上面說(shuō)到的,其他比較常用的還有資產(chǎn)監控平臺、社工庫、自動(dòng)化信息收集工具(比如 ShuiZe)以及各種內部紅隊平臺等等,這里篇幅有限就不再展開(kāi)了。

【爆款文章】集成服務(wù)與RPA的強強聯(lián)袂,速來(lái)圍觀(guān)!

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 93 次瀏覽 ? 2022-08-17 14:29 ? 來(lái)自相關(guān)話(huà)題

  【爆款文章】集成服務(wù)與RPA的強強聯(lián)袂,速來(lái)圍觀(guān)!
  金蝶云·蒼穹V5.0新特性系列文章持續更新
  每工作日早上07:45準時(shí)推送
  為你帶來(lái)最新產(chǎn)品資訊
  老師,業(yè)務(wù)上有個(gè)需求是把某異構系統的采購訂單集成到星瀚系統中,可對方系統不提供接口,沒(méi)辦法獲取數據啊。
  我的想法是用RPA機器人自動(dòng)登錄該異構系統獲取采購訂單信息,保存到Excel文件里面,然后再到集成服務(wù)云使用離線(xiàn)導入功能手工上傳文件,設置好字段映射關(guān)系之后加載到星瀚當中。
  但手工設置費時(shí)費力,有沒(méi)有辦法讓這個(gè)流程自動(dòng)化完成而不需要人工參與呢?
  開(kāi)發(fā)小白
  集成專(zhuān)家
  很簡(jiǎn)單,服務(wù)流程最新發(fā)布特性支持配置RPA節點(diǎn)。只需稍微改造RPA機器人,讓他多做一點(diǎn)事情(稍后揭曉),再把RPA組裝到服務(wù)流程中即可實(shí)現RPA集成。
  通過(guò)該方案,只需執行一個(gè)服務(wù)流程就能完成數據的獲取、轉換和加載步驟了~
  聽(tīng)起來(lái)很棒,迫不及待想請老師指教了呢。
  開(kāi)發(fā)小白
  集成專(zhuān)家
  那就讓我們趕緊開(kāi)始吧!
  適用版本
  該功能適用版本為金蝶云·蒼穹V4.0.016及以上。
  特性展示
  RPA是通過(guò)模擬用戶(hù)界面操作的方式來(lái)完成自動(dòng)化的一項技術(shù)。通過(guò)配置集成服務(wù)云服務(wù)流程的RPA節點(diǎn),可以在集成服務(wù)中嵌入RPA流程,將集成云從各個(gè)系統獲取到的數據信息作用于更廣泛的業(yè)務(wù)場(chǎng)景。
  接下來(lái),小編將以某異構系統的采購訂單集成到星瀚采購訂單為例,為大家介紹服務(wù)流程如何實(shí)現RPA集成。
  
  01 整體思路
  通過(guò)RPA模擬前臺操作,登錄異構系統,打開(kāi)采購訂單,獲取采購訂單數據并上傳Excel文件到FTP服務(wù)器中。
  然后,集成服務(wù)云通過(guò)服務(wù)編排組裝該RPA節點(diǎn)后,獲取相應數據,其他節點(diǎn)進(jìn)行數據的轉換、映射和加載,完成后將執行結果通過(guò)通知節點(diǎn)短信通知到用戶(hù)。
  服務(wù)流程集成RPA案例示例
  02實(shí)現步驟
  步驟一:RPA控制臺參數配置
  使用該功能的前提是【系統服務(wù)云】→【配置工具】→【系統參數】中配置了RPA控制臺的參數信息。如下圖所示:
  注:沒(méi)有此參數請聯(lián)系RPA部門(mén)提供。
  RPA控制臺系統集成參數
  步驟二:用戶(hù)授權與查看
  配置服務(wù)流程的用戶(hù)需要授權RPA通用角色。如下圖所示:
  RPA通用角色
  完成角色授權后,登錄RPA控制臺即可查看當前用戶(hù)可使用的RPA流程。如下圖所示:
  查看RPA流程
  步驟三:設計RPA流程
  RPA流程中,通過(guò)登錄異構系統、打開(kāi)采購訂單、獲取采購訂單數據和保存數據并上傳Excel文件四個(gè)子流程完成異構系統的采購訂單信息的數據獲取。
 ?。ㄗⅲ捍税咐鶕枨髨?chǎng)景改造RPA流程,增加上傳數據文件到FTP服務(wù)器子流程,非必選改造。)
  RPA主流程 - 登錄/打開(kāi)采購訂單/獲取數據/上傳
  各子流程配置詳情如下圖所示:
  
  左右滑動(dòng)查看更多>>(從左至右依次為登錄異構系統、打開(kāi)采購訂單、獲取采購訂單數據、保存并上傳Excel文件)
  步驟四:配置服務(wù)流程
  創(chuàng )建服務(wù)流程后,在流程圖中配置RPA節點(diǎn),選擇RPA流程和執行流程的RPA機器人。如下圖所示:
  服務(wù)流程中嵌入RPA節點(diǎn)
  RPA節點(diǎn)詳情
  步驟五:運行監控
  雙擊流程實(shí)例中的RPA節點(diǎn),可查看RPA任務(wù)執行詳情,如下圖所示:
  服務(wù)流程實(shí)例與RPA節點(diǎn)
  RPA節點(diǎn)執行詳情
  亮點(diǎn)價(jià)值
  亮點(diǎn)一:集成服務(wù)流程和RPA強強聯(lián)合
  打通集成服務(wù)云與RPA控制臺,輕松實(shí)現異構系統的集成與標準化流程的執行、多渠道采集、加工與處理數據,提高工作效率,助力企業(yè)業(yè)務(wù)集成自動(dòng)化。
  亮點(diǎn)二:流程配置簡(jiǎn)單,執行過(guò)程可追溯
  服務(wù)流程中可以直接配置一個(gè)或多個(gè)RPA節點(diǎn),通過(guò)用戶(hù)獲取RPA流程與RPA機器人信息,在服務(wù)流程執行時(shí)自動(dòng)觸發(fā)RPA機器人的執行,并且支持在流程實(shí)例中查看RPA任務(wù)詳情、RPA錄屏和日志信息等。
  亮點(diǎn)三:RPA流程的業(yè)務(wù)角色更直觀(guān)
  服務(wù)流程即業(yè)務(wù)集成流程,RPA節點(diǎn)作為其中一部分,可以直觀(guān)地看到RPA如何在整條業(yè)務(wù)鏈中發(fā)揮作用。
  相關(guān)鏈接
  關(guān)于RPA集成的詳細內容可參考下方鏈接:
  RPA集成指南V1.0:
  RPA節點(diǎn)錄入幣別到異構系統示例:
  服務(wù)流程調用RPA視頻:
  劃重點(diǎn)
  1) 集成服務(wù)云提供了RPA集成功能。通過(guò)該功能,集成服務(wù)流程中可組裝RPA機器人,在集成業(yè)務(wù)流程中實(shí)現標準化流程的自動(dòng)執行。
  2) 集成服務(wù)云的RPA集成具備以下特點(diǎn): 查看全部

  【爆款文章】集成服務(wù)與RPA的強強聯(lián)袂,速來(lái)圍觀(guān)!
  金蝶云·蒼穹V5.0新特性系列文章持續更新
  每工作日早上07:45準時(shí)推送
  為你帶來(lái)最新產(chǎn)品資訊
  老師,業(yè)務(wù)上有個(gè)需求是把某異構系統的采購訂單集成到星瀚系統中,可對方系統不提供接口,沒(méi)辦法獲取數據啊。
  我的想法是用RPA機器人自動(dòng)登錄該異構系統獲取采購訂單信息,保存到Excel文件里面,然后再到集成服務(wù)云使用離線(xiàn)導入功能手工上傳文件,設置好字段映射關(guān)系之后加載到星瀚當中。
  但手工設置費時(shí)費力,有沒(méi)有辦法讓這個(gè)流程自動(dòng)化完成而不需要人工參與呢?
  開(kāi)發(fā)小白
  集成專(zhuān)家
  很簡(jiǎn)單,服務(wù)流程最新發(fā)布特性支持配置RPA節點(diǎn)。只需稍微改造RPA機器人,讓他多做一點(diǎn)事情(稍后揭曉),再把RPA組裝到服務(wù)流程中即可實(shí)現RPA集成。
  通過(guò)該方案,只需執行一個(gè)服務(wù)流程就能完成數據的獲取、轉換和加載步驟了~
  聽(tīng)起來(lái)很棒,迫不及待想請老師指教了呢。
  開(kāi)發(fā)小白
  集成專(zhuān)家
  那就讓我們趕緊開(kāi)始吧!
  適用版本
  該功能適用版本為金蝶云·蒼穹V4.0.016及以上。
  特性展示
  RPA是通過(guò)模擬用戶(hù)界面操作的方式來(lái)完成自動(dòng)化的一項技術(shù)。通過(guò)配置集成服務(wù)云服務(wù)流程的RPA節點(diǎn),可以在集成服務(wù)中嵌入RPA流程,將集成云從各個(gè)系統獲取到的數據信息作用于更廣泛的業(yè)務(wù)場(chǎng)景。
  接下來(lái),小編將以某異構系統的采購訂單集成到星瀚采購訂單為例,為大家介紹服務(wù)流程如何實(shí)現RPA集成。
  
  01 整體思路
  通過(guò)RPA模擬前臺操作,登錄異構系統,打開(kāi)采購訂單,獲取采購訂單數據并上傳Excel文件到FTP服務(wù)器中。
  然后,集成服務(wù)云通過(guò)服務(wù)編排組裝該RPA節點(diǎn)后,獲取相應數據,其他節點(diǎn)進(jìn)行數據的轉換、映射和加載,完成后將執行結果通過(guò)通知節點(diǎn)短信通知到用戶(hù)。
  服務(wù)流程集成RPA案例示例
  02實(shí)現步驟
  步驟一:RPA控制臺參數配置
  使用該功能的前提是【系統服務(wù)云】→【配置工具】→【系統參數】中配置了RPA控制臺的參數信息。如下圖所示:
  注:沒(méi)有此參數請聯(lián)系RPA部門(mén)提供。
  RPA控制臺系統集成參數
  步驟二:用戶(hù)授權與查看
  配置服務(wù)流程的用戶(hù)需要授權RPA通用角色。如下圖所示:
  RPA通用角色
  完成角色授權后,登錄RPA控制臺即可查看當前用戶(hù)可使用的RPA流程。如下圖所示:
  查看RPA流程
  步驟三:設計RPA流程
  RPA流程中,通過(guò)登錄異構系統、打開(kāi)采購訂單、獲取采購訂單數據和保存數據并上傳Excel文件四個(gè)子流程完成異構系統的采購訂單信息的數據獲取。
 ?。ㄗⅲ捍税咐鶕枨髨?chǎng)景改造RPA流程,增加上傳數據文件到FTP服務(wù)器子流程,非必選改造。)
  RPA主流程 - 登錄/打開(kāi)采購訂單/獲取數據/上傳
  各子流程配置詳情如下圖所示:
  
  左右滑動(dòng)查看更多>>(從左至右依次為登錄異構系統、打開(kāi)采購訂單、獲取采購訂單數據、保存并上傳Excel文件)
  步驟四:配置服務(wù)流程
  創(chuàng )建服務(wù)流程后,在流程圖中配置RPA節點(diǎn),選擇RPA流程和執行流程的RPA機器人。如下圖所示:
  服務(wù)流程中嵌入RPA節點(diǎn)
  RPA節點(diǎn)詳情
  步驟五:運行監控
  雙擊流程實(shí)例中的RPA節點(diǎn),可查看RPA任務(wù)執行詳情,如下圖所示:
  服務(wù)流程實(shí)例與RPA節點(diǎn)
  RPA節點(diǎn)執行詳情
  亮點(diǎn)價(jià)值
  亮點(diǎn)一:集成服務(wù)流程和RPA強強聯(lián)合
  打通集成服務(wù)云與RPA控制臺,輕松實(shí)現異構系統的集成與標準化流程的執行、多渠道采集、加工與處理數據,提高工作效率,助力企業(yè)業(yè)務(wù)集成自動(dòng)化。
  亮點(diǎn)二:流程配置簡(jiǎn)單,執行過(guò)程可追溯
  服務(wù)流程中可以直接配置一個(gè)或多個(gè)RPA節點(diǎn),通過(guò)用戶(hù)獲取RPA流程與RPA機器人信息,在服務(wù)流程執行時(shí)自動(dòng)觸發(fā)RPA機器人的執行,并且支持在流程實(shí)例中查看RPA任務(wù)詳情、RPA錄屏和日志信息等。
  亮點(diǎn)三:RPA流程的業(yè)務(wù)角色更直觀(guān)
  服務(wù)流程即業(yè)務(wù)集成流程,RPA節點(diǎn)作為其中一部分,可以直觀(guān)地看到RPA如何在整條業(yè)務(wù)鏈中發(fā)揮作用。
  相關(guān)鏈接
  關(guān)于RPA集成的詳細內容可參考下方鏈接:
  RPA集成指南V1.0:
  RPA節點(diǎn)錄入幣別到異構系統示例:
  服務(wù)流程調用RPA視頻:
  劃重點(diǎn)
  1) 集成服務(wù)云提供了RPA集成功能。通過(guò)該功能,集成服務(wù)流程中可組裝RPA機器人,在集成業(yè)務(wù)流程中實(shí)現標準化流程的自動(dòng)執行。
  2) 集成服務(wù)云的RPA集成具備以下特點(diǎn):

紅隊從資產(chǎn)收集到打點(diǎn)

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 97 次瀏覽 ? 2022-08-12 04:28 ? 來(lái)自相關(guān)話(huà)題

  紅隊從資產(chǎn)收集到打點(diǎn)
  最近想總結一下,在紅隊滲透拿到一個(gè)目標名或者刷src時(shí)候,怎么快速信息收集和批量檢測來(lái)打到一個(gè)點(diǎn),往往在實(shí)際項目中就是拼手速。
  信息收集到打點(diǎn)大致我就分為
  企業(yè)信息結構收集敏感信息收集域名主動(dòng)被動(dòng)收集整理域名ip資產(chǎn)掃描檢測打點(diǎn)
  其中每一步需要收集好幾個(gè)方面的信息,手動(dòng)很累也很慢
  1.企業(yè)信息結構收集
  企業(yè)信息結構收集包括對查詢(xún)目標企業(yè)的公司信息,涉及到哪些主站域名,有哪些控股很多的子公司,這些子公司涉及到哪些域名,然后再進(jìn)行備案反查,你又會(huì )得到一些新的公司,同理也能再次得到一些新的主站域名,將這些進(jìn)行整理---->得到一批待爆破的域名。
  還有的就是除了這些查到的主站域名,往往企業(yè)會(huì )有app、公眾號、小程序這些資產(chǎn),也要對這些資產(chǎn)進(jìn)行收集,然后你又拿到了一批域名。
  手動(dòng)查詢(xún)的話(huà)從以下查詢(xún)
  天眼查 查企業(yè)/子公司/域名/公眾號 https://www.tianyancha.com/愛(ài)企查 https://aiqicha.baidu.com/企查查詢(xún) https://www.qcc.com/啟信寶 https://www.qixin.com/
  工具:
  推薦cSubsidiary利用天眼查查詢(xún)企業(yè)子公司https://github.com/canc3s/cSubsidiary<br />還有pigat:https://github.com/teamssix/pigat<br />公眾號和app的收集:https://github.com/wgpsec/ENSc ... an_GO go版本
  2.敏感信息收集
  利用搜索引擎、github等托管平臺配合一些dorks就可以搜到很多信息。
  熟知的googlehack,gitdork,網(wǎng)盤(pán)泄露等等。
  敏感信息一共要搜集這個(gè)幾個(gè)方面:
  googlehack語(yǔ)法github泄露目標人員姓名/手機/郵箱
  1.googlehack
  但比如googlehack,你需要搜的好幾條語(yǔ)法加上域名
  比如:
  site:*.domain.cominurl:domain.comintitle:keywordkeyword?filetyle:doc|pdf
  一個(gè)域名可以配合多個(gè)語(yǔ)法搜,那么多域名手動(dòng)輸入搜很慢,推薦工具:
  https://github.com/r00tSe7en/GoogleHackingTool 在線(xiàn)Google Hacking 小工具https://www.exploit-db.com/google-hacking-database 語(yǔ)法,自己可以腳本里批量搜
  2.github泄露敏感信息:
  一些常用github dorks,直接搜對應目標信息:
  xxxxx.com "Authorization" #"Authorization: Bearer"xxxxx.com "filename:vim_settings.xml"xxxxx.com "language:PHP"
  也可以在github對各種信息搜索,比如文件類(lèi)型
  filename:manifest.xmlfilename:travis.ymlfilename:vim_settings.xmlfilename:databasefilename:prod.exs NOT prod.secret.exsfilename:prod.secret.exsfilename:.npmrc _authfilename:.dockercfg authfilename:WebServers.xmlfilename:.bash_history filename:sftp-config.jsonfilename:sftp.json path:.vscodefilename:secrets.yml passwordfilename:.esmtprc passwordfilename:passwd path:etcfilename:dbeaver-data-sources.xmlpath:sites databases passwordfilename:config.php dbpasswdfilename:prod.secret.exsfilename:configuration.php JConfig passwordfilename:.sh_history
  包含關(guān)鍵字的指定語(yǔ)言:
  language:python usernamelanguage:php usernamelanguage:sql usernamelanguage:html passwordlanguage:perl passwordlanguage:shell usernamelanguage:java apiHOMEBREW_GITHUB_API_TOKEN language:shell
  搜API/KEYS/TOEKNS關(guān)鍵字:
  api_key“api keys”authorization_bearer:oauthauthauthenticationclient_secretapi_token:“api token”client_idpassworduser_passworduser_passpasscodeclient_secretsecretpassword hashOTPuser auth
  很多關(guān)鍵字可以搜,還是批量搜高效,工具:
  https://github.com/obheda12/Gi ... dorks
  這類(lèi)工具需要設置git令牌,附上gitrob過(guò)程,踩坑:不要下relase ,自己編譯最好:
  git clone https://github.com/michenriksen/gitrob.gitgo mod init #to use go mod 如果報錯 運行g(shù)o mod init github.com/michenriksen/gitrobrm Gopkg* #remove the old stuffgo build #to build it<br /><br />./build.sh
  設置git令牌
  set GITROB_ACCESS_TOKEN=xxxxx
  使用后可以查看圖形界面的結果:
  
  3. 目標人員姓名/手機/郵箱
  通過(guò)開(kāi)源信息收集目標人員姓名/手機/郵箱,為后面打點(diǎn)做字典做準備。
  https://github.com/laramies/theHarvester
  通過(guò)搜索引擎、PGP服務(wù)器以及SHODAN數據庫收集用戶(hù)的email,子域名,主機,雇員名,開(kāi)放端口和banner信息。
  使用:
  -d 開(kāi)關(guān)用于定義域名,-l 用于限制結果數量
  theHarvester -d kali.org -l 200 -banubis,baidu,pentesttools,projectdiscovery,qwant,rapiddns,rocketreach,securityTrails,spyse,sublist3r,threatcrowd,threatminer,trello,twitter,urlscan,virustotal,yahoo,zoomeye,bing,binaryedge,bingapi,bufferoverun,censys,certspotter,crtsh,dnsdumpster,duckduckgo,fullhunt,github-code,google,hackertarget,hunter,intelx,linkedin,linkedin_links,n45ht,omnisint,otx
  按github跑就是了,但是有點(diǎn)坑點(diǎn):
  配置api-keys在/etc/theHarvester 目錄下api-keys.yaml填入對應的api key即可
  有個(gè)坑點(diǎn)是key:后要加個(gè)空格在放key字符串,不然跑不起來(lái)
  人員郵箱字典的構造:
  https://github.com/pry0cc/Goog ... ed.rb
  還可以使用一些社工信息來(lái)做字典,這樣的工具很多了,用一個(gè)就夠了沒(méi)必要用全部:Cupp/Cewl
  https://github.com/r3nt0n/bopscrkpython3 bopscrk.py -i
  3. 域名主動(dòng)被動(dòng)收集
  域名主動(dòng)信息收集內容就有點(diǎn)雜了。
  通過(guò)1、2點(diǎn)我們拿到了一批等待爆破的域名和人員的信息,以及泄露的一些敏感信息(運氣好的話(huà)用泄露的信息已經(jīng)打到點(diǎn)了。)
  現在需要對域名進(jìn)行whois信息查詢(xún)、dns域名正反查詢(xún)、子域名探測爆破三個(gè)方面收集。
  1.whois信息查詢(xún)
  whois需要查詢(xún)域名的whois,然后根據whois信息來(lái)查詢(xún)歷史和反查,這樣你就得到了一些郵箱和可疑域名。
  查域名信息沒(méi)什么說(shuō)的,主要看網(wǎng)址注冊人、到期記錄、創(chuàng )建域的時(shí)間、名稱(chēng)服務(wù)器和聯(lián)系信息等,查最新的一般都是托管的信息,而查看歷史信息就有可能查到真實(shí)聯(lián)系人郵箱電話(huà)等:
  一些常見(jiàn)whois查詢(xún),手動(dòng)的時(shí)候可以查詢(xún):
  https://domaineye.com/reverse- ... whois
  除了正向查詢(xún)whois,還要查詢(xún)whois歷史信息:
  以下幾個(gè)網(wǎng)站允許用戶(hù)訪(fǎng)問(wèn)連接的 WHOIS 數據庫以進(jìn)行調查。這些記錄是十多年來(lái)對有關(guān)域注冊的有用數據進(jìn)行網(wǎng)絡(luò )爬取的結果:
  https://whois.domaintools.com/ ... .com/
  whois歷史信息查詢(xún)不能錯過(guò),明顯可以在whois歷史信息中看真實(shí)郵箱并反查而不是目前托管的郵箱,以及非托管的dns服務(wù)器:
  whois 信息反查
  通過(guò)歷史whois信息找到真實(shí)郵箱or組織名,再反查域名,又可以得到一批資產(chǎn):
  other:
  https://www.reversewhois.io/
  整理一下whois分了三步,先whois查詢(xún)一個(gè)域名,然后對查詢(xún)的信息進(jìn)行歷史whois查詢(xún)和反查,最后得到一批郵箱和域名。手動(dòng)知道過(guò)程就行,實(shí)際做項目用工具批量查了整理:
  https://github.com/xugj-gits/domain-tool 批量whois查詢(xún)https://github.com/melbadry9/WhoEnum
  
  2.dns域名正向反向查詢(xún)
  dns域名查詢(xún)分兩個(gè)部分,歷史記錄和ip反查:
  DNS歷史記錄(doamin2ips)
  Dnsdumpster 是一個(gè)在線(xiàn)實(shí)用程序,我們使用它來(lái)查找子域、目標的 DNS 記錄。
  VT也是可以看dns數據信息的:
  ip反查(ip2domains)
  同ip查詢(xún)多個(gè)解析到這個(gè)ip的域名,尋找更多web資產(chǎn)
  https://viewdns.info/reverseip/
  https://dnslytics.com/
  ip反查也可以使用dig、nslookup、host命令完成:
  工具推薦:
  https://www.infobyip.com/ipbulklookup.php 批量ip反查https://github.com/Sma11New/ip2domain 國內域名推薦ip2domain,會(huì )查詢(xún)權重、ICP備案等
  通過(guò)dns查詢(xún),我們拿到了一些域名和可疑ip段
  3.子域名探測爆破
  沒(méi)啥好說(shuō)的,主要是收集的渠道全、過(guò)濾泛解析。
  常見(jiàn)手法爆破子域名、證書(shū)透明度、搜索引擎、信息泄露、ASN號等等,很多工具已經(jīng)做了這些工作
  https://github.com/shmilylty/O ... redns
  4. 整理域名ip資產(chǎn)
  到這里大致的收集就結束了,就是要對收集結果進(jìn)行整理,通過(guò)上面收集能拿到:
  一批待探測存活的域名一批待確定的ip段一些郵箱,姓名,手機號一些敏感文件、信息、通用密碼(敏感信息收集階段看臉)
  整理后大致如上,有一步需要做的就是把收集的這些域名,轉成ip段,但是是需要判斷這個(gè)ip屬不屬于cdn,屬不屬于泛解析的ip,然后轉成ip后要判斷ip段的權重,哪些段才可能是目標主要的C段。
  https://github.com/EdgeSecurityTeam/Eeyes 對subdomain數據處理、獲取其中真實(shí)IP并整理成c段https://github.com/canc3s/cIPR 整理后查看權重
  5. 掃描檢測打點(diǎn)
  這步就開(kāi)始快速打點(diǎn)了。
  上面整理后的資產(chǎn),需要我們探測的是一批域名和一批C段
  域名需要做的事:
  探測存活title、banner提取、指紋識別爬蟲(chóng)、目錄輕量掃描、輕量漏掃
  C段需要做的事:
  掃描端口,探測存活將掃的web和非web進(jìn)行分類(lèi),把掃到的web資產(chǎn)加入到域名需要做的事,和對待域名沒(méi)區別將掃到的非web(數據庫/遠程登錄協(xié)議)進(jìn)行爆破,比如mysql爆破,rdp爆破
  一批域名和一批C段就這樣做不同的事,來(lái)先探測是否有脆弱的點(diǎn),最后才是回歸常規web,一個(gè)站一個(gè)站的去撕
  一些工具:
  https://github.com/broken5/WebAliveScan web存活判斷https://github.com/fadinglr/EHole 紅隊重點(diǎn)攻擊系統指紋探測工具https://github.com/k8gege/K8CScan 漏洞掃描、密碼爆破https://github.com/b1gcat/DarkEye 主機發(fā)現+爆破https://github.com/Adminisme/ServerScan 高并發(fā)網(wǎng)絡(luò )掃描、服務(wù)探測工具https://github.com/dean2021/titlesearch 批量抓取域名title工具https://github.com/pmiaowu/PmWebDirScan 批量掃目錄備份
  還有的就是一些大家都熟知的xray,vulmap之類(lèi)的漏洞,批量輕量去掃描一下即可。
  把上面的幾個(gè)步驟,工具串起來(lái),行成快速信息收集,快速探測打點(diǎn),最好寫(xiě)個(gè)貫穿流程的工具調用的腳本,自己寫(xiě)過(guò)效果不錯但代碼不好就不拿出來(lái)丟人了,基本這樣過(guò)一遍就容易打到一些比較脆弱的點(diǎn)。 查看全部

  紅隊從資產(chǎn)收集到打點(diǎn)
  最近想總結一下,在紅隊滲透拿到一個(gè)目標名或者刷src時(shí)候,怎么快速信息收集和批量檢測來(lái)打到一個(gè)點(diǎn),往往在實(shí)際項目中就是拼手速。
  信息收集到打點(diǎn)大致我就分為
  企業(yè)信息結構收集敏感信息收集域名主動(dòng)被動(dòng)收集整理域名ip資產(chǎn)掃描檢測打點(diǎn)
  其中每一步需要收集好幾個(gè)方面的信息,手動(dòng)很累也很慢
  1.企業(yè)信息結構收集
  企業(yè)信息結構收集包括對查詢(xún)目標企業(yè)的公司信息,涉及到哪些主站域名,有哪些控股很多的子公司,這些子公司涉及到哪些域名,然后再進(jìn)行備案反查,你又會(huì )得到一些新的公司,同理也能再次得到一些新的主站域名,將這些進(jìn)行整理---->得到一批待爆破的域名。
  還有的就是除了這些查到的主站域名,往往企業(yè)會(huì )有app、公眾號、小程序這些資產(chǎn),也要對這些資產(chǎn)進(jìn)行收集,然后你又拿到了一批域名。
  手動(dòng)查詢(xún)的話(huà)從以下查詢(xún)
  天眼查 查企業(yè)/子公司/域名/公眾號 https://www.tianyancha.com/愛(ài)企查 https://aiqicha.baidu.com/企查查詢(xún) https://www.qcc.com/啟信寶 https://www.qixin.com/
  工具:
  推薦cSubsidiary利用天眼查查詢(xún)企業(yè)子公司https://github.com/canc3s/cSubsidiary<br />還有pigat:https://github.com/teamssix/pigat<br />公眾號和app的收集:https://github.com/wgpsec/ENSc ... an_GO go版本
  2.敏感信息收集
  利用搜索引擎、github等托管平臺配合一些dorks就可以搜到很多信息。
  熟知的googlehack,gitdork,網(wǎng)盤(pán)泄露等等。
  敏感信息一共要搜集這個(gè)幾個(gè)方面:
  googlehack語(yǔ)法github泄露目標人員姓名/手機/郵箱
  1.googlehack
  但比如googlehack,你需要搜的好幾條語(yǔ)法加上域名
  比如:
  site:*.domain.cominurl:domain.comintitle:keywordkeyword?filetyle:doc|pdf
  一個(gè)域名可以配合多個(gè)語(yǔ)法搜,那么多域名手動(dòng)輸入搜很慢,推薦工具:
  https://github.com/r00tSe7en/GoogleHackingTool 在線(xiàn)Google Hacking 小工具https://www.exploit-db.com/google-hacking-database 語(yǔ)法,自己可以腳本里批量搜
  2.github泄露敏感信息:
  一些常用github dorks,直接搜對應目標信息:
  xxxxx.com "Authorization" #"Authorization: Bearer"xxxxx.com "filename:vim_settings.xml"xxxxx.com "language:PHP"
  也可以在github對各種信息搜索,比如文件類(lèi)型
  filename:manifest.xmlfilename:travis.ymlfilename:vim_settings.xmlfilename:databasefilename:prod.exs NOT prod.secret.exsfilename:prod.secret.exsfilename:.npmrc _authfilename:.dockercfg authfilename:WebServers.xmlfilename:.bash_history filename:sftp-config.jsonfilename:sftp.json path:.vscodefilename:secrets.yml passwordfilename:.esmtprc passwordfilename:passwd path:etcfilename:dbeaver-data-sources.xmlpath:sites databases passwordfilename:config.php dbpasswdfilename:prod.secret.exsfilename:configuration.php JConfig passwordfilename:.sh_history
  包含關(guān)鍵字的指定語(yǔ)言:
  language:python usernamelanguage:php usernamelanguage:sql usernamelanguage:html passwordlanguage:perl passwordlanguage:shell usernamelanguage:java apiHOMEBREW_GITHUB_API_TOKEN language:shell
  搜API/KEYS/TOEKNS關(guān)鍵字:
  api_key“api keys”authorization_bearer:oauthauthauthenticationclient_secretapi_token:“api token”client_idpassworduser_passworduser_passpasscodeclient_secretsecretpassword hashOTPuser auth
  很多關(guān)鍵字可以搜,還是批量搜高效,工具:
  https://github.com/obheda12/Gi ... dorks
  這類(lèi)工具需要設置git令牌,附上gitrob過(guò)程,踩坑:不要下relase ,自己編譯最好:
  git clone https://github.com/michenriksen/gitrob.gitgo mod init #to use go mod 如果報錯 運行g(shù)o mod init github.com/michenriksen/gitrobrm Gopkg* #remove the old stuffgo build #to build it<br /><br />./build.sh
  設置git令牌
  set GITROB_ACCESS_TOKEN=xxxxx
  使用后可以查看圖形界面的結果:
  
  3. 目標人員姓名/手機/郵箱
  通過(guò)開(kāi)源信息收集目標人員姓名/手機/郵箱,為后面打點(diǎn)做字典做準備。
  https://github.com/laramies/theHarvester
  通過(guò)搜索引擎、PGP服務(wù)器以及SHODAN數據庫收集用戶(hù)的email,子域名,主機,雇員名,開(kāi)放端口和banner信息。
  使用:
  -d 開(kāi)關(guān)用于定義域名,-l 用于限制結果數量
  theHarvester -d kali.org -l 200 -banubis,baidu,pentesttools,projectdiscovery,qwant,rapiddns,rocketreach,securityTrails,spyse,sublist3r,threatcrowd,threatminer,trello,twitter,urlscan,virustotal,yahoo,zoomeye,bing,binaryedge,bingapi,bufferoverun,censys,certspotter,crtsh,dnsdumpster,duckduckgo,fullhunt,github-code,google,hackertarget,hunter,intelx,linkedin,linkedin_links,n45ht,omnisint,otx
  按github跑就是了,但是有點(diǎn)坑點(diǎn):
  配置api-keys在/etc/theHarvester 目錄下api-keys.yaml填入對應的api key即可
  有個(gè)坑點(diǎn)是key:后要加個(gè)空格在放key字符串,不然跑不起來(lái)
  人員郵箱字典的構造:
  https://github.com/pry0cc/Goog ... ed.rb
  還可以使用一些社工信息來(lái)做字典,這樣的工具很多了,用一個(gè)就夠了沒(méi)必要用全部:Cupp/Cewl
  https://github.com/r3nt0n/bopscrkpython3 bopscrk.py -i
  3. 域名主動(dòng)被動(dòng)收集
  域名主動(dòng)信息收集內容就有點(diǎn)雜了。
  通過(guò)1、2點(diǎn)我們拿到了一批等待爆破的域名和人員的信息,以及泄露的一些敏感信息(運氣好的話(huà)用泄露的信息已經(jīng)打到點(diǎn)了。)
  現在需要對域名進(jìn)行whois信息查詢(xún)、dns域名正反查詢(xún)、子域名探測爆破三個(gè)方面收集。
  1.whois信息查詢(xún)
  whois需要查詢(xún)域名的whois,然后根據whois信息來(lái)查詢(xún)歷史和反查,這樣你就得到了一些郵箱和可疑域名。
  查域名信息沒(méi)什么說(shuō)的,主要看網(wǎng)址注冊人、到期記錄、創(chuàng )建域的時(shí)間、名稱(chēng)服務(wù)器和聯(lián)系信息等,查最新的一般都是托管的信息,而查看歷史信息就有可能查到真實(shí)聯(lián)系人郵箱電話(huà)等:
  一些常見(jiàn)whois查詢(xún),手動(dòng)的時(shí)候可以查詢(xún):
  https://domaineye.com/reverse- ... whois
  除了正向查詢(xún)whois,還要查詢(xún)whois歷史信息:
  以下幾個(gè)網(wǎng)站允許用戶(hù)訪(fǎng)問(wèn)連接的 WHOIS 數據庫以進(jìn)行調查。這些記錄是十多年來(lái)對有關(guān)域注冊的有用數據進(jìn)行網(wǎng)絡(luò )爬取的結果:
  https://whois.domaintools.com/ ... .com/
  whois歷史信息查詢(xún)不能錯過(guò),明顯可以在whois歷史信息中看真實(shí)郵箱并反查而不是目前托管的郵箱,以及非托管的dns服務(wù)器:
  whois 信息反查
  通過(guò)歷史whois信息找到真實(shí)郵箱or組織名,再反查域名,又可以得到一批資產(chǎn):
  other:
  https://www.reversewhois.io/
  整理一下whois分了三步,先whois查詢(xún)一個(gè)域名,然后對查詢(xún)的信息進(jìn)行歷史whois查詢(xún)和反查,最后得到一批郵箱和域名。手動(dòng)知道過(guò)程就行,實(shí)際做項目用工具批量查了整理:
  https://github.com/xugj-gits/domain-tool 批量whois查詢(xún)https://github.com/melbadry9/WhoEnum
  
  2.dns域名正向反向查詢(xún)
  dns域名查詢(xún)分兩個(gè)部分,歷史記錄和ip反查:
  DNS歷史記錄(doamin2ips)
  Dnsdumpster 是一個(gè)在線(xiàn)實(shí)用程序,我們使用它來(lái)查找子域、目標的 DNS 記錄。
  VT也是可以看dns數據信息的:
  ip反查(ip2domains)
  同ip查詢(xún)多個(gè)解析到這個(gè)ip的域名,尋找更多web資產(chǎn)
  https://viewdns.info/reverseip/
  https://dnslytics.com/
  ip反查也可以使用dig、nslookup、host命令完成:
  工具推薦:
  https://www.infobyip.com/ipbulklookup.php 批量ip反查https://github.com/Sma11New/ip2domain 國內域名推薦ip2domain,會(huì )查詢(xún)權重、ICP備案等
  通過(guò)dns查詢(xún),我們拿到了一些域名和可疑ip段
  3.子域名探測爆破
  沒(méi)啥好說(shuō)的,主要是收集的渠道全、過(guò)濾泛解析。
  常見(jiàn)手法爆破子域名、證書(shū)透明度、搜索引擎、信息泄露、ASN號等等,很多工具已經(jīng)做了這些工作
  https://github.com/shmilylty/O ... redns
  4. 整理域名ip資產(chǎn)
  到這里大致的收集就結束了,就是要對收集結果進(jìn)行整理,通過(guò)上面收集能拿到:
  一批待探測存活的域名一批待確定的ip段一些郵箱,姓名,手機號一些敏感文件、信息、通用密碼(敏感信息收集階段看臉)
  整理后大致如上,有一步需要做的就是把收集的這些域名,轉成ip段,但是是需要判斷這個(gè)ip屬不屬于cdn,屬不屬于泛解析的ip,然后轉成ip后要判斷ip段的權重,哪些段才可能是目標主要的C段。
  https://github.com/EdgeSecurityTeam/Eeyes 對subdomain數據處理、獲取其中真實(shí)IP并整理成c段https://github.com/canc3s/cIPR 整理后查看權重
  5. 掃描檢測打點(diǎn)
  這步就開(kāi)始快速打點(diǎn)了。
  上面整理后的資產(chǎn),需要我們探測的是一批域名和一批C段
  域名需要做的事:
  探測存活title、banner提取、指紋識別爬蟲(chóng)、目錄輕量掃描、輕量漏掃
  C段需要做的事:
  掃描端口,探測存活將掃的web和非web進(jìn)行分類(lèi),把掃到的web資產(chǎn)加入到域名需要做的事,和對待域名沒(méi)區別將掃到的非web(數據庫/遠程登錄協(xié)議)進(jìn)行爆破,比如mysql爆破,rdp爆破
  一批域名和一批C段就這樣做不同的事,來(lái)先探測是否有脆弱的點(diǎn),最后才是回歸常規web,一個(gè)站一個(gè)站的去撕
  一些工具:
  https://github.com/broken5/WebAliveScan web存活判斷https://github.com/fadinglr/EHole 紅隊重點(diǎn)攻擊系統指紋探測工具https://github.com/k8gege/K8CScan 漏洞掃描、密碼爆破https://github.com/b1gcat/DarkEye 主機發(fā)現+爆破https://github.com/Adminisme/ServerScan 高并發(fā)網(wǎng)絡(luò )掃描、服務(wù)探測工具https://github.com/dean2021/titlesearch 批量抓取域名title工具https://github.com/pmiaowu/PmWebDirScan 批量掃目錄備份
  還有的就是一些大家都熟知的xray,vulmap之類(lèi)的漏洞,批量輕量去掃描一下即可。
  把上面的幾個(gè)步驟,工具串起來(lái),行成快速信息收集,快速探測打點(diǎn),最好寫(xiě)個(gè)貫穿流程的工具調用的腳本,自己寫(xiě)過(guò)效果不錯但代碼不好就不拿出來(lái)丟人了,基本這樣過(guò)一遍就容易打到一些比較脆弱的點(diǎn)。

文章采集調用 【】lotlib

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 139 次瀏覽 ? 2022-08-04 09:09 ? 來(lái)自相關(guān)話(huà)題

  文章采集調用 【】lotlib
  文章采集調用本地瀏覽器特定鏈接,ip就用轉發(fā)地址好了。其他調用平臺,一般也是接口上獲取。各個(gè)視頻站點(diǎn)可能使用smi等格式數據來(lái)互相傳播,arctime支持?;ヂ?lián)網(wǎng)時(shí)代,隨著(zhù)網(wǎng)速增快,付費會(huì )員日益增多,付費視頻,付費文件所占比例在不斷提高。
  我不知道怎么用,但是我知道現在視頻調試都是一個(gè)程序來(lái)監控的,
<p>importarctimeimportjsonimportmatplotlib。pyplotaspltimportnumpyasnp#獲取官方文件大小,并轉換成acrobat格式defget_ac_transactions(data_content):url=''returnr' 查看全部

  文章采集調用 【】lotlib
  文章采集調用本地瀏覽器特定鏈接,ip就用轉發(fā)地址好了。其他調用平臺,一般也是接口上獲取。各個(gè)視頻站點(diǎn)可能使用smi等格式數據來(lái)互相傳播,arctime支持?;ヂ?lián)網(wǎng)時(shí)代,隨著(zhù)網(wǎng)速增快,付費會(huì )員日益增多,付費視頻,付費文件所占比例在不斷提高。
  我不知道怎么用,但是我知道現在視頻調試都是一個(gè)程序來(lái)監控的,
<p>importarctimeimportjsonimportmatplotlib。pyplotaspltimportnumpyasnp#獲取官方文件大小,并轉換成acrobat格式defget_ac_transactions(data_content):url=''returnr'

天** | 使用selenium做數據采集

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 147 次瀏覽 ? 2022-07-30 11:09 ? 來(lái)自相關(guān)話(huà)題

  天** | 使用selenium做數據采集
  哈工程管工在讀博士,擅長(cháng)數據采集&挖掘。
  文末有代碼可供下載
  馬云在接受CNBC(美國消費者新聞與商業(yè)頻道)采訪(fǎng)時(shí)提出:“整個(gè)世界將變成數據,我認為這還是只是數據時(shí)代的開(kāi)始。新浪潮即將來(lái)臨,很多就業(yè)機會(huì )將被奪走。有些人會(huì )趕上潮流,變得更加富有和成功。但是對于那些落后的人,未來(lái)將是痛苦的?!本托【幙磥?lái),這種說(shuō)法在人文社科研究當中也同樣適用。在當前數以萬(wàn)計甚至數以十萬(wàn)計研究樣本“遍地走”的時(shí)代,若我們還拘泥于傳統的“小樣本”研究(比如樣本量為100多的調查問(wèn)卷數據等),不僅難以跟隨時(shí)代的腳步,還會(huì )逐漸喪失學(xué)術(shù)競爭力、從而被時(shí)代淘汰。那么,究竟該如何獲取屬于自己的大樣本數據呢?今天小編就帶大家用selenium庫來(lái)爬取國內某知名第三方企業(yè)信息平臺(天**)的企業(yè)工商信息。
  一、自動(dòng)打開(kāi)網(wǎng)站頁(yè)面
  首先,數據爬取的第一步是利用selenium庫啟動(dòng)瀏覽器,打開(kāi)我們的目標網(wǎng)站。部分代碼
  #?@Author??:?Jacob-ZHANG<br />import?requests,base64<br />from?PIL?import?Image<br />import?csv,re<br />from?selenium?import?webdriver<br />import?time,random<br /><br />#1啟動(dòng)瀏覽器。<br />#win<br />browser=webdriver.Chrome(executable_path='driver/chromedriver.exe')<br />#mac<br />#browser=webdriver.Chrome(executable_path='driver/chromedriver')<br /><br />#2加入這個(gè)腳本可以避免被識別<br />browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",?{<br />"source":?"""<br />?????Object.defineProperty(navigator,?'webdriver',?{<br />??????get:?()?=>?undefined<br />????})<br />???"""?})<br /><br />#3延遲10s啟動(dòng)<br />browser.implicitly_wait(10)<br /><br />#4利用谷歌瀏覽器打開(kāi)目標網(wǎng)頁(yè)<br />browser.get('https://pro.xxxxxx.com/searchx')<br /><br />#5將窗口最大化<br />browser.maximize_window()<br /><br />#6給網(wǎng)頁(yè)一些時(shí)間加載<br />time.sleep(random.randint(1,?2))<br />...<br />...<br />...<br />
  非常簡(jiǎn)單,如下所示。
  需要說(shuō)明的是:
  二、模擬登陸
  天**反扒的第一關(guān)便是需要登錄才能夠查看具體的頁(yè)面信息。相比于利用復雜JS逆向技術(shù)完成登陸而言,利用selenium庫模擬人的操作、從而實(shí)現網(wǎng)站自動(dòng)化登陸的做法則顯得更為簡(jiǎn)單易行。從下圖來(lái)看,我們需要利用selenium庫來(lái)完成“點(diǎn)擊密碼登錄(切換到密碼登錄頁(yè)面,也即下圖所示頁(yè)面)-向賬號對話(huà)框內輸入賬號-向密碼對話(huà)框內輸入密碼-向驗證碼對話(huà)框內輸入驗證碼-點(diǎn)擊登錄”等一系列操作后,才能登錄到網(wǎng)站的信息頁(yè)面,獲取自己要想的數據。
  
  對于網(wǎng)站的登錄我們提供了以下兩種方法:一種是自動(dòng)化登錄;另一種則是手動(dòng)登錄。
  2.1 自動(dòng)化登錄
  首先,我們先來(lái)看看較為復雜的自動(dòng)化登錄。要想實(shí)現網(wǎng)頁(yè)的自動(dòng)化登錄,其關(guān)鍵在于利用 「外部力量」 來(lái)識別驗證碼并完成導入。具體而言,我們首先需要定位驗證碼在網(wǎng)頁(yè)當中的元素位置,其次利用截圖軟件根據驗證碼元素位置來(lái)截取驗證碼圖片,再次利用外部庫對驗證碼圖片進(jìn)行識別,最后將識別出的驗證碼錄入對話(huà)框。自動(dòng)化登錄的具體過(guò)程可以分為 get_code_image函數 和 parse_code函數 兩個(gè)步驟進(jìn)行,具體代碼如下所示。其中,驗證碼的解析小編是調用了百度AI的開(kāi)源庫進(jìn)行的。另外,需要注意的是,利用selenium庫打開(kāi)的登錄頁(yè)面一開(kāi)始是不顯示驗證碼的,必須向賬號框和密碼框輸入內容以后,它才會(huì )顯示驗證碼。因此,對于驗證碼的識別和錄入,我們將它放在了所有操作中的最后部分。
  parse_code函數
  def?parse_code():<br />????#用百度API解析圖片<br />????request_url?=?"https://aip.baidubce.com/rest/ ... %3Bbr />????f?=?open('temp/驗證碼.png',?'rb')<br />????img?=?base64.b64encode(f.read())<br />????params?=?{"image":?img}<br />????access_token?=?'24.a7fbbfb9dcab2e1054cc827f09d09234.2592000.1625930266.282335-19004069'<br /><br />????request_url?=?request_url?+?"?access_token="?+?access_token<br />????headers?=?{'content-type':?'application/x-www-form-urlencoded'}<br />????response?=?requests.post(request_url,?data=params,?headers=headers)<br />????<br />????#得到解析結果<br />????dictionary=response.json()<br />????<br />????#得到驗證碼<br />????yanzhengma=dictionary['words_result'][0]['words']<br />????<br />????#錄入驗證碼<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[6]/input').send_keys(yanzhengma)<br />????<br />????#?點(diǎn)擊登錄按鈕<br />????time.sleep(random.randint(1,?2))<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[8]').click()<br /><br />
  parse_code函數
  def?get_code_image():<br />????# 1啟動(dòng)瀏覽器。<br />????browser?=?webdriver.Chrome()<br />????#?2加入這個(gè)腳本可以避免被識別<br />????browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",?{<br />????????"source":?"""<br />?????????Object.defineProperty(navigator,?'webdriver',?{<br />??????????get:?()?=>?undefined<br />????????})<br />???????"""})<br />????#?3延遲10s啟動(dòng)<br />????browser.implicitly_wait(10)<br />????<br />????#?4利用谷歌瀏覽器打開(kāi)目標網(wǎng)頁(yè)<br />????browser.get('https://pro.xxxxxx.com/searchx')<br />????<br />????#?5將窗口最大化<br />????browser.maximize_window()<br />????<br />????#?6給網(wǎng)頁(yè)一些時(shí)間加載<br />????time.sleep(random.randint(1,?2))<br /><br />????#將頁(yè)面從快捷登錄切換到密碼登錄<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/div[1]/div[2]').click()<br />????time.sleep(0.5)<br />????<br />????#輸入賬號<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[2]/input').send_keys(<br />????????'******')<br />????time.sleep(random.randint(1,?2))<br />????<br />????#?輸入密碼<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[4]/input').send_keys('*******')<br />????time.sleep(random.randint(1,?2))<br />????browser.save_screenshot('temp/屏幕.png')#截圖整個(gè)頁(yè)面】<br />????<br />????#定位驗證碼x,y坐標<br />????left_angle=browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[6]/img').location<br />????image=browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[6]/img')<br />????<br />????#獲取驗證碼的長(cháng)和寬<br />????size=image.size<br />????<br />????#設定我們需要截取的位置<br />????rangle?=?(int(left_angle['x']),?int(left_angle['y']?),?int(left_angle['x']?+?size['width']?+?230),<br /><br />??????????????int(left_angle['y']?+?size['height']?+?300))<br />????<br />????#打開(kāi)截圖<br />????open_image=Image.open('temp/屏幕.png')<br />????<br />????#從圖片中截取我們需要的的區域<br />????jietu=open_image.crop(rangle)<br />????jietu.save('temp/驗證碼.png')<br />
  接下來(lái),我們再來(lái)看看如何實(shí)現手動(dòng)登錄。相比于自動(dòng)化登錄,手動(dòng)登錄的操作更為簡(jiǎn)單。具體地,我們只要在完成自動(dòng)打開(kāi)網(wǎng)站頁(yè)面的代碼后加入input()函數,然后自己手動(dòng)向網(wǎng)站的對話(huà)框內輸入賬號、密碼、驗證碼并點(diǎn)擊登錄,就可以進(jìn)入到網(wǎng)站的信息頁(yè)面。
  三、獲取自己想要的數據
  完成登陸之后,就會(huì )跳轉到如下頁(yè)面。然后,大家就可以根據自己的目標繼續撰寫(xiě)屬于自己的“個(gè)性化代碼”了。下面,小編以獲取31個(gè)省市的特定類(lèi)型的企業(yè)數據為例,給大家分享一下自己獲取數據的過(guò)程。
  
  其實(shí),代碼撰寫(xiě)的邏輯很簡(jiǎn)單。首先要做的就是先選中我們要爬取的目標城市。下來(lái)就是根據自己的需求來(lái)定制個(gè)性化的篩選標準。以小編自己的需求為例,先通過(guò)點(diǎn)擊高級模式,向企業(yè)名稱(chēng)對話(huà)框里輸入關(guān)鍵詞,比如醫院(當然,大家也可以通過(guò)限定行業(yè)來(lái)挑選目標);然后,去掉機構類(lèi)型中已勾選的企業(yè),選擇事業(yè)單位;接下來(lái),勾選全部企業(yè)類(lèi)型;最后點(diǎn)擊查看結果。
  至此就完成了篩選,得到了滿(mǎn)足我們要求的所有企業(yè)(見(jiàn)下圖1)。接下來(lái),我們要做的就是遍歷每一頁(yè)里的每一家企業(yè),然后獲取企業(yè)頁(yè)面信息(見(jiàn)下圖2)中自己想要的數據了。
  由于后續代碼較長(cháng),就不在這里一一列舉了。有需要的小伙伴可以在后臺留言,然后向xx索取。
  說(shuō)明
  對于初學(xué)者而言,直接上手可能比較難,建議先收藏本文,待熟練掌握爬蟲(chóng)可以實(shí)驗本文的代碼。
  如果想復現本文代碼,需熟悉
  本文教程&代碼免費分享,但作者時(shí)間和精力寶貴,可能無(wú)法做到一一指導,盡請包涵。
  代碼鏈接: 提取碼: ob2j
  精選文章
  <p style="margin: 0px;padding: 0px;clear: both;min-height: 1em;outline: 0px;max-width: 100%;color: rgb(63, 63, 63);font-size: 15px;letter-spacing: 0px;white-space: normal;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;line-height: normal;box-sizing: border-box !important;overflow-wrap: break-word !important;">長(cháng)期招募小伙伴
  從符號到嵌入:計算社會(huì )科學(xué)的兩種文本表示<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  推薦 | 社科(經(jīng)管)文本分析快速指南<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  使用cntext訓練Glove詞嵌入模型
  cntext庫 | 關(guān)于DUTIR被污染解決辦法<br style="margin: 0px;padding: 0px;" />
  EmoBank | 中文維度情感詞典<br />
  sklearnex庫 | 兩行代碼百倍加速你的機器學(xué)習代碼<br style="margin: 0px;padding: 0px;" />
  認知的測量 | 向量距離vs語(yǔ)義投影
  Wordify | 發(fā)現和區分消費者詞匯的工具
  karateclub庫 | 計算社交網(wǎng)絡(luò )中節點(diǎn)的向量
  視頻專(zhuān)欄課 | Python網(wǎng)絡(luò )爬蟲(chóng)與文本分析
  擴增內置pkl | 歡迎各位向cntext庫分享情感詞典<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  文本分析 | 中國企業(yè)高管團隊創(chuàng )新注意力(含代碼)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  LIWC vs Python | 文本分析之詞典統計法略講(含代碼)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  PNAS | 文本網(wǎng)絡(luò )分析&文化橋梁Python代碼實(shí)現<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  Wordify | 發(fā)現和區分消費者詞匯的工具
  BERTopic庫 | 使用預訓練模型做話(huà)題建模
  tomotopy | 速度最快的LDA主題模型
  文本分析方法在《管理世界》(2021.5)中的應用
  Wow~70G上市公司定期報告數據集
  doccano|為機器學(xué)習建模做數據標注
  使用WeasyPrint自動(dòng)生成pdf報告文件
  100min視頻 | Python文本分析與會(huì )計
  在jupyter內運行R代碼</p> 查看全部

  天** | 使用selenium做數據采集
  哈工程管工在讀博士,擅長(cháng)數據采集&挖掘。
  文末有代碼可供下載
  馬云在接受CNBC(美國消費者新聞與商業(yè)頻道)采訪(fǎng)時(shí)提出:“整個(gè)世界將變成數據,我認為這還是只是數據時(shí)代的開(kāi)始。新浪潮即將來(lái)臨,很多就業(yè)機會(huì )將被奪走。有些人會(huì )趕上潮流,變得更加富有和成功。但是對于那些落后的人,未來(lái)將是痛苦的?!本托【幙磥?lái),這種說(shuō)法在人文社科研究當中也同樣適用。在當前數以萬(wàn)計甚至數以十萬(wàn)計研究樣本“遍地走”的時(shí)代,若我們還拘泥于傳統的“小樣本”研究(比如樣本量為100多的調查問(wèn)卷數據等),不僅難以跟隨時(shí)代的腳步,還會(huì )逐漸喪失學(xué)術(shù)競爭力、從而被時(shí)代淘汰。那么,究竟該如何獲取屬于自己的大樣本數據呢?今天小編就帶大家用selenium庫來(lái)爬取國內某知名第三方企業(yè)信息平臺(天**)的企業(yè)工商信息。
  一、自動(dòng)打開(kāi)網(wǎng)站頁(yè)面
  首先,數據爬取的第一步是利用selenium庫啟動(dòng)瀏覽器,打開(kāi)我們的目標網(wǎng)站。部分代碼
  #?@Author??:?Jacob-ZHANG<br />import?requests,base64<br />from?PIL?import?Image<br />import?csv,re<br />from?selenium?import?webdriver<br />import?time,random<br /><br />#1啟動(dòng)瀏覽器。<br />#win<br />browser=webdriver.Chrome(executable_path='driver/chromedriver.exe')<br />#mac<br />#browser=webdriver.Chrome(executable_path='driver/chromedriver')<br /><br />#2加入這個(gè)腳本可以避免被識別<br />browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",?{<br />"source":?"""<br />?????Object.defineProperty(navigator,?'webdriver',?{<br />??????get:?()?=>?undefined<br />????})<br />???"""?})<br /><br />#3延遲10s啟動(dòng)<br />browser.implicitly_wait(10)<br /><br />#4利用谷歌瀏覽器打開(kāi)目標網(wǎng)頁(yè)<br />browser.get('https://pro.xxxxxx.com/searchx')<br /><br />#5將窗口最大化<br />browser.maximize_window()<br /><br />#6給網(wǎng)頁(yè)一些時(shí)間加載<br />time.sleep(random.randint(1,?2))<br />...<br />...<br />...<br />
  非常簡(jiǎn)單,如下所示。
  需要說(shuō)明的是:
  二、模擬登陸
  天**反扒的第一關(guān)便是需要登錄才能夠查看具體的頁(yè)面信息。相比于利用復雜JS逆向技術(shù)完成登陸而言,利用selenium庫模擬人的操作、從而實(shí)現網(wǎng)站自動(dòng)化登陸的做法則顯得更為簡(jiǎn)單易行。從下圖來(lái)看,我們需要利用selenium庫來(lái)完成“點(diǎn)擊密碼登錄(切換到密碼登錄頁(yè)面,也即下圖所示頁(yè)面)-向賬號對話(huà)框內輸入賬號-向密碼對話(huà)框內輸入密碼-向驗證碼對話(huà)框內輸入驗證碼-點(diǎn)擊登錄”等一系列操作后,才能登錄到網(wǎng)站的信息頁(yè)面,獲取自己要想的數據。
  
  對于網(wǎng)站的登錄我們提供了以下兩種方法:一種是自動(dòng)化登錄;另一種則是手動(dòng)登錄。
  2.1 自動(dòng)化登錄
  首先,我們先來(lái)看看較為復雜的自動(dòng)化登錄。要想實(shí)現網(wǎng)頁(yè)的自動(dòng)化登錄,其關(guān)鍵在于利用 「外部力量」 來(lái)識別驗證碼并完成導入。具體而言,我們首先需要定位驗證碼在網(wǎng)頁(yè)當中的元素位置,其次利用截圖軟件根據驗證碼元素位置來(lái)截取驗證碼圖片,再次利用外部庫對驗證碼圖片進(jìn)行識別,最后將識別出的驗證碼錄入對話(huà)框。自動(dòng)化登錄的具體過(guò)程可以分為 get_code_image函數 和 parse_code函數 兩個(gè)步驟進(jìn)行,具體代碼如下所示。其中,驗證碼的解析小編是調用了百度AI的開(kāi)源庫進(jìn)行的。另外,需要注意的是,利用selenium庫打開(kāi)的登錄頁(yè)面一開(kāi)始是不顯示驗證碼的,必須向賬號框和密碼框輸入內容以后,它才會(huì )顯示驗證碼。因此,對于驗證碼的識別和錄入,我們將它放在了所有操作中的最后部分。
  parse_code函數
  def?parse_code():<br />????#用百度API解析圖片<br />????request_url?=?"https://aip.baidubce.com/rest/ ... %3Bbr />????f?=?open('temp/驗證碼.png',?'rb')<br />????img?=?base64.b64encode(f.read())<br />????params?=?{"image":?img}<br />????access_token?=?'24.a7fbbfb9dcab2e1054cc827f09d09234.2592000.1625930266.282335-19004069'<br /><br />????request_url?=?request_url?+?"?access_token="?+?access_token<br />????headers?=?{'content-type':?'application/x-www-form-urlencoded'}<br />????response?=?requests.post(request_url,?data=params,?headers=headers)<br />????<br />????#得到解析結果<br />????dictionary=response.json()<br />????<br />????#得到驗證碼<br />????yanzhengma=dictionary['words_result'][0]['words']<br />????<br />????#錄入驗證碼<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[6]/input').send_keys(yanzhengma)<br />????<br />????#?點(diǎn)擊登錄按鈕<br />????time.sleep(random.randint(1,?2))<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[8]').click()<br /><br />
  parse_code函數
  def?get_code_image():<br />????# 1啟動(dòng)瀏覽器。<br />????browser?=?webdriver.Chrome()<br />????#?2加入這個(gè)腳本可以避免被識別<br />????browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",?{<br />????????"source":?"""<br />?????????Object.defineProperty(navigator,?'webdriver',?{<br />??????????get:?()?=>?undefined<br />????????})<br />???????"""})<br />????#?3延遲10s啟動(dòng)<br />????browser.implicitly_wait(10)<br />????<br />????#?4利用谷歌瀏覽器打開(kāi)目標網(wǎng)頁(yè)<br />????browser.get('https://pro.xxxxxx.com/searchx')<br />????<br />????#?5將窗口最大化<br />????browser.maximize_window()<br />????<br />????#?6給網(wǎng)頁(yè)一些時(shí)間加載<br />????time.sleep(random.randint(1,?2))<br /><br />????#將頁(yè)面從快捷登錄切換到密碼登錄<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/div[1]/div[2]').click()<br />????time.sleep(0.5)<br />????<br />????#輸入賬號<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[2]/input').send_keys(<br />????????'******')<br />????time.sleep(random.randint(1,?2))<br />????<br />????#?輸入密碼<br />????browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[4]/input').send_keys('*******')<br />????time.sleep(random.randint(1,?2))<br />????browser.save_screenshot('temp/屏幕.png')#截圖整個(gè)頁(yè)面】<br />????<br />????#定位驗證碼x,y坐標<br />????left_angle=browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[6]/img').location<br />????image=browser.find_element_by_xpath('//*[@id="web-content"]/div/div[2]/div[3]/form/div[6]/img')<br />????<br />????#獲取驗證碼的長(cháng)和寬<br />????size=image.size<br />????<br />????#設定我們需要截取的位置<br />????rangle?=?(int(left_angle['x']),?int(left_angle['y']?),?int(left_angle['x']?+?size['width']?+?230),<br /><br />??????????????int(left_angle['y']?+?size['height']?+?300))<br />????<br />????#打開(kāi)截圖<br />????open_image=Image.open('temp/屏幕.png')<br />????<br />????#從圖片中截取我們需要的的區域<br />????jietu=open_image.crop(rangle)<br />????jietu.save('temp/驗證碼.png')<br />
  接下來(lái),我們再來(lái)看看如何實(shí)現手動(dòng)登錄。相比于自動(dòng)化登錄,手動(dòng)登錄的操作更為簡(jiǎn)單。具體地,我們只要在完成自動(dòng)打開(kāi)網(wǎng)站頁(yè)面的代碼后加入input()函數,然后自己手動(dòng)向網(wǎng)站的對話(huà)框內輸入賬號、密碼、驗證碼并點(diǎn)擊登錄,就可以進(jìn)入到網(wǎng)站的信息頁(yè)面。
  三、獲取自己想要的數據
  完成登陸之后,就會(huì )跳轉到如下頁(yè)面。然后,大家就可以根據自己的目標繼續撰寫(xiě)屬于自己的“個(gè)性化代碼”了。下面,小編以獲取31個(gè)省市的特定類(lèi)型的企業(yè)數據為例,給大家分享一下自己獲取數據的過(guò)程。
  
  其實(shí),代碼撰寫(xiě)的邏輯很簡(jiǎn)單。首先要做的就是先選中我們要爬取的目標城市。下來(lái)就是根據自己的需求來(lái)定制個(gè)性化的篩選標準。以小編自己的需求為例,先通過(guò)點(diǎn)擊高級模式,向企業(yè)名稱(chēng)對話(huà)框里輸入關(guān)鍵詞,比如醫院(當然,大家也可以通過(guò)限定行業(yè)來(lái)挑選目標);然后,去掉機構類(lèi)型中已勾選的企業(yè),選擇事業(yè)單位;接下來(lái),勾選全部企業(yè)類(lèi)型;最后點(diǎn)擊查看結果。
  至此就完成了篩選,得到了滿(mǎn)足我們要求的所有企業(yè)(見(jiàn)下圖1)。接下來(lái),我們要做的就是遍歷每一頁(yè)里的每一家企業(yè),然后獲取企業(yè)頁(yè)面信息(見(jiàn)下圖2)中自己想要的數據了。
  由于后續代碼較長(cháng),就不在這里一一列舉了。有需要的小伙伴可以在后臺留言,然后向xx索取。
  說(shuō)明
  對于初學(xué)者而言,直接上手可能比較難,建議先收藏本文,待熟練掌握爬蟲(chóng)可以實(shí)驗本文的代碼。
  如果想復現本文代碼,需熟悉
  本文教程&代碼免費分享,但作者時(shí)間和精力寶貴,可能無(wú)法做到一一指導,盡請包涵。
  代碼鏈接: 提取碼: ob2j
  精選文章
  <p style="margin: 0px;padding: 0px;clear: both;min-height: 1em;outline: 0px;max-width: 100%;color: rgb(63, 63, 63);font-size: 15px;letter-spacing: 0px;white-space: normal;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;line-height: normal;box-sizing: border-box !important;overflow-wrap: break-word !important;">長(cháng)期招募小伙伴
  從符號到嵌入:計算社會(huì )科學(xué)的兩種文本表示<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  推薦 | 社科(經(jīng)管)文本分析快速指南<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  使用cntext訓練Glove詞嵌入模型
  cntext庫 | 關(guān)于DUTIR被污染解決辦法<br style="margin: 0px;padding: 0px;" />
  EmoBank | 中文維度情感詞典<br />
  sklearnex庫 | 兩行代碼百倍加速你的機器學(xué)習代碼<br style="margin: 0px;padding: 0px;" />
  認知的測量 | 向量距離vs語(yǔ)義投影
  Wordify | 發(fā)現和區分消費者詞匯的工具
  karateclub庫 | 計算社交網(wǎng)絡(luò )中節點(diǎn)的向量
  視頻專(zhuān)欄課 | Python網(wǎng)絡(luò )爬蟲(chóng)與文本分析
  擴增內置pkl | 歡迎各位向cntext庫分享情感詞典<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  文本分析 | 中國企業(yè)高管團隊創(chuàng )新注意力(含代碼)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  LIWC vs Python | 文本分析之詞典統計法略講(含代碼)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  PNAS | 文本網(wǎng)絡(luò )分析&文化橋梁Python代碼實(shí)現<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  Wordify | 發(fā)現和區分消費者詞匯的工具
  BERTopic庫 | 使用預訓練模型做話(huà)題建模
  tomotopy | 速度最快的LDA主題模型
  文本分析方法在《管理世界》(2021.5)中的應用
  Wow~70G上市公司定期報告數據集
  doccano|為機器學(xué)習建模做數據標注
  使用WeasyPrint自動(dòng)生成pdf報告文件
  100min視頻 | Python文本分析與會(huì )計
  在jupyter內運行R代碼</p>

文章采集調用 《抽獎》某次紅藍對抗之Solr-RCE實(shí)戰繞過(guò)

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 133 次瀏覽 ? 2022-07-28 03:27 ? 來(lái)自相關(guān)話(huà)題

  文章采集調用 《抽獎》某次紅藍對抗之Solr-RCE實(shí)戰繞過(guò)
  ?
  文章首發(fā)于:先知社區
  前言
  在某次紅藍對抗過(guò)程中。
  要結束的時(shí)候突然發(fā)現了掃描器爆出了
  Solr-RCE-CVE-2019-0192漏洞。
  但是進(jìn)行測試過(guò)程中,發(fā)現存在各種各樣的問(wèn)題
  繞過(guò)1
  進(jìn)行測試,發(fā)現目標只可以執行單命令,返回字段比較少的命令。
  輸入:whoami、ipconfig
  執行dir,無(wú)法執行。
  不出網(wǎng)
  想著(zhù)直接執行ps直接上線(xiàn)就好了,各種嘗試之后,后知后覺(jué)發(fā)現對方不出網(wǎng)
  寫(xiě)websgell
  發(fā)現目標不出網(wǎng)的時(shí)候,只有寫(xiě)webshell這一條路子可以走了。
  但是目標只能執行個(gè)別命令還無(wú)法解決。
  dir無(wú)法執行。陷入了沉思,加入單雙引號也不行。
  根據以前的內網(wǎng)經(jīng)驗是不是系統無(wú)法默認調用到dir.exe。
  那么 cmd /c dir是不是可以。
  驚奇的發(fā)現,可以完美的執行命令。
  通過(guò)dir,找到目錄。進(jìn)行寫(xiě)馬嘗試。
  但是發(fā)現目標路由規則寫(xiě)死了,無(wú)法直接訪(fǎng)問(wèn)到.jsp的文件。
  
  剛開(kāi)始以為是根目錄的問(wèn)題。
  發(fā)現不止根目錄,常用的css/js和img下面的也不行。
  后續在webshell中看到,翻文件看到了有類(lèi)似路由機制的驗證
  言歸正傳
  在執行rce的時(shí)候,找到了solr的目錄。發(fā)現這里的.jsp是沒(méi)有這個(gè)驗證的。
  利用命令執行進(jìn)行找到該位置,進(jìn)行寫(xiě)文件。
  問(wèn)題來(lái)了。echo 寫(xiě)入一直無(wú)法寫(xiě)入。。
  問(wèn)題解決
  把這里的特殊字符進(jìn)行特殊字符編碼,即可成功寫(xiě)入。
  又遇到一個(gè)問(wèn)題。jsp的馬子都有%號,這里不論怎么做 %就是寫(xiě)不進(jìn)去。
  差點(diǎn)放棄。找不到不帶%的馬子。
  柳暗花明
  但是想到了上午利用過(guò)的Certutil可以進(jìn)行編碼解碼
  這樣就沒(méi)有特殊字符了。
  完全沒(méi)問(wèn)題。
  剛開(kāi)始一點(diǎn)一點(diǎn)追加,發(fā)現下面的會(huì )寫(xiě)進(jìn)去兩行
  看了一下就最后有一個(gè)+號,沒(méi)有特殊字符。
  全部直接寫(xiě)進(jìn)去。
  然后decode進(jìn)行解碼,完美。
  
  訪(fǎng)問(wèn)但是是500.不過(guò)確很開(kāi)心,因為確實(shí)寫(xiě)上來(lái)。
  接下來(lái)解決為啥500就可以了。
  type 123.jsp
  查看一下。
  發(fā)現最后decode的時(shí)候,少了一個(gè)>
  本地測試,是沒(méi)有這個(gè)問(wèn)題的??赡苁悄繕艘淮涡宰址L(cháng)度的問(wèn)題。
  這里很簡(jiǎn)單了。
  追加一下就可以了。
  連接成功
  驗證
  文末福利:
  《安卓Frida逆向與協(xié)議分析》
  本書(shū)翔實(shí)地介紹流行的Frida工具在安卓逆向工程中的應用,內容包括:如何安裝和使用Frida、基本環(huán)境的搭建、Frida-tools、Frida腳本、Frida API、批量自動(dòng)化Trace和分析、RPC遠程方法調用、在無(wú)須逆向算法具體實(shí)現的情況下對Frida工具的調用,并提供了大量App逆向與協(xié)議分析案例,書(shū)中還介紹了更加穩定的Xposed框架的使用方法,以及從安卓源碼開(kāi)始定制屬于自己的抓包沙箱,打造無(wú)法被繞過(guò)的抓包環(huán)境等內容。
  本書(shū)案例豐富,注重實(shí)操,適合安卓應用安全工程師、安卓逆向分析工程師、爬蟲(chóng)工程師以及大數據采集和分析工程師使用。
  昨天文章抽獎圖片有點(diǎn)問(wèn)題,師傅們重新掃碼再抽一波把。
  福利時(shí)間:7月25日-7月31日
  2. 活動(dòng)規則:
 ?、?掃描下方二維碼參與抽獎或公眾號后臺回復安卓逆向即可參與;
 ?、?必要條件:轉發(fā)本文到朋友圈,抽獎前不可刪除;
 ?、?開(kāi)獎結束后,請中獎小伙伴及時(shí)將中獎信息和朋友圈轉發(fā)記錄發(fā)送到公眾號后臺聯(lián)系,超過(guò)24小時(shí)未領(lǐng)取的視為自動(dòng)放棄哈?。?!
  未滿(mǎn)足②條件但被抽中,則獲獎資格會(huì )被取消哦
  昨天文章抽獎圖片有點(diǎn)問(wèn)題,師傅們重新掃碼再抽一波把。 查看全部

  文章采集調用 《抽獎》某次紅藍對抗之Solr-RCE實(shí)戰繞過(guò)
  ?
  文章首發(fā)于:先知社區
  前言
  在某次紅藍對抗過(guò)程中。
  要結束的時(shí)候突然發(fā)現了掃描器爆出了
  Solr-RCE-CVE-2019-0192漏洞。
  但是進(jìn)行測試過(guò)程中,發(fā)現存在各種各樣的問(wèn)題
  繞過(guò)1
  進(jìn)行測試,發(fā)現目標只可以執行單命令,返回字段比較少的命令。
  輸入:whoami、ipconfig
  執行dir,無(wú)法執行。
  不出網(wǎng)
  想著(zhù)直接執行ps直接上線(xiàn)就好了,各種嘗試之后,后知后覺(jué)發(fā)現對方不出網(wǎng)
  寫(xiě)websgell
  發(fā)現目標不出網(wǎng)的時(shí)候,只有寫(xiě)webshell這一條路子可以走了。
  但是目標只能執行個(gè)別命令還無(wú)法解決。
  dir無(wú)法執行。陷入了沉思,加入單雙引號也不行。
  根據以前的內網(wǎng)經(jīng)驗是不是系統無(wú)法默認調用到dir.exe。
  那么 cmd /c dir是不是可以。
  驚奇的發(fā)現,可以完美的執行命令。
  通過(guò)dir,找到目錄。進(jìn)行寫(xiě)馬嘗試。
  但是發(fā)現目標路由規則寫(xiě)死了,無(wú)法直接訪(fǎng)問(wèn)到.jsp的文件。
  
  剛開(kāi)始以為是根目錄的問(wèn)題。
  發(fā)現不止根目錄,常用的css/js和img下面的也不行。
  后續在webshell中看到,翻文件看到了有類(lèi)似路由機制的驗證
  言歸正傳
  在執行rce的時(shí)候,找到了solr的目錄。發(fā)現這里的.jsp是沒(méi)有這個(gè)驗證的。
  利用命令執行進(jìn)行找到該位置,進(jìn)行寫(xiě)文件。
  問(wèn)題來(lái)了。echo 寫(xiě)入一直無(wú)法寫(xiě)入。。
  問(wèn)題解決
  把這里的特殊字符進(jìn)行特殊字符編碼,即可成功寫(xiě)入。
  又遇到一個(gè)問(wèn)題。jsp的馬子都有%號,這里不論怎么做 %就是寫(xiě)不進(jìn)去。
  差點(diǎn)放棄。找不到不帶%的馬子。
  柳暗花明
  但是想到了上午利用過(guò)的Certutil可以進(jìn)行編碼解碼
  這樣就沒(méi)有特殊字符了。
  完全沒(méi)問(wèn)題。
  剛開(kāi)始一點(diǎn)一點(diǎn)追加,發(fā)現下面的會(huì )寫(xiě)進(jìn)去兩行
  看了一下就最后有一個(gè)+號,沒(méi)有特殊字符。
  全部直接寫(xiě)進(jìn)去。
  然后decode進(jìn)行解碼,完美。
  
  訪(fǎng)問(wèn)但是是500.不過(guò)確很開(kāi)心,因為確實(shí)寫(xiě)上來(lái)。
  接下來(lái)解決為啥500就可以了。
  type 123.jsp
  查看一下。
  發(fā)現最后decode的時(shí)候,少了一個(gè)>
  本地測試,是沒(méi)有這個(gè)問(wèn)題的??赡苁悄繕艘淮涡宰址L(cháng)度的問(wèn)題。
  這里很簡(jiǎn)單了。
  追加一下就可以了。
  連接成功
  驗證
  文末福利:
  《安卓Frida逆向與協(xié)議分析》
  本書(shū)翔實(shí)地介紹流行的Frida工具在安卓逆向工程中的應用,內容包括:如何安裝和使用Frida、基本環(huán)境的搭建、Frida-tools、Frida腳本、Frida API、批量自動(dòng)化Trace和分析、RPC遠程方法調用、在無(wú)須逆向算法具體實(shí)現的情況下對Frida工具的調用,并提供了大量App逆向與協(xié)議分析案例,書(shū)中還介紹了更加穩定的Xposed框架的使用方法,以及從安卓源碼開(kāi)始定制屬于自己的抓包沙箱,打造無(wú)法被繞過(guò)的抓包環(huán)境等內容。
  本書(shū)案例豐富,注重實(shí)操,適合安卓應用安全工程師、安卓逆向分析工程師、爬蟲(chóng)工程師以及大數據采集和分析工程師使用。
  昨天文章抽獎圖片有點(diǎn)問(wèn)題,師傅們重新掃碼再抽一波把。
  福利時(shí)間:7月25日-7月31日
  2. 活動(dòng)規則:
 ?、?掃描下方二維碼參與抽獎或公眾號后臺回復安卓逆向即可參與;
 ?、?必要條件:轉發(fā)本文到朋友圈,抽獎前不可刪除;
 ?、?開(kāi)獎結束后,請中獎小伙伴及時(shí)將中獎信息和朋友圈轉發(fā)記錄發(fā)送到公眾號后臺聯(lián)系,超過(guò)24小時(shí)未領(lǐng)取的視為自動(dòng)放棄哈?。?!
  未滿(mǎn)足②條件但被抽中,則獲獎資格會(huì )被取消哦
  昨天文章抽獎圖片有點(diǎn)問(wèn)題,師傅們重新掃碼再抽一波把。

社區精選 | 搭建前端監控,采集用戶(hù)行為的 N 種姿勢

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 155 次瀏覽 ? 2022-07-27 21:31 ? 來(lái)自相關(guān)話(huà)題

  社區精選 | 搭建前端監控,采集用戶(hù)行為的 N 種姿勢
  今天為各位帶來(lái)的是社區作者楊成功的文章,在這篇文章中他為大家介紹了搭建前端監控,采集用戶(hù)行為的 N 種姿勢。
  讓我們一起來(lái)了解吧~
  大家好,我是楊成功。
  上一篇我們詳細介紹了前端如何采集異常數據。采集異常數據是為了隨時(shí)監測線(xiàn)上項目的運行情況,發(fā)現問(wèn)題及時(shí)修復。在很多場(chǎng)景下,除了異常監控有用,收集用戶(hù)的行為數據同樣有意義。
  怎么定義行為數據?顧名思義,就是用戶(hù)在使用產(chǎn)品過(guò)程中產(chǎn)生的行為軌跡。比如去過(guò)哪幾個(gè)頁(yè)面,點(diǎn)過(guò)哪幾個(gè)按鈕,甚至在某個(gè)頁(yè)面停留了多長(cháng)時(shí)間,某個(gè)按鈕點(diǎn)擊了多少次,如果有需求都可以記錄下來(lái)。
  但是記錄行為數據是一個(gè)和業(yè)務(wù)緊密關(guān)聯(lián)的事情,不可能把每個(gè)用戶(hù)每一步操作都極其詳細的記錄下來(lái),這樣會(huì )產(chǎn)生極其龐大的數據,很顯然不現實(shí)。
  合理的做法是,根據產(chǎn)品的實(shí)際情況評估,哪個(gè)模塊哪個(gè)按鈕需要重點(diǎn)記錄,則可以采集的詳細一些;哪些模塊不需要重點(diǎn)關(guān)注,則簡(jiǎn)單記錄一下基本信息。
  根據這個(gè)邏輯,我們可以把行為數據分為兩類(lèi):
  通用數據特定數據
  下面分別介紹這兩類(lèi)數據該如何收集。
  通用數據
  在一個(gè)產(chǎn)品中,用戶(hù)最基本的行為就是切換頁(yè)面。用戶(hù)使用了哪些功能,也能從切換頁(yè)面中體現出來(lái)。因此通用數據一般是在頁(yè)面切換時(shí)產(chǎn)生,表示某個(gè)用戶(hù)訪(fǎng)問(wèn)了某個(gè)頁(yè)面。
  頁(yè)面切換對應到前端就是路由切換,可以通過(guò)監聽(tīng)路由變化來(lái)拿到新頁(yè)面的數據。Vue 在全局路由守衛中監聽(tīng)路由變化,任意路由切換都能執行這里的回調函數。
  //?Vue3?路由寫(xiě)法<br />const?router?=?createRouter({?...?})<br />router.beforeEach(to?=>?{<br />??//?to?代表新頁(yè)面的路由對象<br />??recordBehaviors(to)<br />})<br />
  React 在組件的 useEffect 中實(shí)現相同的功能。不過(guò)要注意一點(diǎn),監聽(tīng)所有路由變化,則需要所有路由都經(jīng)過(guò)這個(gè)組件,監聽(tīng)才有效果。具體的方法是配置路由時(shí)加*配置:
  import?HomePage?from?'@/pages/Home'<br />,<br />
  然后在這個(gè)組件的的 useEffect 中監聽(tīng)路由變化:
  //?HomePage.jsx<br />const?{?pathname?}?=?useLocation();<br />useEffect(()?=>?{<br />??//?路由切換這個(gè)函數觸發(fā)<br />??recordBehaviors(pathname);<br />},?[pathname]);<br />
  上面代碼中,在路由切換時(shí)都調用了recordBehaviors()方法并傳入了參數。
  Vue 傳的是一個(gè)路由對象,React 傳的是路由地址,接下來(lái)就可以在這個(gè)函數內收集數據了。
  明確了在哪里收集數據,我們還要知道收集哪些數據。收集行為數據最基本的字段如下:
  上面的字段中,應用標識、環(huán)境、版本號統稱(chēng)應用字段,用于標志數據的來(lái)源。其他字段主要分為用戶(hù),頁(yè)面,時(shí)間三類(lèi),通過(guò)這三類(lèi)數據就可以簡(jiǎn)單的判斷出一件事:誰(shuí)到過(guò)哪個(gè)頁(yè)面,并停留了多長(cháng)時(shí)間。
  應用字段的配置和獲取方式我們在上一節中講過(guò),就不做多余介紹了,獲取字段的方式都是通用的。
  下面介紹其他的幾類(lèi)數據如何獲取。
  獲取用戶(hù)信息
  現代前端應用存儲用戶(hù)信息的方式基本都是一樣的,localStorage 存一份,狀態(tài)管理里存一份。因此獲取用戶(hù)信息從這兩處的任意一處獲得即可。這里簡(jiǎn)單介紹下如何從狀態(tài)管理中獲取。
  最簡(jiǎn)單的方法,在函數recordBehaviors()所處的 js 文件中,直接導入用戶(hù)狀態(tài):
  //?從狀態(tài)管理里中導出用戶(hù)數據<br />import?{?UserStore?}?from?'@/stores';<br />let?{?user_id,?user_name?}?=?UserStore;<br />
  這里的@/stores 指向我項目中的文件 src/stores/index.ts,表示狀態(tài)管理的入口文件,使用時(shí)替換成自己項目的實(shí)際位置。實(shí)際情況中還會(huì )有用戶(hù)數據為空的問(wèn)題,這里需要單獨處理一下,方便我們在后續的數據查看中能看出分別:
  import?{?UserStore?}?from?'@/stores';<br /><br />//?收集行為函數<br />const?recordBehaviors?=?()=>?{<br />??let?report_date?=?{<br />????...<br />??}<br />??if(UserStore)?{<br />????let?{?user_id,?user_name}?=?UserStore<br />????report_date.user_id?=?user_id?||?0<br />????report_date.user_name?=?user_name?||?'未命名'<br />??}?else?{<br />????report_date.user_id?=?user_id?||?-1<br />????report_date.user_name?=?user_name?||?'未獲取'<br />??}<br />}<br />
  上面代碼中,首先判斷了狀態(tài)管理中是否有用戶(hù)數據,如果有則獲取,沒(méi)有則指定默認值。這里指定默認值的細節要注意,不是隨便指定的,比如 user_id 的默認值有如下意義:
  用戶(hù)數據是經(jīng)常容易出錯的地方,因為涉及到登錄狀態(tài)和權限等復雜問(wèn)題。指定了上述默認值后,就可以從收集到的行為數據中判斷出某個(gè)頁(yè)面用戶(hù)狀態(tài)是否正常。
  
  獲取頁(yè)面信息
  前面我們在監聽(tīng)路由變化的地方調用了 recordBehaviors 函數并傳入了參數,頁(yè)面信息可以從參數中拿到,我們先看在 Vue 中怎么獲?。?br />   //?路由配置<br />{<br />??path:?'/test',<br />??meta:?{<br />????title:?'測試頁(yè)面'<br />??},<br />??component:?()?=>?import('@/views/test/Index.vue')<br />}<br /><br />//?獲取配置<br />const?recordBehaviors?=?(to)=>?{<br />??let?page_route?=?to.path<br />??let?page_title?=?to.meta.title<br />}<br />
  Vue 中比較簡(jiǎn)單,可以直接從參數中拿到頁(yè)面數據。相比之下,React 的參數只是一個(gè)路由地址,想拿到頁(yè)面名稱(chēng)還需要做單獨處理。
  一般在設計權限時(shí),我們會(huì )在服務(wù)端會(huì )維護一套路由數據,包含路由地址和名稱(chēng)。路由數據在登錄后獲取,存在狀態(tài)管理中,那么有了 pathname 就可以從路由數據中找到對應的路由名稱(chēng)。
  //?React?中<br />import?{?RouteStore?}?from?'@/stores';<br /><br />const?recordBehaviors?=?(pathname)?=>?{<br />??let?{?routers?}?=?RouteStore;?//?取出路由數據<br />??let?route?=?routers.find((row)?=>?(row.path?=?pathname));<br />??if?(route)?{<br />????let?page_route?=?route.path;<br />????let?page_title?=?route.title;<br />??}<br />};<br />
  這樣,頁(yè)面信息的 page_route、page_title 兩個(gè)字段也拿到了。
  設置時(shí)間
  行為數據中用兩個(gè)字段start_at、end_at分別表示用戶(hù)進(jìn)入頁(yè)面和離開(kāi)頁(yè)面的時(shí)間。這兩個(gè)字段非常重要,我們在后續使用數據的時(shí)候可以判斷出很多信息,比如:
  還有很多信息,都能根據這兩個(gè)時(shí)間字段判斷。開(kāi)始時(shí)間很好辦,函數觸發(fā)時(shí)直接獲取當前時(shí)間:
  <br />var?start_at?=?new?Date();<br />
  結束時(shí)間這里需要考慮的情況比較多。首先要確定數據什么時(shí)候上報?用戶(hù)進(jìn)入頁(yè)面后上報,還是離開(kāi)頁(yè)面時(shí)上報?
  如果進(jìn)入頁(yè)面時(shí)上報,可以保證行為數據一定會(huì )被記錄,不會(huì )丟失,但此時(shí) end_at 字段必然為空。這樣的話(huà),就需要在離開(kāi)頁(yè)面時(shí)再調接口,將這條記錄的 end_time 更新,這種方式的實(shí)現比較麻煩一些:
  //?進(jìn)入頁(yè)面時(shí)調用<br />const?recordBehaviors?=?()?=>?{<br />??let?report_date?=?{...}?//?此時(shí)?end_at?為空<br />??http.post('/behaviors/insert',?report_date).then(res=>?{<br />????let?id?=?res.id?//?數據?id<br />????localStorage.setItem('CURRENT_BEHAVIOR_ID',?id)<br />??})<br />}<br /><br />//?離開(kāi)頁(yè)面時(shí)調用:<br />const?updateBehaviors?=?()=>?{<br />??let?id?=?localStorage.getItem('CURRENT_BEHAVIOR_ID')<br />??let?end_at?=?new?Date()<br />??http.post('/behaviors/update/'+id,?end_at)?//?根據?id?更新結束時(shí)間<br />??localStorage.removeItem('CURRENT_BEHAVIOR_ID')<br />}<br />
  上面代碼中,進(jìn)入頁(yè)面先上報數據,并保存下 id,離開(kāi)頁(yè)面再根據 id 更新這條數據的結束時(shí)間。
  如果在離開(kāi)頁(yè)面時(shí)上報,那么就要保證離開(kāi)頁(yè)面前上報接口已經(jīng)觸發(fā),否則會(huì )導致數據丟失。在滿(mǎn)足這個(gè)前提條件下,上報邏輯會(huì )變成這樣:
  //?進(jìn)入頁(yè)面時(shí)調用<br />const?recordBehaviors?=?()?=>?{<br />??let?report_date?=?{...}?//?此時(shí)?end_at?為空<br />??localStorage.setItem('CURRENT_BEHAVIOR',?JSON.stringify(report_date));<br />}<br /><br />//?離開(kāi)頁(yè)面時(shí)調用<br />const?reportBehaviors?=?()?=>?{<br />??let?end_at?=?new?Date()<br />??let?report_str?=?localStorage.getItem('CURRENT_BEHAVIOR')<br />??if(report_str)?{<br />????let?report_date?=?JSON.parse(report_str)<br />????report_date.end_at?=?end_at<br />????http.post('/behaviors/insert',?report_date)<br />??}?else?{<br />????console.log('無(wú)行為數據')<br />??}<br />}<br />
  對比一下這兩種方案,第一種的弊端是接口需要調兩次,這會(huì )使接口請求量倍增。第二種方案只調用一次,但是需要特別注意可靠性處理,總體來(lái)說(shuō)第二種方案更好些。
  特定數據
  除了通用數據,大部分情況我們還要在具體的頁(yè)面中收集某些特定的行為。比如某個(gè)關(guān)鍵的按鈕有沒(méi)有點(diǎn)擊,點(diǎn)了多少次;或者某個(gè)關(guān)鍵區域用戶(hù)有沒(méi)有看到,看到(曝光)了多少次等等。
  收集數據還有一個(gè)更專(zhuān)業(yè)的叫法 ———— 埋點(diǎn)。直觀(guān)理解是,哪里需要上報數據,就埋一個(gè)上報函數進(jìn)去。
  通用數據針對所有頁(yè)面自動(dòng)收集,特定數據就需要根據每個(gè)頁(yè)面的實(shí)際需求手動(dòng)添加。以一個(gè)按鈕為例:
  點(diǎn)擊;<br />const?onClick?=?(e)?=>?{<br />??//?console.log(e);<br />??repoerEvents(e);<br />};<br />
  上面代碼中,我們想記錄這個(gè)按鈕的點(diǎn)擊情況,所以做了一個(gè)簡(jiǎn)單的埋點(diǎn) ———— 在按鈕點(diǎn)擊事件中調用 repoerEvents()方法,這個(gè)方法內部會(huì )收集數據并上報。
  這是最原始的埋點(diǎn)方式,直接將上報方法放到事件函數中。repoerEvents() 方法接收一個(gè)事件對象參數,在參數中獲取需要上報的事件數據。
  特定數據與通用數據的許多字段是一樣的,收集特定數據需要的基本字段如下:
  這些基本字段中,前 7 個(gè)字段與前面通用數據的獲取完全一樣,這里就不贅述了。實(shí)際上特定數據需要獲取的專(zhuān)有字段只有 3 個(gè):
  這三個(gè)字段也非常容易獲取。event_type 表示事件觸發(fā)的類(lèi)型,比如點(diǎn)擊、滾動(dòng)、拖動(dòng)等,可以在事件對象中拿到。action_tag 和 action_label 是必須指定的屬性,表示本次埋點(diǎn)的標識和文字描述,用于在后續的數據處理時(shí)方便查閱和統計。
  了解了采集特定數據是怎么回事,接下來(lái)我們用代碼實(shí)現。
  手動(dòng)埋點(diǎn)上報
  假設要為登錄按鈕做埋點(diǎn),按照上面的數據采集方式,我們書(shū)寫(xiě)代碼如下:
  <br />??登錄<br />;<br />const?onClick?=?(e)?=>?{<br />??//?console.log(e);<br />??repoerEvents(e);<br />};<br />
  代碼中,我們通過(guò)元素的自定義屬性傳遞了 tag 和 label 兩個(gè)標識,用于在上報函數中獲取。
  上報函數 repoerEvents() 代碼邏輯如下:
  
  //?埋點(diǎn)上報函數<br />const?repoerEvents?=?(e)=>?{<br />??let?report_date?=?{...}<br />??let?{?tag,?label?}?=?e.target.dataset<br />??if(!tag?||?!label)?{<br />????return?new?Error('上報元素屬性缺失')<br />??}<br />??report_date.event_type?=?e.type<br />??report_date.action_tag?=?tag<br />??report_date.action_label?=?label<br /><br />??//?上報數據<br />??http.post('/events/insert',?report_date)<br />}<br />
  這樣就實(shí)現了一個(gè)基本的特定數據埋點(diǎn)上報功能。
  全局自動(dòng)上報
  現在我們回過(guò)頭來(lái)梳理一下這個(gè)上報流程,雖然基本功能實(shí)現了,但是還有些不合理之處,比如:
  首先我們的埋點(diǎn)方式是基于事件的,也就是說(shuō),不管元素本身是否需要事件處理,我們都要給他加上,并在函數內部調用 repoerEvents() 方法。如果一個(gè)項目需要埋點(diǎn)的地方非常多,這種方式的接入成本就會(huì )非常高。
  參考之前做異常監控的邏輯,我們換一個(gè)思路:能否全局監聽(tīng)事件自動(dòng)上報呢?
  思考一下,如果要做全局監聽(tīng)事件,那么只能監聽(tīng)需要埋點(diǎn)的元素的事件。那么如何判斷哪些元素需要埋點(diǎn)呢?
  上面我們?yōu)槁顸c(diǎn)的元素指定了data-tag和data-label兩個(gè)自定義屬性,那是不是根據這兩個(gè)自定義屬性判斷就可以?我們來(lái)試驗一下:
  window.addEventListener('click',?(event)?=>?{<br />??let?{?tag,?label,?trigger?}?=?event.target.dataset;<br />??if?(tag?&&?label?&&?trigger?==?'click')?{<br />????//?說(shuō)明該元素需要埋點(diǎn)<br />????repoerEvents(event);<br />??}<br />});<br />
  上面代碼還多判斷了一個(gè)自定義屬性 dataset.trigger,表示元素在哪種事件觸發(fā)時(shí)需要上報。全局監聽(tīng)事件需要這個(gè)標識,這樣可避免事件沖突。
  添加全局監聽(tīng)后,收集某個(gè)元素的特定數據就簡(jiǎn)單了,方法如下:
  <br />??保存<br /><br />
  試驗證明,上述全局處理的方式是可行的,這樣的話(huà)就不需要在每一個(gè)元素上添加或修改事件處理函數了,只需要在元素中添加三個(gè)自定義屬性 data-tag,data-label,data-trigger 就能自動(dòng)實(shí)現數據埋點(diǎn)上報。
  組件上報
  上面全局監聽(tīng)事件上報的方式已經(jīng)比手動(dòng)埋點(diǎn)高效了許多,現在我們再換一個(gè)場(chǎng)景。
  一般情況下當埋點(diǎn)功能成熟之后,會(huì )封裝成一個(gè) SDK 供其他項目使用。如果我們將采集數據按照 SDK 的思路實(shí)現,讓開(kāi)發(fā)者在全局監聽(tīng)事件,是不是一個(gè)好的方式呢?
  顯然是不太友好的。如果是一個(gè) SDK,那么最好的方式是將所有內容聚合成一個(gè)組件,在組件內實(shí)現上報的所有功能,而不是讓使用者在項目中添加監聽(tīng)事件。
  封裝組件的話(huà),那么組件的功能最好是將要添加埋點(diǎn)的元素包裹,這樣自定義元素也就不需要指定了,而轉為組件的屬性,然后在組件內實(shí)現事件監聽(tīng)。
  以 React 為例,我們看一下如何將上面的采集功能封裝為組件:
  import?{?useEffect,?useRef?}?from?'react';<br /><br />const?CusReport?=?(props)?=>?{<br />??const?dom?=?useRef(null);<br />??const?handelEvent?=?()?=>?{<br />????console.log(props);?//?{tag:xx,?label:xx,?trigger:xx}<br />????repoerEvents(props);<br />??};<br />??useEffect(()?=>?{<br />????if?(dom.current?instanceof?HTMLElement)?{<br />??????dom.current.addEventListener(props.trigger,?handelEvent);<br />????}<br />??},?[]);<br />??return?(<br />????<br />??????{props.children}<br />????<br />??);<br />};<br /><br />export?default?CusReport;<br />
  組件使用方式如下:
  <br />??測試<br /><br />
  這樣就比較優(yōu)雅了,不需要修改目標元素,只要把組件包裹在目標元素之外即可。
  總結
  本文介紹了搭建前端監控如何采集行為數據,將數據分為通用數據和特定數據兩個(gè)大類(lèi)分別處理。同時(shí)也介紹了多種上報數據的方式,不同的場(chǎng)景可以選擇不同的方式。
  其中的數據部分只介紹了實(shí)現功能的基礎字段,實(shí)際情況中可以根據自己的業(yè)務(wù)需求添加。
  許多小伙伴留言這套前端監控能否開(kāi)源,肯定是要開(kāi)源的,不過(guò)內容比較多我還在做,等到基本完善了我會(huì )發(fā)一個(gè)版本,感謝小伙伴們的關(guān)注。
  SegmentFault 思否社區小編說(shuō)
  自 2022-07-01 起 SegmentFault 思否公眾號改版啦!之后將陸續推出新的欄目和大家見(jiàn)面?。ㄕ埵媚恳源絶?)
  在「社區精選」欄目中,我們將為廣大開(kāi)發(fā)者推薦來(lái)自 SegmentFault 思否開(kāi)發(fā)者社區的優(yōu)質(zhì)技術(shù)文章,這些文章全部出自社區中充滿(mǎn)智慧的技術(shù)創(chuàng )作者哦!
  希望通過(guò)這一欄目,大家可以共同學(xué)習技術(shù)干貨,GET 新技能和各種花式技術(shù)小 Tips。
  歡迎越來(lái)越多的開(kāi)發(fā)者加入創(chuàng )作者的行列,我們將持續甄選出社區中優(yōu)質(zhì)的內容推介給更多人,讓閃閃發(fā)光的技術(shù)創(chuàng )作者們走到聚光燈下,被更多人認識。
  「社區精選」投稿郵箱:
  投稿請附上社區文章地址 查看全部

  社區精選 | 搭建前端監控,采集用戶(hù)行為的 N 種姿勢
  今天為各位帶來(lái)的是社區作者楊成功的文章,在這篇文章中他為大家介紹了搭建前端監控,采集用戶(hù)行為的 N 種姿勢。
  讓我們一起來(lái)了解吧~
  大家好,我是楊成功。
  上一篇我們詳細介紹了前端如何采集異常數據。采集異常數據是為了隨時(shí)監測線(xiàn)上項目的運行情況,發(fā)現問(wèn)題及時(shí)修復。在很多場(chǎng)景下,除了異常監控有用,收集用戶(hù)的行為數據同樣有意義。
  怎么定義行為數據?顧名思義,就是用戶(hù)在使用產(chǎn)品過(guò)程中產(chǎn)生的行為軌跡。比如去過(guò)哪幾個(gè)頁(yè)面,點(diǎn)過(guò)哪幾個(gè)按鈕,甚至在某個(gè)頁(yè)面停留了多長(cháng)時(shí)間,某個(gè)按鈕點(diǎn)擊了多少次,如果有需求都可以記錄下來(lái)。
  但是記錄行為數據是一個(gè)和業(yè)務(wù)緊密關(guān)聯(lián)的事情,不可能把每個(gè)用戶(hù)每一步操作都極其詳細的記錄下來(lái),這樣會(huì )產(chǎn)生極其龐大的數據,很顯然不現實(shí)。
  合理的做法是,根據產(chǎn)品的實(shí)際情況評估,哪個(gè)模塊哪個(gè)按鈕需要重點(diǎn)記錄,則可以采集的詳細一些;哪些模塊不需要重點(diǎn)關(guān)注,則簡(jiǎn)單記錄一下基本信息。
  根據這個(gè)邏輯,我們可以把行為數據分為兩類(lèi):
  通用數據特定數據
  下面分別介紹這兩類(lèi)數據該如何收集。
  通用數據
  在一個(gè)產(chǎn)品中,用戶(hù)最基本的行為就是切換頁(yè)面。用戶(hù)使用了哪些功能,也能從切換頁(yè)面中體現出來(lái)。因此通用數據一般是在頁(yè)面切換時(shí)產(chǎn)生,表示某個(gè)用戶(hù)訪(fǎng)問(wèn)了某個(gè)頁(yè)面。
  頁(yè)面切換對應到前端就是路由切換,可以通過(guò)監聽(tīng)路由變化來(lái)拿到新頁(yè)面的數據。Vue 在全局路由守衛中監聽(tīng)路由變化,任意路由切換都能執行這里的回調函數。
  //?Vue3?路由寫(xiě)法<br />const?router?=?createRouter({?...?})<br />router.beforeEach(to?=>?{<br />??//?to?代表新頁(yè)面的路由對象<br />??recordBehaviors(to)<br />})<br />
  React 在組件的 useEffect 中實(shí)現相同的功能。不過(guò)要注意一點(diǎn),監聽(tīng)所有路由變化,則需要所有路由都經(jīng)過(guò)這個(gè)組件,監聽(tīng)才有效果。具體的方法是配置路由時(shí)加*配置:
  import?HomePage?from?'@/pages/Home'<br />,<br />
  然后在這個(gè)組件的的 useEffect 中監聽(tīng)路由變化:
  //?HomePage.jsx<br />const?{?pathname?}?=?useLocation();<br />useEffect(()?=>?{<br />??//?路由切換這個(gè)函數觸發(fā)<br />??recordBehaviors(pathname);<br />},?[pathname]);<br />
  上面代碼中,在路由切換時(shí)都調用了recordBehaviors()方法并傳入了參數。
  Vue 傳的是一個(gè)路由對象,React 傳的是路由地址,接下來(lái)就可以在這個(gè)函數內收集數據了。
  明確了在哪里收集數據,我們還要知道收集哪些數據。收集行為數據最基本的字段如下:
  上面的字段中,應用標識、環(huán)境、版本號統稱(chēng)應用字段,用于標志數據的來(lái)源。其他字段主要分為用戶(hù),頁(yè)面,時(shí)間三類(lèi),通過(guò)這三類(lèi)數據就可以簡(jiǎn)單的判斷出一件事:誰(shuí)到過(guò)哪個(gè)頁(yè)面,并停留了多長(cháng)時(shí)間。
  應用字段的配置和獲取方式我們在上一節中講過(guò),就不做多余介紹了,獲取字段的方式都是通用的。
  下面介紹其他的幾類(lèi)數據如何獲取。
  獲取用戶(hù)信息
  現代前端應用存儲用戶(hù)信息的方式基本都是一樣的,localStorage 存一份,狀態(tài)管理里存一份。因此獲取用戶(hù)信息從這兩處的任意一處獲得即可。這里簡(jiǎn)單介紹下如何從狀態(tài)管理中獲取。
  最簡(jiǎn)單的方法,在函數recordBehaviors()所處的 js 文件中,直接導入用戶(hù)狀態(tài):
  //?從狀態(tài)管理里中導出用戶(hù)數據<br />import?{?UserStore?}?from?'@/stores';<br />let?{?user_id,?user_name?}?=?UserStore;<br />
  這里的@/stores 指向我項目中的文件 src/stores/index.ts,表示狀態(tài)管理的入口文件,使用時(shí)替換成自己項目的實(shí)際位置。實(shí)際情況中還會(huì )有用戶(hù)數據為空的問(wèn)題,這里需要單獨處理一下,方便我們在后續的數據查看中能看出分別:
  import?{?UserStore?}?from?'@/stores';<br /><br />//?收集行為函數<br />const?recordBehaviors?=?()=>?{<br />??let?report_date?=?{<br />????...<br />??}<br />??if(UserStore)?{<br />????let?{?user_id,?user_name}?=?UserStore<br />????report_date.user_id?=?user_id?||?0<br />????report_date.user_name?=?user_name?||?'未命名'<br />??}?else?{<br />????report_date.user_id?=?user_id?||?-1<br />????report_date.user_name?=?user_name?||?'未獲取'<br />??}<br />}<br />
  上面代碼中,首先判斷了狀態(tài)管理中是否有用戶(hù)數據,如果有則獲取,沒(méi)有則指定默認值。這里指定默認值的細節要注意,不是隨便指定的,比如 user_id 的默認值有如下意義:
  用戶(hù)數據是經(jīng)常容易出錯的地方,因為涉及到登錄狀態(tài)和權限等復雜問(wèn)題。指定了上述默認值后,就可以從收集到的行為數據中判斷出某個(gè)頁(yè)面用戶(hù)狀態(tài)是否正常。
  
  獲取頁(yè)面信息
  前面我們在監聽(tīng)路由變化的地方調用了 recordBehaviors 函數并傳入了參數,頁(yè)面信息可以從參數中拿到,我們先看在 Vue 中怎么獲?。?br />   //?路由配置<br />{<br />??path:?'/test',<br />??meta:?{<br />????title:?'測試頁(yè)面'<br />??},<br />??component:?()?=>?import('@/views/test/Index.vue')<br />}<br /><br />//?獲取配置<br />const?recordBehaviors?=?(to)=>?{<br />??let?page_route?=?to.path<br />??let?page_title?=?to.meta.title<br />}<br />
  Vue 中比較簡(jiǎn)單,可以直接從參數中拿到頁(yè)面數據。相比之下,React 的參數只是一個(gè)路由地址,想拿到頁(yè)面名稱(chēng)還需要做單獨處理。
  一般在設計權限時(shí),我們會(huì )在服務(wù)端會(huì )維護一套路由數據,包含路由地址和名稱(chēng)。路由數據在登錄后獲取,存在狀態(tài)管理中,那么有了 pathname 就可以從路由數據中找到對應的路由名稱(chēng)。
  //?React?中<br />import?{?RouteStore?}?from?'@/stores';<br /><br />const?recordBehaviors?=?(pathname)?=>?{<br />??let?{?routers?}?=?RouteStore;?//?取出路由數據<br />??let?route?=?routers.find((row)?=>?(row.path?=?pathname));<br />??if?(route)?{<br />????let?page_route?=?route.path;<br />????let?page_title?=?route.title;<br />??}<br />};<br />
  這樣,頁(yè)面信息的 page_route、page_title 兩個(gè)字段也拿到了。
  設置時(shí)間
  行為數據中用兩個(gè)字段start_at、end_at分別表示用戶(hù)進(jìn)入頁(yè)面和離開(kāi)頁(yè)面的時(shí)間。這兩個(gè)字段非常重要,我們在后續使用數據的時(shí)候可以判斷出很多信息,比如:
  還有很多信息,都能根據這兩個(gè)時(shí)間字段判斷。開(kāi)始時(shí)間很好辦,函數觸發(fā)時(shí)直接獲取當前時(shí)間:
  <br />var?start_at?=?new?Date();<br />
  結束時(shí)間這里需要考慮的情況比較多。首先要確定數據什么時(shí)候上報?用戶(hù)進(jìn)入頁(yè)面后上報,還是離開(kāi)頁(yè)面時(shí)上報?
  如果進(jìn)入頁(yè)面時(shí)上報,可以保證行為數據一定會(huì )被記錄,不會(huì )丟失,但此時(shí) end_at 字段必然為空。這樣的話(huà),就需要在離開(kāi)頁(yè)面時(shí)再調接口,將這條記錄的 end_time 更新,這種方式的實(shí)現比較麻煩一些:
  //?進(jìn)入頁(yè)面時(shí)調用<br />const?recordBehaviors?=?()?=>?{<br />??let?report_date?=?{...}?//?此時(shí)?end_at?為空<br />??http.post('/behaviors/insert',?report_date).then(res=>?{<br />????let?id?=?res.id?//?數據?id<br />????localStorage.setItem('CURRENT_BEHAVIOR_ID',?id)<br />??})<br />}<br /><br />//?離開(kāi)頁(yè)面時(shí)調用:<br />const?updateBehaviors?=?()=>?{<br />??let?id?=?localStorage.getItem('CURRENT_BEHAVIOR_ID')<br />??let?end_at?=?new?Date()<br />??http.post('/behaviors/update/'+id,?end_at)?//?根據?id?更新結束時(shí)間<br />??localStorage.removeItem('CURRENT_BEHAVIOR_ID')<br />}<br />
  上面代碼中,進(jìn)入頁(yè)面先上報數據,并保存下 id,離開(kāi)頁(yè)面再根據 id 更新這條數據的結束時(shí)間。
  如果在離開(kāi)頁(yè)面時(shí)上報,那么就要保證離開(kāi)頁(yè)面前上報接口已經(jīng)觸發(fā),否則會(huì )導致數據丟失。在滿(mǎn)足這個(gè)前提條件下,上報邏輯會(huì )變成這樣:
  //?進(jìn)入頁(yè)面時(shí)調用<br />const?recordBehaviors?=?()?=>?{<br />??let?report_date?=?{...}?//?此時(shí)?end_at?為空<br />??localStorage.setItem('CURRENT_BEHAVIOR',?JSON.stringify(report_date));<br />}<br /><br />//?離開(kāi)頁(yè)面時(shí)調用<br />const?reportBehaviors?=?()?=>?{<br />??let?end_at?=?new?Date()<br />??let?report_str?=?localStorage.getItem('CURRENT_BEHAVIOR')<br />??if(report_str)?{<br />????let?report_date?=?JSON.parse(report_str)<br />????report_date.end_at?=?end_at<br />????http.post('/behaviors/insert',?report_date)<br />??}?else?{<br />????console.log('無(wú)行為數據')<br />??}<br />}<br />
  對比一下這兩種方案,第一種的弊端是接口需要調兩次,這會(huì )使接口請求量倍增。第二種方案只調用一次,但是需要特別注意可靠性處理,總體來(lái)說(shuō)第二種方案更好些。
  特定數據
  除了通用數據,大部分情況我們還要在具體的頁(yè)面中收集某些特定的行為。比如某個(gè)關(guān)鍵的按鈕有沒(méi)有點(diǎn)擊,點(diǎn)了多少次;或者某個(gè)關(guān)鍵區域用戶(hù)有沒(méi)有看到,看到(曝光)了多少次等等。
  收集數據還有一個(gè)更專(zhuān)業(yè)的叫法 ———— 埋點(diǎn)。直觀(guān)理解是,哪里需要上報數據,就埋一個(gè)上報函數進(jìn)去。
  通用數據針對所有頁(yè)面自動(dòng)收集,特定數據就需要根據每個(gè)頁(yè)面的實(shí)際需求手動(dòng)添加。以一個(gè)按鈕為例:
  點(diǎn)擊;<br />const?onClick?=?(e)?=>?{<br />??//?console.log(e);<br />??repoerEvents(e);<br />};<br />
  上面代碼中,我們想記錄這個(gè)按鈕的點(diǎn)擊情況,所以做了一個(gè)簡(jiǎn)單的埋點(diǎn) ———— 在按鈕點(diǎn)擊事件中調用 repoerEvents()方法,這個(gè)方法內部會(huì )收集數據并上報。
  這是最原始的埋點(diǎn)方式,直接將上報方法放到事件函數中。repoerEvents() 方法接收一個(gè)事件對象參數,在參數中獲取需要上報的事件數據。
  特定數據與通用數據的許多字段是一樣的,收集特定數據需要的基本字段如下:
  這些基本字段中,前 7 個(gè)字段與前面通用數據的獲取完全一樣,這里就不贅述了。實(shí)際上特定數據需要獲取的專(zhuān)有字段只有 3 個(gè):
  這三個(gè)字段也非常容易獲取。event_type 表示事件觸發(fā)的類(lèi)型,比如點(diǎn)擊、滾動(dòng)、拖動(dòng)等,可以在事件對象中拿到。action_tag 和 action_label 是必須指定的屬性,表示本次埋點(diǎn)的標識和文字描述,用于在后續的數據處理時(shí)方便查閱和統計。
  了解了采集特定數據是怎么回事,接下來(lái)我們用代碼實(shí)現。
  手動(dòng)埋點(diǎn)上報
  假設要為登錄按鈕做埋點(diǎn),按照上面的數據采集方式,我們書(shū)寫(xiě)代碼如下:
  <br />??登錄<br />;<br />const?onClick?=?(e)?=>?{<br />??//?console.log(e);<br />??repoerEvents(e);<br />};<br />
  代碼中,我們通過(guò)元素的自定義屬性傳遞了 tag 和 label 兩個(gè)標識,用于在上報函數中獲取。
  上報函數 repoerEvents() 代碼邏輯如下:
  
  //?埋點(diǎn)上報函數<br />const?repoerEvents?=?(e)=>?{<br />??let?report_date?=?{...}<br />??let?{?tag,?label?}?=?e.target.dataset<br />??if(!tag?||?!label)?{<br />????return?new?Error('上報元素屬性缺失')<br />??}<br />??report_date.event_type?=?e.type<br />??report_date.action_tag?=?tag<br />??report_date.action_label?=?label<br /><br />??//?上報數據<br />??http.post('/events/insert',?report_date)<br />}<br />
  這樣就實(shí)現了一個(gè)基本的特定數據埋點(diǎn)上報功能。
  全局自動(dòng)上報
  現在我們回過(guò)頭來(lái)梳理一下這個(gè)上報流程,雖然基本功能實(shí)現了,但是還有些不合理之處,比如:
  首先我們的埋點(diǎn)方式是基于事件的,也就是說(shuō),不管元素本身是否需要事件處理,我們都要給他加上,并在函數內部調用 repoerEvents() 方法。如果一個(gè)項目需要埋點(diǎn)的地方非常多,這種方式的接入成本就會(huì )非常高。
  參考之前做異常監控的邏輯,我們換一個(gè)思路:能否全局監聽(tīng)事件自動(dòng)上報呢?
  思考一下,如果要做全局監聽(tīng)事件,那么只能監聽(tīng)需要埋點(diǎn)的元素的事件。那么如何判斷哪些元素需要埋點(diǎn)呢?
  上面我們?yōu)槁顸c(diǎn)的元素指定了data-tag和data-label兩個(gè)自定義屬性,那是不是根據這兩個(gè)自定義屬性判斷就可以?我們來(lái)試驗一下:
  window.addEventListener('click',?(event)?=>?{<br />??let?{?tag,?label,?trigger?}?=?event.target.dataset;<br />??if?(tag?&&?label?&&?trigger?==?'click')?{<br />????//?說(shuō)明該元素需要埋點(diǎn)<br />????repoerEvents(event);<br />??}<br />});<br />
  上面代碼還多判斷了一個(gè)自定義屬性 dataset.trigger,表示元素在哪種事件觸發(fā)時(shí)需要上報。全局監聽(tīng)事件需要這個(gè)標識,這樣可避免事件沖突。
  添加全局監聽(tīng)后,收集某個(gè)元素的特定數據就簡(jiǎn)單了,方法如下:
  <br />??保存<br /><br />
  試驗證明,上述全局處理的方式是可行的,這樣的話(huà)就不需要在每一個(gè)元素上添加或修改事件處理函數了,只需要在元素中添加三個(gè)自定義屬性 data-tag,data-label,data-trigger 就能自動(dòng)實(shí)現數據埋點(diǎn)上報。
  組件上報
  上面全局監聽(tīng)事件上報的方式已經(jīng)比手動(dòng)埋點(diǎn)高效了許多,現在我們再換一個(gè)場(chǎng)景。
  一般情況下當埋點(diǎn)功能成熟之后,會(huì )封裝成一個(gè) SDK 供其他項目使用。如果我們將采集數據按照 SDK 的思路實(shí)現,讓開(kāi)發(fā)者在全局監聽(tīng)事件,是不是一個(gè)好的方式呢?
  顯然是不太友好的。如果是一個(gè) SDK,那么最好的方式是將所有內容聚合成一個(gè)組件,在組件內實(shí)現上報的所有功能,而不是讓使用者在項目中添加監聽(tīng)事件。
  封裝組件的話(huà),那么組件的功能最好是將要添加埋點(diǎn)的元素包裹,這樣自定義元素也就不需要指定了,而轉為組件的屬性,然后在組件內實(shí)現事件監聽(tīng)。
  以 React 為例,我們看一下如何將上面的采集功能封裝為組件:
  import?{?useEffect,?useRef?}?from?'react';<br /><br />const?CusReport?=?(props)?=>?{<br />??const?dom?=?useRef(null);<br />??const?handelEvent?=?()?=>?{<br />????console.log(props);?//?{tag:xx,?label:xx,?trigger:xx}<br />????repoerEvents(props);<br />??};<br />??useEffect(()?=>?{<br />????if?(dom.current?instanceof?HTMLElement)?{<br />??????dom.current.addEventListener(props.trigger,?handelEvent);<br />????}<br />??},?[]);<br />??return?(<br />????<br />??????{props.children}<br />????<br />??);<br />};<br /><br />export?default?CusReport;<br />
  組件使用方式如下:
  <br />??測試<br /><br />
  這樣就比較優(yōu)雅了,不需要修改目標元素,只要把組件包裹在目標元素之外即可。
  總結
  本文介紹了搭建前端監控如何采集行為數據,將數據分為通用數據和特定數據兩個(gè)大類(lèi)分別處理。同時(shí)也介紹了多種上報數據的方式,不同的場(chǎng)景可以選擇不同的方式。
  其中的數據部分只介紹了實(shí)現功能的基礎字段,實(shí)際情況中可以根據自己的業(yè)務(wù)需求添加。
  許多小伙伴留言這套前端監控能否開(kāi)源,肯定是要開(kāi)源的,不過(guò)內容比較多我還在做,等到基本完善了我會(huì )發(fā)一個(gè)版本,感謝小伙伴們的關(guān)注。
  SegmentFault 思否社區小編說(shuō)
  自 2022-07-01 起 SegmentFault 思否公眾號改版啦!之后將陸續推出新的欄目和大家見(jiàn)面?。ㄕ埵媚恳源絶?)
  在「社區精選」欄目中,我們將為廣大開(kāi)發(fā)者推薦來(lái)自 SegmentFault 思否開(kāi)發(fā)者社區的優(yōu)質(zhì)技術(shù)文章,這些文章全部出自社區中充滿(mǎn)智慧的技術(shù)創(chuàng )作者哦!
  希望通過(guò)這一欄目,大家可以共同學(xué)習技術(shù)干貨,GET 新技能和各種花式技術(shù)小 Tips。
  歡迎越來(lái)越多的開(kāi)發(fā)者加入創(chuàng )作者的行列,我們將持續甄選出社區中優(yōu)質(zhì)的內容推介給更多人,讓閃閃發(fā)光的技術(shù)創(chuàng )作者們走到聚光燈下,被更多人認識。
  「社區精選」投稿郵箱:
  投稿請附上社區文章地址

實(shí)戰 | SRC信息收集思路總結

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 100 次瀏覽 ? 2022-06-18 20:11 ? 來(lái)自相關(guān)話(huà)題

  實(shí)戰 | SRC信息收集思路總結
  說(shuō)到信息收集,網(wǎng)上已經(jīng)有許多文章進(jìn)行描述了,那么從正常的子域名、端口、旁站、C段等進(jìn)行信息收集的話(huà),對于正常項目已經(jīng)夠用了,但是挖掘SRC的話(huà),在諸多競爭對手的“幫助”下,大家收集到的信息都差不多,挖掘的漏洞也往往存在重復的情況。
  那么現在我就想分享一下平時(shí)自己進(jìn)行SRC挖掘過(guò)程中,主要是如何進(jìn)行入手的。以下均為小弟拙見(jiàn),大佬勿噴。
  0x01 確定目標
  無(wú)目標隨便打,有沒(méi)有自己對應的SRC應急響應平臺不說(shuō),還往往會(huì )因為一開(kāi)始沒(méi)有挖掘到漏洞而隨意放棄,這樣往往不能挖掘到深層次的漏洞。挖到的大多數是大家都可以簡(jiǎn)單挖到的漏洞,存在大概率重復可能。所以在真的想要花點(diǎn)時(shí)間在SRC漏洞挖掘上的話(huà),建議先選好目標。
  那么目標怎么選呢,考慮到收益回報與付出的比例來(lái)看,建議是從專(zhuān)屬SRC入手,特別在一些活動(dòng)中,可以獲取比平時(shí)更高的收益。
  微信搜一搜:
  
  百度搜一搜:
  現在有活動(dòng)的src已經(jīng)浮現水面了,那么我們就可與從中選擇自己感興趣的SRC。
  0x02 確認測試范圍
  前面說(shuō)到確定測什么SRC,那么下面就要通過(guò)一些方法,獲取這個(gè)SRC的測試范圍,以免測偏。
  1、公眾號
  從公眾號推文入手,活動(dòng)頁(yè)面中可以發(fā)現測試范圍
  2、應急響應官網(wǎng)
  在應急響應官網(wǎng),往往會(huì )有一些活動(dòng)的公告,在里面可以獲取到相應的測試范圍。
  3、愛(ài)企查
  從愛(ài)企查等商業(yè)查詢(xún)平臺獲取公司所屬域名
  
  搜索想要測試等SRC所屬公司名稱(chēng),在知識產(chǎn)權->網(wǎng)站備案中可以獲取測試范圍。
  0x03 子域名(oneforall)
  拿到域名之后,下一步我考慮使用oneforall掃描獲取子域名,就像網(wǎng)上信息收集的文章一樣,主域名的站點(diǎn)不是靜態(tài)界面就是安全防護等級極強,不是隨便就能夠發(fā)現漏洞的,我們挖掘SRC也是要從子域名開(kāi)始,從邊緣資產(chǎn)或一般資產(chǎn)中發(fā)現漏洞。
  工具下載:
  https://github.com/shmilylty/OneForAll
  具體用法如下:
  常用的獲取子域名有2種選擇,一種使用--target指定單個(gè)域名,一種使用--targets指定域名文件。
  python3 oneforall.py --target example.com run<br />python3 oneforall.py --targets ./domains.txt run
  其他獲取子域名的工具還有layer子域名挖掘機、Sublist3r、證書(shū)透明度、在線(xiàn)工具等,這里就不一一闡述了,大體思路是一樣等,獲取子域,然后從中篩選邊緣資產(chǎn),安全防護低資產(chǎn)。
  0x04 系統指紋探測
  通過(guò)上面的方法,我們可以在/OneForAll-0.4.3/results/路徑下獲取以域名為名字的csv文件。里面放入到便是掃描到到所有子域名以及相應信息了。
  下一步便是將收集到到域名全部進(jìn)行一遍指紋探測,從中找出一些明顯使用CMS、OA系統、shiro、Fastjson等的站點(diǎn)。下面介紹平時(shí)使用的2款工具:
  1、Ehole
  下載地址:
  https://github.com/EdgeSecurityTeam/EHole
  使用方法:
  ./Ehole-darwin -l url.txt //URL地址需帶上協(xié)議,每行一個(gè)<br />./Ehole-darwin -f 192.168.1.1/24 //支持單IP或IP段,fofa識別需要配置fofa密鑰和郵箱<br />./Ehole-darwin -l url.txt -json export.json //結果輸出至export.json文件
  2、Glass
  下載地址:
  https://github.com/s7ckTeam/Glass
  使用方法:
  python3 Glass.py -u http://www.examples.com // 單url測試<br />python3 Glass.py -w domain.txt -o 1.txt // url文件內
  0x05 框架型站點(diǎn)漏洞測試
  前面經(jīng)過(guò)了子域名收集以及對收集到的子域名進(jìn)行了指紋信息識別之后,那么對于框架型的站點(diǎn),我們可以?xún)?yōu)先進(jìn)行測試。
  類(lèi)似用友NC、通達OA、藍凌OA等,可以通過(guò)嘗試現有的Nday漏洞進(jìn)行攻擊。
  
  0x06 非框架型站點(diǎn)漏洞測試
  前面測試完框架型的站點(diǎn)了,之后就應該往正常網(wǎng)站,或者經(jīng)過(guò)了二開(kāi)未能直接檢測出指紋的站點(diǎn)進(jìn)行滲透了。那么對于這類(lèi)站點(diǎn),最經(jīng)常遇到的便是登錄框,在這里,我們便可以開(kāi)始測試了。
  
  1、用戶(hù)名枚舉
  抓包嘗試是否用戶(hù)名存在與不存在的情況,返回結果不同。
  2、驗證碼
  是否存在驗證碼,驗證碼是否可以抓包截斷繞過(guò),驗證碼是否可以為空。
  3、暴力破解
  下面是我收集的集中常見(jiàn)的用戶(hù)名
  1.弱口令用戶(hù)名如admin,test,ceshi等<br />2.員工姓名全拼,員工姓名簡(jiǎn)拼<br />3.公司特征+員工工號/員工姓名<br />4.員工工號+姓名簡(jiǎn)拼<br />5.員工姓名全拼+員工工號<br />6.員工姓名全拼+重復次數,如zhangsan和zhangsan01<br />7.其他
  關(guān)于暴力破解我要扯一句了,就是關(guān)于密碼字典的問(wèn)題。經(jīng)常會(huì )聽(tīng)到某人說(shuō)他的字典多么多么的大,有好幾個(gè)G之類(lèi)的,但是在我覺(jué)得,這很沒(méi)有必要,有些密碼是你跑幾天都跑不出來(lái)的,就算字典確實(shí)夠大,也沒(méi)有必要這樣跑,可能影響心情不說(shuō),大規模地暴力破解,很容易讓人覺(jué)得你在拒絕服務(wù)攻擊。
  其實(shí)我的話(huà)一般跑一跑弱口令就差不多了。
  關(guān)于弱口令字典的問(wèn)題,我也想說(shuō)一嘴,你最好看看,你字典里面的admin、123456、password處在什么位置。記得之前玩CTF的時(shí)候,默認密碼123456,但是那個(gè)師傅死活做不出來(lái),后面一看,字典里面居然沒(méi)有123456這個(gè)密碼。。。
  這里推薦一個(gè)字典,個(gè)人感覺(jué)還是挺好用的。當然更多的是需要自己不斷更新。
  https://github.com/fuzz-security/SuperWordlist
  4、工具cupp和cewl
  對于一些情況,密碼不是直接使用弱口令,而是通過(guò)一些公司的特征+個(gè)人信息制作的,那么這個(gè)時(shí)候,我們的字典便不能直接使用了,需要在這之前加上一些特征,例如阿里SRC可能是a;百度SRC可能是bd等。
  下面2款kali自帶等工具,可以通過(guò)收集信息,生成好用的字典,方便滲透。說(shuō)真的,在滲透測試過(guò)程中,弱口令,YYDS!
  具體使用說(shuō)明和工具介紹,可以查看文章:
  5、自行注冊
  如果能夠注冊那就好辦了,自己注冊一下賬戶(hù)即可。
  6、小總結
  對于非框架的站點(diǎn),登錄接口一般是必不可少的,可能就在主頁(yè),也可能在某個(gè)路徑下,藏著(zhù)后臺的登錄接口,在嘗試了多種方法成功登錄之后,記得嘗試里面是否存在未授權漏洞、越權等漏洞。
  這里借用來(lái)自WS師傅的建議:可以直接掃描出來(lái)的洞,基本都被交完了,可以更多往邏輯漏洞方面找。登錄后的漏洞重復率,比登錄前的往往會(huì )低很多。
  0x07 端口掃描
  前面就是正常的滲透了,那么一個(gè)域名只是在80、443端口才有web服務(wù)嗎?不可否認有些時(shí)候真的是,但是絕大多數情況下,類(lèi)似8080、8443、8081、8089、7001等端口,往往會(huì )有驚喜哦~
  端口掃描也算是老生常談了,市面上也有很多介紹端口掃描的工具使用方法,這里也不細說(shuō)了,就放出平時(shí)使用的命令吧。
  sudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v examples.comsudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -p 1-65535 examples.com
  0x08 目錄掃描dirsearch
  目錄掃描在滲透測試過(guò)程中我認為是必不可少的,一個(gè)站點(diǎn)在不同目錄下的不同文件,往往可能有驚喜哦。
  個(gè)人是喜歡使用dirserach這款工具,不僅高效、頁(yè)面也好看。市面上還有例如御劍、御劍t00ls版等,也是不錯的選擇。
  dirsearch下載地址:
  https://github.com/maurosoria/dirsearch
  具體使用方法可以查看github介紹,這里我一般是使用如下命令(因為擔心線(xiàn)程太高所以通過(guò)-t參數設置為2)
  python3 dirsearch.py -u www.xxx.com -e * -t 2
  關(guān)鍵的地方是大家都可以下載這款工具,獲取它自帶的字典,那么路徑的話(huà),便是大家都能夠搜得到的了,所以這里我推薦是可以適當整合一些師傅們發(fā)出來(lái)的路徑字典到/dirsearch-0.4.2/db/dicc.txt中。例如我的話(huà),是增加了springboot未授權的一些路徑、swagger的路徑以及一些例如vmvare-vcenter的漏洞路徑。
  
  0x09 JS信息收集
  在一個(gè)站點(diǎn)掃描了目錄、嘗試登錄失敗并且沒(méi)有自己注冊功能的情況下,我們還可以從JS文件入手,獲取一些URL,也許某個(gè)URL便能夠未授權訪(fǎng)問(wèn)獲取敏感信息呢。
  1、JSFinder
  工具下載:
  JSFinder是一款用作快速在網(wǎng)站的js文件中提取URL,子域名的工具。個(gè)人覺(jué)得美中不足的地方便是不能對獲取到到URL進(jìn)行一些過(guò)濾,在某些情況下,JS文件中可以爬取非常多的URL,這其中可能大部分是頁(yè)面空或者返回200但是頁(yè)面顯示404的。來(lái)自HZ師傅的建議,可以修改一下工具,基于當前的基礎上,檢測獲取的URL是否可以訪(fǎng)問(wèn),訪(fǎng)問(wèn)后的頁(yè)面大小為多少,標題是什么。。。
  思路放這了,找個(gè)時(shí)間改一改?
  #檢測URL狀態(tài)碼#-----------------------#! /usr/bin/env python#coding=utf-8import sysimport requestsurl='xxxx'request = requests.get(url)httpStatusCode = request.status_codeif httpStatusCode == 200: xxxxelse: xxxx
  #檢測URL返回包大小#-----------------------import requestsdef hum_convert(value): units = ["B", "KB", "MB", "GB", "TB", "PB"] size = 1024.0 for i in range(len(units)): if (value / size) < 1: return "%.2f%s" % (value, units[i]) value = value / sizer = requests.get('https://www.baidu.com')r.status_coder.headerslength = len(r.text)print(hum_convert(length))
  #獲取網(wǎng)站標題#-----------------------#!/usr/bin/python#coding=utf-8urllib.requestimport urllib.requestimport reurl = urllib.request.urlopen('http://www.xxx.com')html = url.read().decode('utf-8')title=re.findall('(.+)',html)print (title)
  2、JS文件
  JS文件與HTML、CSS等文件統一作為前端文件,是可以通過(guò)瀏覽器訪(fǎng)問(wèn)到的,相對于HTML和CSS等文件的顯示和美化作用,JS文件將會(huì )能夠將頁(yè)面的功能點(diǎn)進(jìn)行升華。
  
  對于滲透測試來(lái)說(shuō),JS文件不僅僅能夠找到一些URL、內網(wǎng)IP地址、手機號、調用的組件版本等信息,還存在一些接口,因為前端需要,所以一些接口將會(huì )在JS文件中直接或間接呈現。下面我將介紹如何發(fā)現這些隱藏的接口。
  1、首先在某個(gè)頁(yè)面中,鼠標右鍵,選擇檢查
  2、點(diǎn)擊Application
  
  3、在Frames->top->Scripts中能夠獲取當前頁(yè)面請求到的所有JS
  
  4、火狐瀏覽器的話(huà),則是在調試中
  
  5、如果你請求的JS文件內容都疊在了前幾行的話(huà),下面這個(gè)鍵可以幫你美化輸出
  6、在JS文件中,可以尤為注意帶有api字眼的文件或內容,例如下面這里我發(fā)現了一個(gè)接口。
  
  0xA小程序、APP
  web端沒(méi)有思路的時(shí)候,可以結合小程序、APP來(lái)進(jìn)行滲透。小程序或APP的服務(wù)端其實(shí)可以在一定程度上與web應用的服務(wù)端相聯(lián)系。也就是說(shuō),我們在小程序或者APP上,一樣能夠挖掘web端的漏洞如SQL注入、XSS等,并且相對來(lái)說(shuō),這類(lèi)等服務(wù)端安全措施會(huì )相對沒(méi)有那么完備,所以在web端確實(shí)沒(méi)有思路的時(shí)候,可以迂回滲透,從小程序、APP中進(jìn)行。
  #小程序抓包、APP抓包參考鏈接:<br />https://mp.weixin.qq.com/s/xuo ... %3Bbr />https://mp.weixin.qq.com/s/45Y ... %3Bbr />https://mp.weixin.qq.com/s/M5x ... %3Bbr />https://mp.weixin.qq.com/s/Mfkbxtrxv5AvY-n_bMU7ig
  0xB總結
  以上就是我個(gè)人挖掘SRC的一些信息收集思路,挖掘SRC有的時(shí)候真的很看運氣,也許別人對一個(gè)接口簡(jiǎn)單Fuzz,便出了一個(gè)注入,而我們花了幾天,還是一直看到返回內容為404。所以有的時(shí)候真的可以換個(gè)站試試,也許就挖到高危甚至嚴重了~
  作為一名SRC小白,以上內容均為小弟拙見(jiàn),希望能夠通過(guò)這篇文章,幫到更多的網(wǎng)絡(luò )安全小白,沒(méi)能幫上大佬們真的很抱歉~后續也會(huì )持續提高自己,將學(xué)到的更多的東西分享給大家。
  0XC 推薦一個(gè)網(wǎng)站
  
  有SRC的廠(chǎng)商列表,可以自己去專(zhuān)屬的SRC提交漏洞
   查看全部

  實(shí)戰 | SRC信息收集思路總結
  說(shuō)到信息收集,網(wǎng)上已經(jīng)有許多文章進(jìn)行描述了,那么從正常的子域名、端口、旁站、C段等進(jìn)行信息收集的話(huà),對于正常項目已經(jīng)夠用了,但是挖掘SRC的話(huà),在諸多競爭對手的“幫助”下,大家收集到的信息都差不多,挖掘的漏洞也往往存在重復的情況。
  那么現在我就想分享一下平時(shí)自己進(jìn)行SRC挖掘過(guò)程中,主要是如何進(jìn)行入手的。以下均為小弟拙見(jiàn),大佬勿噴。
  0x01 確定目標
  無(wú)目標隨便打,有沒(méi)有自己對應的SRC應急響應平臺不說(shuō),還往往會(huì )因為一開(kāi)始沒(méi)有挖掘到漏洞而隨意放棄,這樣往往不能挖掘到深層次的漏洞。挖到的大多數是大家都可以簡(jiǎn)單挖到的漏洞,存在大概率重復可能。所以在真的想要花點(diǎn)時(shí)間在SRC漏洞挖掘上的話(huà),建議先選好目標。
  那么目標怎么選呢,考慮到收益回報與付出的比例來(lái)看,建議是從專(zhuān)屬SRC入手,特別在一些活動(dòng)中,可以獲取比平時(shí)更高的收益。
  微信搜一搜:
  
  百度搜一搜:
  現在有活動(dòng)的src已經(jīng)浮現水面了,那么我們就可與從中選擇自己感興趣的SRC。
  0x02 確認測試范圍
  前面說(shuō)到確定測什么SRC,那么下面就要通過(guò)一些方法,獲取這個(gè)SRC的測試范圍,以免測偏。
  1、公眾號
  從公眾號推文入手,活動(dòng)頁(yè)面中可以發(fā)現測試范圍
  2、應急響應官網(wǎng)
  在應急響應官網(wǎng),往往會(huì )有一些活動(dòng)的公告,在里面可以獲取到相應的測試范圍。
  3、愛(ài)企查
  從愛(ài)企查等商業(yè)查詢(xún)平臺獲取公司所屬域名
  
  搜索想要測試等SRC所屬公司名稱(chēng),在知識產(chǎn)權->網(wǎng)站備案中可以獲取測試范圍。
  0x03 子域名(oneforall)
  拿到域名之后,下一步我考慮使用oneforall掃描獲取子域名,就像網(wǎng)上信息收集的文章一樣,主域名的站點(diǎn)不是靜態(tài)界面就是安全防護等級極強,不是隨便就能夠發(fā)現漏洞的,我們挖掘SRC也是要從子域名開(kāi)始,從邊緣資產(chǎn)或一般資產(chǎn)中發(fā)現漏洞。
  工具下載:
  https://github.com/shmilylty/OneForAll
  具體用法如下:
  常用的獲取子域名有2種選擇,一種使用--target指定單個(gè)域名,一種使用--targets指定域名文件。
  python3 oneforall.py --target example.com run<br />python3 oneforall.py --targets ./domains.txt run
  其他獲取子域名的工具還有layer子域名挖掘機、Sublist3r、證書(shū)透明度、在線(xiàn)工具等,這里就不一一闡述了,大體思路是一樣等,獲取子域,然后從中篩選邊緣資產(chǎn),安全防護低資產(chǎn)。
  0x04 系統指紋探測
  通過(guò)上面的方法,我們可以在/OneForAll-0.4.3/results/路徑下獲取以域名為名字的csv文件。里面放入到便是掃描到到所有子域名以及相應信息了。
  下一步便是將收集到到域名全部進(jìn)行一遍指紋探測,從中找出一些明顯使用CMS、OA系統、shiro、Fastjson等的站點(diǎn)。下面介紹平時(shí)使用的2款工具:
  1、Ehole
  下載地址:
  https://github.com/EdgeSecurityTeam/EHole
  使用方法:
  ./Ehole-darwin -l url.txt //URL地址需帶上協(xié)議,每行一個(gè)<br />./Ehole-darwin -f 192.168.1.1/24 //支持單IP或IP段,fofa識別需要配置fofa密鑰和郵箱<br />./Ehole-darwin -l url.txt -json export.json //結果輸出至export.json文件
  2、Glass
  下載地址:
  https://github.com/s7ckTeam/Glass
  使用方法:
  python3 Glass.py -u http://www.examples.com // 單url測試<br />python3 Glass.py -w domain.txt -o 1.txt // url文件內
  0x05 框架型站點(diǎn)漏洞測試
  前面經(jīng)過(guò)了子域名收集以及對收集到的子域名進(jìn)行了指紋信息識別之后,那么對于框架型的站點(diǎn),我們可以?xún)?yōu)先進(jìn)行測試。
  類(lèi)似用友NC、通達OA、藍凌OA等,可以通過(guò)嘗試現有的Nday漏洞進(jìn)行攻擊。
  
  0x06 非框架型站點(diǎn)漏洞測試
  前面測試完框架型的站點(diǎn)了,之后就應該往正常網(wǎng)站,或者經(jīng)過(guò)了二開(kāi)未能直接檢測出指紋的站點(diǎn)進(jìn)行滲透了。那么對于這類(lèi)站點(diǎn),最經(jīng)常遇到的便是登錄框,在這里,我們便可以開(kāi)始測試了。
  
  1、用戶(hù)名枚舉
  抓包嘗試是否用戶(hù)名存在與不存在的情況,返回結果不同。
  2、驗證碼
  是否存在驗證碼,驗證碼是否可以抓包截斷繞過(guò),驗證碼是否可以為空。
  3、暴力破解
  下面是我收集的集中常見(jiàn)的用戶(hù)名
  1.弱口令用戶(hù)名如admin,test,ceshi等<br />2.員工姓名全拼,員工姓名簡(jiǎn)拼<br />3.公司特征+員工工號/員工姓名<br />4.員工工號+姓名簡(jiǎn)拼<br />5.員工姓名全拼+員工工號<br />6.員工姓名全拼+重復次數,如zhangsan和zhangsan01<br />7.其他
  關(guān)于暴力破解我要扯一句了,就是關(guān)于密碼字典的問(wèn)題。經(jīng)常會(huì )聽(tīng)到某人說(shuō)他的字典多么多么的大,有好幾個(gè)G之類(lèi)的,但是在我覺(jué)得,這很沒(méi)有必要,有些密碼是你跑幾天都跑不出來(lái)的,就算字典確實(shí)夠大,也沒(méi)有必要這樣跑,可能影響心情不說(shuō),大規模地暴力破解,很容易讓人覺(jué)得你在拒絕服務(wù)攻擊。
  其實(shí)我的話(huà)一般跑一跑弱口令就差不多了。
  關(guān)于弱口令字典的問(wèn)題,我也想說(shuō)一嘴,你最好看看,你字典里面的admin、123456、password處在什么位置。記得之前玩CTF的時(shí)候,默認密碼123456,但是那個(gè)師傅死活做不出來(lái),后面一看,字典里面居然沒(méi)有123456這個(gè)密碼。。。
  這里推薦一個(gè)字典,個(gè)人感覺(jué)還是挺好用的。當然更多的是需要自己不斷更新。
  https://github.com/fuzz-security/SuperWordlist
  4、工具cupp和cewl
  對于一些情況,密碼不是直接使用弱口令,而是通過(guò)一些公司的特征+個(gè)人信息制作的,那么這個(gè)時(shí)候,我們的字典便不能直接使用了,需要在這之前加上一些特征,例如阿里SRC可能是a;百度SRC可能是bd等。
  下面2款kali自帶等工具,可以通過(guò)收集信息,生成好用的字典,方便滲透。說(shuō)真的,在滲透測試過(guò)程中,弱口令,YYDS!
  具體使用說(shuō)明和工具介紹,可以查看文章:
  5、自行注冊
  如果能夠注冊那就好辦了,自己注冊一下賬戶(hù)即可。
  6、小總結
  對于非框架的站點(diǎn),登錄接口一般是必不可少的,可能就在主頁(yè),也可能在某個(gè)路徑下,藏著(zhù)后臺的登錄接口,在嘗試了多種方法成功登錄之后,記得嘗試里面是否存在未授權漏洞、越權等漏洞。
  這里借用來(lái)自WS師傅的建議:可以直接掃描出來(lái)的洞,基本都被交完了,可以更多往邏輯漏洞方面找。登錄后的漏洞重復率,比登錄前的往往會(huì )低很多。
  0x07 端口掃描
  前面就是正常的滲透了,那么一個(gè)域名只是在80、443端口才有web服務(wù)嗎?不可否認有些時(shí)候真的是,但是絕大多數情況下,類(lèi)似8080、8443、8081、8089、7001等端口,往往會(huì )有驚喜哦~
  端口掃描也算是老生常談了,市面上也有很多介紹端口掃描的工具使用方法,這里也不細說(shuō)了,就放出平時(shí)使用的命令吧。
  sudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v examples.comsudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -p 1-65535 examples.com
  0x08 目錄掃描dirsearch
  目錄掃描在滲透測試過(guò)程中我認為是必不可少的,一個(gè)站點(diǎn)在不同目錄下的不同文件,往往可能有驚喜哦。
  個(gè)人是喜歡使用dirserach這款工具,不僅高效、頁(yè)面也好看。市面上還有例如御劍、御劍t00ls版等,也是不錯的選擇。
  dirsearch下載地址:
  https://github.com/maurosoria/dirsearch
  具體使用方法可以查看github介紹,這里我一般是使用如下命令(因為擔心線(xiàn)程太高所以通過(guò)-t參數設置為2)
  python3 dirsearch.py -u www.xxx.com -e * -t 2
  關(guān)鍵的地方是大家都可以下載這款工具,獲取它自帶的字典,那么路徑的話(huà),便是大家都能夠搜得到的了,所以這里我推薦是可以適當整合一些師傅們發(fā)出來(lái)的路徑字典到/dirsearch-0.4.2/db/dicc.txt中。例如我的話(huà),是增加了springboot未授權的一些路徑、swagger的路徑以及一些例如vmvare-vcenter的漏洞路徑。
  
  0x09 JS信息收集
  在一個(gè)站點(diǎn)掃描了目錄、嘗試登錄失敗并且沒(méi)有自己注冊功能的情況下,我們還可以從JS文件入手,獲取一些URL,也許某個(gè)URL便能夠未授權訪(fǎng)問(wèn)獲取敏感信息呢。
  1、JSFinder
  工具下載:
  JSFinder是一款用作快速在網(wǎng)站的js文件中提取URL,子域名的工具。個(gè)人覺(jué)得美中不足的地方便是不能對獲取到到URL進(jìn)行一些過(guò)濾,在某些情況下,JS文件中可以爬取非常多的URL,這其中可能大部分是頁(yè)面空或者返回200但是頁(yè)面顯示404的。來(lái)自HZ師傅的建議,可以修改一下工具,基于當前的基礎上,檢測獲取的URL是否可以訪(fǎng)問(wèn),訪(fǎng)問(wèn)后的頁(yè)面大小為多少,標題是什么。。。
  思路放這了,找個(gè)時(shí)間改一改?
  #檢測URL狀態(tài)碼#-----------------------#! /usr/bin/env python#coding=utf-8import sysimport requestsurl='xxxx'request = requests.get(url)httpStatusCode = request.status_codeif httpStatusCode == 200: xxxxelse: xxxx
  #檢測URL返回包大小#-----------------------import requestsdef hum_convert(value): units = ["B", "KB", "MB", "GB", "TB", "PB"] size = 1024.0 for i in range(len(units)): if (value / size) < 1: return "%.2f%s" % (value, units[i]) value = value / sizer = requests.get('https://www.baidu.com')r.status_coder.headerslength = len(r.text)print(hum_convert(length))
  #獲取網(wǎng)站標題#-----------------------#!/usr/bin/python#coding=utf-8urllib.requestimport urllib.requestimport reurl = urllib.request.urlopen('http://www.xxx.com')html = url.read().decode('utf-8')title=re.findall('(.+)',html)print (title)
  2、JS文件
  JS文件與HTML、CSS等文件統一作為前端文件,是可以通過(guò)瀏覽器訪(fǎng)問(wèn)到的,相對于HTML和CSS等文件的顯示和美化作用,JS文件將會(huì )能夠將頁(yè)面的功能點(diǎn)進(jìn)行升華。
  
  對于滲透測試來(lái)說(shuō),JS文件不僅僅能夠找到一些URL、內網(wǎng)IP地址、手機號、調用的組件版本等信息,還存在一些接口,因為前端需要,所以一些接口將會(huì )在JS文件中直接或間接呈現。下面我將介紹如何發(fā)現這些隱藏的接口。
  1、首先在某個(gè)頁(yè)面中,鼠標右鍵,選擇檢查
  2、點(diǎn)擊Application
  
  3、在Frames->top->Scripts中能夠獲取當前頁(yè)面請求到的所有JS
  
  4、火狐瀏覽器的話(huà),則是在調試中
  
  5、如果你請求的JS文件內容都疊在了前幾行的話(huà),下面這個(gè)鍵可以幫你美化輸出
  6、在JS文件中,可以尤為注意帶有api字眼的文件或內容,例如下面這里我發(fā)現了一個(gè)接口。
  
  0xA小程序、APP
  web端沒(méi)有思路的時(shí)候,可以結合小程序、APP來(lái)進(jìn)行滲透。小程序或APP的服務(wù)端其實(shí)可以在一定程度上與web應用的服務(wù)端相聯(lián)系。也就是說(shuō),我們在小程序或者APP上,一樣能夠挖掘web端的漏洞如SQL注入、XSS等,并且相對來(lái)說(shuō),這類(lèi)等服務(wù)端安全措施會(huì )相對沒(méi)有那么完備,所以在web端確實(shí)沒(méi)有思路的時(shí)候,可以迂回滲透,從小程序、APP中進(jìn)行。
  #小程序抓包、APP抓包參考鏈接:<br />https://mp.weixin.qq.com/s/xuo ... %3Bbr />https://mp.weixin.qq.com/s/45Y ... %3Bbr />https://mp.weixin.qq.com/s/M5x ... %3Bbr />https://mp.weixin.qq.com/s/Mfkbxtrxv5AvY-n_bMU7ig
  0xB總結
  以上就是我個(gè)人挖掘SRC的一些信息收集思路,挖掘SRC有的時(shí)候真的很看運氣,也許別人對一個(gè)接口簡(jiǎn)單Fuzz,便出了一個(gè)注入,而我們花了幾天,還是一直看到返回內容為404。所以有的時(shí)候真的可以換個(gè)站試試,也許就挖到高危甚至嚴重了~
  作為一名SRC小白,以上內容均為小弟拙見(jiàn),希望能夠通過(guò)這篇文章,幫到更多的網(wǎng)絡(luò )安全小白,沒(méi)能幫上大佬們真的很抱歉~后續也會(huì )持續提高自己,將學(xué)到的更多的東西分享給大家。
  0XC 推薦一個(gè)網(wǎng)站
  
  有SRC的廠(chǎng)商列表,可以自己去專(zhuān)屬的SRC提交漏洞
  

SRC信息收集思路分享,很有用!

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 108 次瀏覽 ? 2022-06-18 15:01 ? 來(lái)自相關(guān)話(huà)題

  SRC信息收集思路分享,很有用!
  說(shuō)到信息收集,網(wǎng)上已經(jīng)有許多文章進(jìn)行描述了,那么從正常的子域名、端口、旁站、C段等進(jìn)行信息收集的話(huà),對于正常項目已經(jīng)夠用了,但是挖掘SRC的話(huà),在諸多競爭對手的“幫助”下,大家收集到的信息都差不多,挖掘的漏洞也往往存在重復的情況。
  那么現在我就想分享一下平時(shí)自己進(jìn)行SRC挖掘過(guò)程中,主要是如何進(jìn)行入手的。
  確定目標
  無(wú)目標隨便打,有沒(méi)有自己對應的SRC應急響應平臺不說(shuō),還往往會(huì )因為一開(kāi)始沒(méi)有挖掘到漏洞而隨意放棄,這樣往往不能挖掘到深層次的漏洞。挖到的大多數是大家都可以簡(jiǎn)單挖到的漏洞,存在大概率重復可能。
  所以在真的想要花點(diǎn)時(shí)間在SRC漏洞挖掘上的話(huà),建議先選好目標。
  那么目標怎么選呢,考慮到收益回報與付出的比例來(lái)看,建議是從專(zhuān)屬SRC入手,特別在一些活動(dòng)中,可以獲取比平時(shí)更高的收益。關(guān)聯(lián)閱讀:
  微信搜一搜:
  
  百度搜一搜:
  現在有活動(dòng)的src已經(jīng)浮現水面了,那么我們就可以從中選擇自己感興趣的SRC。
  確認測試范圍
  前面說(shuō)到確定測什么SRC,那么下面就要通過(guò)一些方法,獲取這個(gè)SRC的測試范圍,以免測偏。
  1、公眾號
  從公眾號推文入手,活動(dòng)頁(yè)面中可以發(fā)現測試范圍
  2、應急響應官網(wǎng)
  在應急響應官網(wǎng),往往會(huì )有一些活動(dòng)的公告,在里面可以獲取到相應的測試范圍。
  3、愛(ài)企查
  從愛(ài)企查等商業(yè)查詢(xún)平臺獲取公司所屬域名
  
  搜索想要測試等SRC所屬公司名稱(chēng),在知識產(chǎn)權->網(wǎng)站備案中可以獲取測試范圍。
  子域名(oneforall)
  拿到域名之后,下一步我考慮使用oneforall掃描獲取子域名,就像網(wǎng)上信息收集的文章一樣,主域名的站點(diǎn)不是靜態(tài)界面就是安全防護等級極強,不是隨便就能夠發(fā)現漏洞的,我們挖掘SRC也是要從子域名開(kāi)始,從邊緣資產(chǎn)或一般資產(chǎn)中發(fā)現漏洞。
  工具下載:
  https://github.com/shmilylty/OneForAll
  具體用法如下:
  常用的獲取子域名有2種選擇,一種使用--target指定單個(gè)域名,一種使用--targets指定域名文件。
  python3 oneforall.py --target example.com run<br />python3 oneforall.py --targets ./domains.txt run
  其他獲取子域名的工具還有layer子域名挖掘機、Sublist3r、證書(shū)透明度、在線(xiàn)工具等,這里就不一一闡述了,大體思路是一樣等,獲取子域,然后從中篩選邊緣資產(chǎn),安全防護低資產(chǎn)。
  系統指紋探測
  通過(guò)上面的方法,我們可以在/OneForAll-0.4.3/results/路徑下獲取以域名為名字的csv文件。
  里面放入到便是掃描到到所有子域名以及相應信息了。
  下一步便是將收集到到域名全部進(jìn)行一遍指紋探測,從中找出一些明顯使用CMS、OA系統、shiro、Fastjson等的站點(diǎn)。
  下面介紹平時(shí)使用的2款工具:
  1、Ehole
  下載地址:
  https://github.com/EdgeSecurityTeam/EHole
  使用方法:
  ./Ehole-darwin -l url.txt //URL地址需帶上協(xié)議,每行一個(gè)<br />./Ehole-darwin -f 192.168.1.1/24 //支持單IP或IP段,fofa識別需要配置fofa密鑰和郵箱<br />./Ehole-darwin -l url.txt -json export.json //結果輸出至export.json文件
  2、Glass(現在無(wú)法使用,勿下載)
  下載地址:
  https://github.com/s7ckTeam/Glass
  使用方法:
  python3 Glass.py -u http://www.examples.com // 單url測試<br />python3 Glass.py -w domain.txt -o 1.txt // url文件內
  框架型站點(diǎn)漏洞測試
  前面經(jīng)過(guò)了子域名收集以及對收集到的子域名進(jìn)行了指紋信息識別之后,那么對于框架型的站點(diǎn),我們可以?xún)?yōu)先進(jìn)行測試。
  類(lèi)似用友NC、通達OA、藍凌OA等,可以通過(guò)嘗試現有的Nday漏洞進(jìn)行攻擊。
  
  非框架型站點(diǎn)漏洞測試
  前面測試完框架型的站點(diǎn)了,之后就應該往正常網(wǎng)站,或者經(jīng)過(guò)了二開(kāi)未能直接檢測出指紋的站點(diǎn)進(jìn)行滲透了。
  那么對于這類(lèi)站點(diǎn),最經(jīng)常遇到的便是登錄框,在這里,我們便可以開(kāi)始測試了。
  
  1、用戶(hù)名枚舉
  抓包嘗試是否用戶(hù)名存在與不存在的情況,返回結果不同。
  2、驗證碼
  是否存在驗證碼,驗證碼是否可以抓包截斷繞過(guò),驗證碼是否可以為空。
  3、暴力破解
  下面是我收集的集中常見(jiàn)的用戶(hù)名
  1.弱口令用戶(hù)名如admin,test,ceshi等<br />2.員工姓名全拼,員工姓名簡(jiǎn)拼<br />3.公司特征+員工工號/員工姓名<br />4.員工工號+姓名簡(jiǎn)拼<br />5.員工姓名全拼+員工工號<br />6.員工姓名全拼+重復次數,如zhangsan和zhangsan01<br />7.其他
  關(guān)于暴力破解我要扯一句了,就是關(guān)于密碼字典的問(wèn)題。
  經(jīng)常會(huì )聽(tīng)到某人說(shuō)他的字典多么多么的大,有好幾個(gè)G之類(lèi)的,但是在我覺(jué)得,這很沒(méi)有必要,有些密碼是你跑幾天都跑不出來(lái)的,就算字典確實(shí)夠大,也沒(méi)有必要這樣跑,可能影響心情不說(shuō),大規模地暴力破解,很容易讓人覺(jué)得你在拒絕服務(wù)攻擊。
  其實(shí)我的話(huà)一般跑一跑弱口令就差不多了。
  關(guān)于弱口令字典的問(wèn)題,我也想說(shuō)一嘴,你最好看看,你字典里面的admin、123456、password處在什么位置。記得之前玩CTF的時(shí)候,默認密碼123456,但是那個(gè)師傅死活做不出來(lái),后面一看,字典里面居然沒(méi)有123456這個(gè)密碼。。。
  這里推薦一個(gè)字典,個(gè)人感覺(jué)還是挺好用的。當然更多的是需要自己不斷更新。
  https://github.com/fuzz-security/SuperWordlist
  4、工具cupp和cewl
  對于一些情況,密碼不是直接使用弱口令,而是通過(guò)一些公司的特征+個(gè)人信息制作的,那么這個(gè)時(shí)候,我們的字典便不能直接使用了,需要在這之前加上一些特征,例如阿里SRC可能是a;百度SRC可能是bd等。
  下面2款kali自帶等工具,可以通過(guò)收集信息,生成好用的字典,方便滲透。
  說(shuō)真的,在滲透測試過(guò)程中,弱口令,YYDS!
  具體使用說(shuō)明和工具介紹,可以查看文章:
  5、自行注冊
  如果能夠注冊那就好辦了,自己注冊一下賬戶(hù)即可。
  6、小總結
  對于非框架的站點(diǎn),登錄接口一般是必不可少的,可能就在主頁(yè),也可能在某個(gè)路徑下,藏著(zhù)后臺的登錄接口,在嘗試了多種方法成功登錄之后,記得嘗試里面是否存在未授權漏洞、越權等漏洞。
  這里借用來(lái)自WS師傅的建議:可以直接掃描出來(lái)的洞,基本都被交完了,可以更多往邏輯漏洞方面找。登錄后的漏洞重復率,比登錄前的往往會(huì )低很多。
  端口掃描
  前面就是正常的滲透了,那么一個(gè)域名只是在80、443端口才有web服務(wù)嗎?
  不可否認有些時(shí)候真的是,但是絕大多數情況下,類(lèi)似8080、8443、8081、8089、7001等端口,往往會(huì )有驚喜哦~
  端口掃描也算是老生常談了,市面上也有很多介紹端口掃描的工具使用方法,這里也不細說(shuō)了,就放出平時(shí)使用的命令吧。
  sudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v examples.comsudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -p 1-65535 examples.com
  目錄掃描dirsearch
  目錄掃描在滲透測試過(guò)程中我認為是必不可少的,一個(gè)站點(diǎn)在不同目錄下的不同文件,往往可能有驚喜哦。
  個(gè)人是喜歡使用dirserach這款工具,不僅高效、頁(yè)面也好看。市面上還有例如御劍、御劍t00ls版等,也是不錯的選擇。
  dirsearch下載地址:
  https://github.com/maurosoria/dirsearch
  具體使用方法可以查看github介紹,這里我一般是使用如下命令(因為擔心線(xiàn)程太高所以通過(guò)-t參數設置為2)
  python3 dirsearch.py -u www.xxx.com -e * -t 2
  關(guān)鍵的地方是大家都可以下載這款工具,獲取它自帶的字典,那么路徑的話(huà),便是大家都能夠搜得到的了,所以這里我推薦是可以適當整合一些師傅們發(fā)出來(lái)的路徑字典到/dirsearch-0.4.2/db/dicc.txt中。
  例如我的話(huà),是增加了springboot未授權的一些路徑、swagger的路徑以及一些例如vmvare-vcenter的漏洞路徑。
  
  JS信息收集
  在一個(gè)站點(diǎn)掃描了目錄、嘗試登錄失敗并且沒(méi)有自己注冊功能的情況下,我們還可以從JS文件入手,獲取一些URL,也許某個(gè)URL便能夠未授權訪(fǎng)問(wèn)獲取敏感信息呢。
  1、JSFinder
  工具下載:
  https://github.com/Threezh1/JSFinder
  JSFinder是一款用作快速在網(wǎng)站的js文件中提取URL,子域名的工具。
  個(gè)人覺(jué)得美中不足的地方便是不能對獲取到到URL進(jìn)行一些過(guò)濾,在某些情況下,JS文件中可以爬取非常多的URL,這其中可能大部分是頁(yè)面空或者返回200但是頁(yè)面顯示404的。
  來(lái)自HZ師傅的建議,可以修改一下工具,基于當前的基礎上,檢測獲取的URL是否可以訪(fǎng)問(wèn),訪(fǎng)問(wèn)后的頁(yè)面大小為多少,標題是什么。。。
  思路放這了,找個(gè)時(shí)間改一改?
  #檢測URL狀態(tài)碼#-----------------------#! /usr/bin/env python#coding=utf-8import sysimport requestsurl='xxxx'request = requests.get(url)httpStatusCode = request.status_codeif httpStatusCode == 200: xxxxelse: xxxx
  #檢測URL返回包大小#-----------------------import requestsdef hum_convert(value): units = ["B", "KB", "MB", "GB", "TB", "PB"] size = 1024.0 for i in range(len(units)): if (value / size) < 1: return "%.2f%s" % (value, units[i]) value = value / sizer = requests.get('https://www.baidu.com')r.status_coder.headerslength = len(r.text)print(hum_convert(length))
  #獲取網(wǎng)站標題#-----------------------#!/usr/bin/python#coding=utf-8urllib.requestimport urllib.requestimport reurl = urllib.request.urlopen('http://www.xxx.com')html = url.read().decode('utf-8')title=re.findall('(.+)',html)print (title)
  2、JS文件
  JS文件與HTML、CSS等文件統一作為前端文件,是可以通過(guò)瀏覽器訪(fǎng)問(wèn)到的,相對于HTML和CSS等文件的顯示和美化作用,JS文件將會(huì )能夠將頁(yè)面的功能點(diǎn)進(jìn)行升華。
  
  對于滲透測試來(lái)說(shuō),JS文件不僅僅能夠找到一些URL、內網(wǎng)IP地址、手機號、調用的組件版本等信息,還存在一些接口,因為前端需要,所以一些接口將會(huì )在JS文件中直接或間接呈現。
  下面我將介紹如何發(fā)現這些隱藏的接口。
  1、首先在某個(gè)頁(yè)面中,鼠標右鍵,選擇檢查
  2、點(diǎn)擊Application
  
  3、在Frames->top->Scripts中能夠獲取當前頁(yè)面請求到的所有JS
  
  4、火狐瀏覽器的話(huà),則是在調試中
  
  5、如果你請求的JS文件內容都疊在了前幾行的話(huà),下面這個(gè)鍵可以幫你美化輸出
  6、在JS文件中,可以尤為注意帶有api字眼的文件或內容,例如下面這里我發(fā)現了一個(gè)接口。
  
  小程序、APP
  web端沒(méi)有思路的時(shí)候,可以結合小程序、APP來(lái)進(jìn)行滲透。
  小程序或APP的服務(wù)端其實(shí)可以在一定程度上與web應用的服務(wù)端相聯(lián)系。
  也就是說(shuō),我們在小程序或者APP上,一樣能夠挖掘web端的漏洞如SQL注入、XSS等,并且相對來(lái)說(shuō),這類(lèi)等服務(wù)端安全措施會(huì )相對沒(méi)有那么完備,所以在web端確實(shí)沒(méi)有思路的時(shí)候,可以迂回滲透,從小程序、APP中進(jìn)行。
  #小程序抓包、APP抓包參考鏈接:<br />https://mp.weixin.qq.com/s/xuo ... %3Bbr />https://mp.weixin.qq.com/s/45Y ... %3Bbr />https://mp.weixin.qq.com/s/M5x ... %3Bbr />https://mp.weixin.qq.com/s/Mfkbxtrxv5AvY-n_bMU7ig
  總結
  以上就是我個(gè)人挖掘SRC的一些信息收集思路,挖掘SRC有的時(shí)候真的很看運氣,也許別人對一個(gè)接口簡(jiǎn)單Fuzz,便出了一個(gè)注入,而我們花了幾天,還是一直看到返回內容為404。
  所以有的時(shí)候真的可以換個(gè)站試試,也許就挖到高危甚至嚴重了~
  作為一名SRC小白,希望能夠通過(guò)這篇文章,幫到更多的網(wǎng)絡(luò )安全小白,沒(méi)能幫上大佬們真的很抱歉~后續也會(huì )持續提高自己,將學(xué)到的更多的東西分享給大家。
  聲明:本公眾號所分享內容僅用于網(wǎng)安愛(ài)好者之間的技術(shù)討論,禁止用于違法途徑,所有滲透都需獲取授權!否則需自行承擔,本公眾號及原作者不承擔相應的后果.
  @學(xué)習更多滲透技能!體驗靶場(chǎng)實(shí)戰練習
  <p style="outline: 0px;letter-spacing: 0.544px;">(hack視頻資料及工具)<br style="outline: 0px;" />
  </p>
 ?。ú糠终故荆?br />   往期推薦
  看到這里了,點(diǎn)個(gè)“贊”、“再看”吧 查看全部

  SRC信息收集思路分享,很有用!
  說(shuō)到信息收集,網(wǎng)上已經(jīng)有許多文章進(jìn)行描述了,那么從正常的子域名、端口、旁站、C段等進(jìn)行信息收集的話(huà),對于正常項目已經(jīng)夠用了,但是挖掘SRC的話(huà),在諸多競爭對手的“幫助”下,大家收集到的信息都差不多,挖掘的漏洞也往往存在重復的情況。
  那么現在我就想分享一下平時(shí)自己進(jìn)行SRC挖掘過(guò)程中,主要是如何進(jìn)行入手的。
  確定目標
  無(wú)目標隨便打,有沒(méi)有自己對應的SRC應急響應平臺不說(shuō),還往往會(huì )因為一開(kāi)始沒(méi)有挖掘到漏洞而隨意放棄,這樣往往不能挖掘到深層次的漏洞。挖到的大多數是大家都可以簡(jiǎn)單挖到的漏洞,存在大概率重復可能。
  所以在真的想要花點(diǎn)時(shí)間在SRC漏洞挖掘上的話(huà),建議先選好目標。
  那么目標怎么選呢,考慮到收益回報與付出的比例來(lái)看,建議是從專(zhuān)屬SRC入手,特別在一些活動(dòng)中,可以獲取比平時(shí)更高的收益。關(guān)聯(lián)閱讀:
  微信搜一搜:
  
  百度搜一搜:
  現在有活動(dòng)的src已經(jīng)浮現水面了,那么我們就可以從中選擇自己感興趣的SRC。
  確認測試范圍
  前面說(shuō)到確定測什么SRC,那么下面就要通過(guò)一些方法,獲取這個(gè)SRC的測試范圍,以免測偏。
  1、公眾號
  從公眾號推文入手,活動(dòng)頁(yè)面中可以發(fā)現測試范圍
  2、應急響應官網(wǎng)
  在應急響應官網(wǎng),往往會(huì )有一些活動(dòng)的公告,在里面可以獲取到相應的測試范圍。
  3、愛(ài)企查
  從愛(ài)企查等商業(yè)查詢(xún)平臺獲取公司所屬域名
  
  搜索想要測試等SRC所屬公司名稱(chēng),在知識產(chǎn)權->網(wǎng)站備案中可以獲取測試范圍。
  子域名(oneforall)
  拿到域名之后,下一步我考慮使用oneforall掃描獲取子域名,就像網(wǎng)上信息收集的文章一樣,主域名的站點(diǎn)不是靜態(tài)界面就是安全防護等級極強,不是隨便就能夠發(fā)現漏洞的,我們挖掘SRC也是要從子域名開(kāi)始,從邊緣資產(chǎn)或一般資產(chǎn)中發(fā)現漏洞。
  工具下載:
  https://github.com/shmilylty/OneForAll
  具體用法如下:
  常用的獲取子域名有2種選擇,一種使用--target指定單個(gè)域名,一種使用--targets指定域名文件。
  python3 oneforall.py --target example.com run<br />python3 oneforall.py --targets ./domains.txt run
  其他獲取子域名的工具還有layer子域名挖掘機、Sublist3r、證書(shū)透明度、在線(xiàn)工具等,這里就不一一闡述了,大體思路是一樣等,獲取子域,然后從中篩選邊緣資產(chǎn),安全防護低資產(chǎn)。
  系統指紋探測
  通過(guò)上面的方法,我們可以在/OneForAll-0.4.3/results/路徑下獲取以域名為名字的csv文件。
  里面放入到便是掃描到到所有子域名以及相應信息了。
  下一步便是將收集到到域名全部進(jìn)行一遍指紋探測,從中找出一些明顯使用CMS、OA系統、shiro、Fastjson等的站點(diǎn)。
  下面介紹平時(shí)使用的2款工具:
  1、Ehole
  下載地址:
  https://github.com/EdgeSecurityTeam/EHole
  使用方法:
  ./Ehole-darwin -l url.txt //URL地址需帶上協(xié)議,每行一個(gè)<br />./Ehole-darwin -f 192.168.1.1/24 //支持單IP或IP段,fofa識別需要配置fofa密鑰和郵箱<br />./Ehole-darwin -l url.txt -json export.json //結果輸出至export.json文件
  2、Glass(現在無(wú)法使用,勿下載)
  下載地址:
  https://github.com/s7ckTeam/Glass
  使用方法:
  python3 Glass.py -u http://www.examples.com // 單url測試<br />python3 Glass.py -w domain.txt -o 1.txt // url文件內
  框架型站點(diǎn)漏洞測試
  前面經(jīng)過(guò)了子域名收集以及對收集到的子域名進(jìn)行了指紋信息識別之后,那么對于框架型的站點(diǎn),我們可以?xún)?yōu)先進(jìn)行測試。
  類(lèi)似用友NC、通達OA、藍凌OA等,可以通過(guò)嘗試現有的Nday漏洞進(jìn)行攻擊。
  
  非框架型站點(diǎn)漏洞測試
  前面測試完框架型的站點(diǎn)了,之后就應該往正常網(wǎng)站,或者經(jīng)過(guò)了二開(kāi)未能直接檢測出指紋的站點(diǎn)進(jìn)行滲透了。
  那么對于這類(lèi)站點(diǎn),最經(jīng)常遇到的便是登錄框,在這里,我們便可以開(kāi)始測試了。
  
  1、用戶(hù)名枚舉
  抓包嘗試是否用戶(hù)名存在與不存在的情況,返回結果不同。
  2、驗證碼
  是否存在驗證碼,驗證碼是否可以抓包截斷繞過(guò),驗證碼是否可以為空。
  3、暴力破解
  下面是我收集的集中常見(jiàn)的用戶(hù)名
  1.弱口令用戶(hù)名如admin,test,ceshi等<br />2.員工姓名全拼,員工姓名簡(jiǎn)拼<br />3.公司特征+員工工號/員工姓名<br />4.員工工號+姓名簡(jiǎn)拼<br />5.員工姓名全拼+員工工號<br />6.員工姓名全拼+重復次數,如zhangsan和zhangsan01<br />7.其他
  關(guān)于暴力破解我要扯一句了,就是關(guān)于密碼字典的問(wèn)題。
  經(jīng)常會(huì )聽(tīng)到某人說(shuō)他的字典多么多么的大,有好幾個(gè)G之類(lèi)的,但是在我覺(jué)得,這很沒(méi)有必要,有些密碼是你跑幾天都跑不出來(lái)的,就算字典確實(shí)夠大,也沒(méi)有必要這樣跑,可能影響心情不說(shuō),大規模地暴力破解,很容易讓人覺(jué)得你在拒絕服務(wù)攻擊。
  其實(shí)我的話(huà)一般跑一跑弱口令就差不多了。
  關(guān)于弱口令字典的問(wèn)題,我也想說(shuō)一嘴,你最好看看,你字典里面的admin、123456、password處在什么位置。記得之前玩CTF的時(shí)候,默認密碼123456,但是那個(gè)師傅死活做不出來(lái),后面一看,字典里面居然沒(méi)有123456這個(gè)密碼。。。
  這里推薦一個(gè)字典,個(gè)人感覺(jué)還是挺好用的。當然更多的是需要自己不斷更新。
  https://github.com/fuzz-security/SuperWordlist
  4、工具cupp和cewl
  對于一些情況,密碼不是直接使用弱口令,而是通過(guò)一些公司的特征+個(gè)人信息制作的,那么這個(gè)時(shí)候,我們的字典便不能直接使用了,需要在這之前加上一些特征,例如阿里SRC可能是a;百度SRC可能是bd等。
  下面2款kali自帶等工具,可以通過(guò)收集信息,生成好用的字典,方便滲透。
  說(shuō)真的,在滲透測試過(guò)程中,弱口令,YYDS!
  具體使用說(shuō)明和工具介紹,可以查看文章:
  5、自行注冊
  如果能夠注冊那就好辦了,自己注冊一下賬戶(hù)即可。
  6、小總結
  對于非框架的站點(diǎn),登錄接口一般是必不可少的,可能就在主頁(yè),也可能在某個(gè)路徑下,藏著(zhù)后臺的登錄接口,在嘗試了多種方法成功登錄之后,記得嘗試里面是否存在未授權漏洞、越權等漏洞。
  這里借用來(lái)自WS師傅的建議:可以直接掃描出來(lái)的洞,基本都被交完了,可以更多往邏輯漏洞方面找。登錄后的漏洞重復率,比登錄前的往往會(huì )低很多。
  端口掃描
  前面就是正常的滲透了,那么一個(gè)域名只是在80、443端口才有web服務(wù)嗎?
  不可否認有些時(shí)候真的是,但是絕大多數情況下,類(lèi)似8080、8443、8081、8089、7001等端口,往往會(huì )有驚喜哦~
  端口掃描也算是老生常談了,市面上也有很多介紹端口掃描的工具使用方法,這里也不細說(shuō)了,就放出平時(shí)使用的命令吧。
  sudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v examples.comsudo nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -p 1-65535 examples.com
  目錄掃描dirsearch
  目錄掃描在滲透測試過(guò)程中我認為是必不可少的,一個(gè)站點(diǎn)在不同目錄下的不同文件,往往可能有驚喜哦。
  個(gè)人是喜歡使用dirserach這款工具,不僅高效、頁(yè)面也好看。市面上還有例如御劍、御劍t00ls版等,也是不錯的選擇。
  dirsearch下載地址:
  https://github.com/maurosoria/dirsearch
  具體使用方法可以查看github介紹,這里我一般是使用如下命令(因為擔心線(xiàn)程太高所以通過(guò)-t參數設置為2)
  python3 dirsearch.py -u www.xxx.com -e * -t 2
  關(guān)鍵的地方是大家都可以下載這款工具,獲取它自帶的字典,那么路徑的話(huà),便是大家都能夠搜得到的了,所以這里我推薦是可以適當整合一些師傅們發(fā)出來(lái)的路徑字典到/dirsearch-0.4.2/db/dicc.txt中。
  例如我的話(huà),是增加了springboot未授權的一些路徑、swagger的路徑以及一些例如vmvare-vcenter的漏洞路徑。
  
  JS信息收集
  在一個(gè)站點(diǎn)掃描了目錄、嘗試登錄失敗并且沒(méi)有自己注冊功能的情況下,我們還可以從JS文件入手,獲取一些URL,也許某個(gè)URL便能夠未授權訪(fǎng)問(wèn)獲取敏感信息呢。
  1、JSFinder
  工具下載:
  https://github.com/Threezh1/JSFinder
  JSFinder是一款用作快速在網(wǎng)站的js文件中提取URL,子域名的工具。
  個(gè)人覺(jué)得美中不足的地方便是不能對獲取到到URL進(jìn)行一些過(guò)濾,在某些情況下,JS文件中可以爬取非常多的URL,這其中可能大部分是頁(yè)面空或者返回200但是頁(yè)面顯示404的。
  來(lái)自HZ師傅的建議,可以修改一下工具,基于當前的基礎上,檢測獲取的URL是否可以訪(fǎng)問(wèn),訪(fǎng)問(wèn)后的頁(yè)面大小為多少,標題是什么。。。
  思路放這了,找個(gè)時(shí)間改一改?
  #檢測URL狀態(tài)碼#-----------------------#! /usr/bin/env python#coding=utf-8import sysimport requestsurl='xxxx'request = requests.get(url)httpStatusCode = request.status_codeif httpStatusCode == 200: xxxxelse: xxxx
  #檢測URL返回包大小#-----------------------import requestsdef hum_convert(value): units = ["B", "KB", "MB", "GB", "TB", "PB"] size = 1024.0 for i in range(len(units)): if (value / size) < 1: return "%.2f%s" % (value, units[i]) value = value / sizer = requests.get('https://www.baidu.com')r.status_coder.headerslength = len(r.text)print(hum_convert(length))
  #獲取網(wǎng)站標題#-----------------------#!/usr/bin/python#coding=utf-8urllib.requestimport urllib.requestimport reurl = urllib.request.urlopen('http://www.xxx.com')html = url.read().decode('utf-8')title=re.findall('(.+)',html)print (title)
  2、JS文件
  JS文件與HTML、CSS等文件統一作為前端文件,是可以通過(guò)瀏覽器訪(fǎng)問(wèn)到的,相對于HTML和CSS等文件的顯示和美化作用,JS文件將會(huì )能夠將頁(yè)面的功能點(diǎn)進(jìn)行升華。
  
  對于滲透測試來(lái)說(shuō),JS文件不僅僅能夠找到一些URL、內網(wǎng)IP地址、手機號、調用的組件版本等信息,還存在一些接口,因為前端需要,所以一些接口將會(huì )在JS文件中直接或間接呈現。
  下面我將介紹如何發(fā)現這些隱藏的接口。
  1、首先在某個(gè)頁(yè)面中,鼠標右鍵,選擇檢查
  2、點(diǎn)擊Application
  
  3、在Frames->top->Scripts中能夠獲取當前頁(yè)面請求到的所有JS
  
  4、火狐瀏覽器的話(huà),則是在調試中
  
  5、如果你請求的JS文件內容都疊在了前幾行的話(huà),下面這個(gè)鍵可以幫你美化輸出
  6、在JS文件中,可以尤為注意帶有api字眼的文件或內容,例如下面這里我發(fā)現了一個(gè)接口。
  
  小程序、APP
  web端沒(méi)有思路的時(shí)候,可以結合小程序、APP來(lái)進(jìn)行滲透。
  小程序或APP的服務(wù)端其實(shí)可以在一定程度上與web應用的服務(wù)端相聯(lián)系。
  也就是說(shuō),我們在小程序或者APP上,一樣能夠挖掘web端的漏洞如SQL注入、XSS等,并且相對來(lái)說(shuō),這類(lèi)等服務(wù)端安全措施會(huì )相對沒(méi)有那么完備,所以在web端確實(shí)沒(méi)有思路的時(shí)候,可以迂回滲透,從小程序、APP中進(jìn)行。
  #小程序抓包、APP抓包參考鏈接:<br />https://mp.weixin.qq.com/s/xuo ... %3Bbr />https://mp.weixin.qq.com/s/45Y ... %3Bbr />https://mp.weixin.qq.com/s/M5x ... %3Bbr />https://mp.weixin.qq.com/s/Mfkbxtrxv5AvY-n_bMU7ig
  總結
  以上就是我個(gè)人挖掘SRC的一些信息收集思路,挖掘SRC有的時(shí)候真的很看運氣,也許別人對一個(gè)接口簡(jiǎn)單Fuzz,便出了一個(gè)注入,而我們花了幾天,還是一直看到返回內容為404。
  所以有的時(shí)候真的可以換個(gè)站試試,也許就挖到高危甚至嚴重了~
  作為一名SRC小白,希望能夠通過(guò)這篇文章,幫到更多的網(wǎng)絡(luò )安全小白,沒(méi)能幫上大佬們真的很抱歉~后續也會(huì )持續提高自己,將學(xué)到的更多的東西分享給大家。
  聲明:本公眾號所分享內容僅用于網(wǎng)安愛(ài)好者之間的技術(shù)討論,禁止用于違法途徑,所有滲透都需獲取授權!否則需自行承擔,本公眾號及原作者不承擔相應的后果.
  @學(xué)習更多滲透技能!體驗靶場(chǎng)實(shí)戰練習
  <p style="outline: 0px;letter-spacing: 0.544px;">(hack視頻資料及工具)<br style="outline: 0px;" />
  </p>
 ?。ú糠终故荆?br />   往期推薦
  看到這里了,點(diǎn)個(gè)“贊”、“再看”吧

文章采集excel模型分析,接下來(lái)進(jìn)行數據的提取、數據可視化相關(guān)內容的學(xué)習和進(jìn)階~

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 156 次瀏覽 ? 2022-06-09 14:19 ? 來(lái)自相關(guān)話(huà)題

  文章采集excel模型分析,接下來(lái)進(jìn)行數據的提取、數據可視化相關(guān)內容的學(xué)習和進(jìn)階~
  文章采集調用excel模型分析,接下來(lái)進(jìn)行數據的提取、數據可視化相關(guān)內容的學(xué)習和進(jìn)階~自動(dòng)刷新banner、喜歡題目后推薦的手游、相應游戲的收入大幅增長(cháng)~歡迎關(guān)注公眾號并與本人交流~【長(cháng)按掃碼加入游戲帝國數據交流群】群名稱(chēng):數據魔方收集題目:哪些網(wǎng)站比百度更懂中國互聯(lián)網(wǎng)?將通過(guò)采集不同維度的數據,去分析中國互聯(lián)網(wǎng)的發(fā)展和中國互聯(lián)網(wǎng)游戲的發(fā)展趨勢。
  這次采集的數據集是智能問(wèn)答領(lǐng)域的相關(guān)數據。數據集量為22631條,包含512個(gè)樣本。每條樣本包含30個(gè)問(wèn)題,554個(gè)標簽。這次的目標是分析一下2010年至今國內大多數站點(diǎn)上的語(yǔ)音通話(huà),也包括大量沒(méi)有開(kāi)放數據的,例如通話(huà)能力不達標準的站點(diǎn)。預計耗時(shí)3-5天。數據集說(shuō)明數據樣本:本數據集已經(jīng)對問(wèn)題,標簽以及語(yǔ)音頻率進(jìn)行了pre處理,每條問(wèn)題后面使用逗號間隔隔開(kāi)。
  讀取數據時(shí)需要選擇以weka或java版本的python庫,手機端的java在數據讀取階段發(fā)生了錯誤,大約耗時(shí)30秒。我嘗試在kaggle的googleanalytics中數據導入,然后再使用kaggle自帶的googleloggersspark套件導入數據集,但最終結果并不好。代碼數據格式:youtu.be/i17alvfog提取方式:以字典格式讀取youtu.be/kgcolor_readerkcbxt提取數據的關(guān)鍵字段fname提取數據所在位置:數據讀取工具:pipinstallpandaspandasimportpandasaspd#讀取獲取數據pandasread_csv('數據集.csv',index=true)#將數據集變換成dataframe格式csv_data=pd.read_csv(pandas.dataframe(fname=fname))#讀取數據集head=csv_data.head(5)#數據集概覽title1=csv_data.title2=csv_data.title3=csv_data.title4=csv_data.title5=csv_data.title6=csv_data.title7=csv_data.title8=csv_data.title9=csv_data.title10=csv_data.title11=csv_data.title12=csv_data.title13=csv_data.title14=csv_data.title15=csv_data.title16=csv_data.title17=csv_data.title18=csv_data.title19=csv_data.title20=csv_data.title21=csv_data.title22=csv_data.title23=csv_data.title24=csv_data.title25=csv_data.title26=csv_data.title27=csv_data.title28=csv_data.title29。 查看全部

  文章采集excel模型分析,接下來(lái)進(jìn)行數據的提取、數據可視化相關(guān)內容的學(xué)習和進(jìn)階~
  文章采集調用excel模型分析,接下來(lái)進(jìn)行數據的提取、數據可視化相關(guān)內容的學(xué)習和進(jìn)階~自動(dòng)刷新banner、喜歡題目后推薦的手游、相應游戲的收入大幅增長(cháng)~歡迎關(guān)注公眾號并與本人交流~【長(cháng)按掃碼加入游戲帝國數據交流群】群名稱(chēng):數據魔方收集題目:哪些網(wǎng)站比百度更懂中國互聯(lián)網(wǎng)?將通過(guò)采集不同維度的數據,去分析中國互聯(lián)網(wǎng)的發(fā)展和中國互聯(lián)網(wǎng)游戲的發(fā)展趨勢。
  這次采集的數據集是智能問(wèn)答領(lǐng)域的相關(guān)數據。數據集量為22631條,包含512個(gè)樣本。每條樣本包含30個(gè)問(wèn)題,554個(gè)標簽。這次的目標是分析一下2010年至今國內大多數站點(diǎn)上的語(yǔ)音通話(huà),也包括大量沒(méi)有開(kāi)放數據的,例如通話(huà)能力不達標準的站點(diǎn)。預計耗時(shí)3-5天。數據集說(shuō)明數據樣本:本數據集已經(jīng)對問(wèn)題,標簽以及語(yǔ)音頻率進(jìn)行了pre處理,每條問(wèn)題后面使用逗號間隔隔開(kāi)。
  讀取數據時(shí)需要選擇以weka或java版本的python庫,手機端的java在數據讀取階段發(fā)生了錯誤,大約耗時(shí)30秒。我嘗試在kaggle的googleanalytics中數據導入,然后再使用kaggle自帶的googleloggersspark套件導入數據集,但最終結果并不好。代碼數據格式:youtu.be/i17alvfog提取方式:以字典格式讀取youtu.be/kgcolor_readerkcbxt提取數據的關(guān)鍵字段fname提取數據所在位置:數據讀取工具:pipinstallpandaspandasimportpandasaspd#讀取獲取數據pandasread_csv('數據集.csv',index=true)#將數據集變換成dataframe格式csv_data=pd.read_csv(pandas.dataframe(fname=fname))#讀取數據集head=csv_data.head(5)#數據集概覽title1=csv_data.title2=csv_data.title3=csv_data.title4=csv_data.title5=csv_data.title6=csv_data.title7=csv_data.title8=csv_data.title9=csv_data.title10=csv_data.title11=csv_data.title12=csv_data.title13=csv_data.title14=csv_data.title15=csv_data.title16=csv_data.title17=csv_data.title18=csv_data.title19=csv_data.title20=csv_data.title21=csv_data.title22=csv_data.title23=csv_data.title24=csv_data.title25=csv_data.title26=csv_data.title27=csv_data.title28=csv_data.title29。

文章采集調用googleapi,估計能免費獲取到文章的幾篇文章

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 126 次瀏覽 ? 2022-05-22 16:06 ? 來(lái)自相關(guān)話(huà)題

  文章采集調用googleapi,估計能免費獲取到文章的幾篇文章
  文章采集調用googleapi,估計能免費獲取到文章的幾篇文章,但注意看好文章?。?!推薦小黑胖收集整理的。那個(gè)都能看到,只是bug多。要不你弄弄這個(gè),一篇文章都能看到。其他平臺應該也行。但能不能一次采集到整個(gè)鏈接,就是另一回事了。需要用到文章收集軟件。把需要收集的文章都收集起來(lái),重點(diǎn)是還是一次性收集好后不能再次收集的,在返回收集文章的軟件就行。
  需要注冊一個(gè)谷歌賬號,
  采集云,一鍵登錄,
  如果是簡(jiǎn)單的博客爬蟲(chóng),可以直接下載相關(guān)api文檔即可。googleapidocs需要翻墻,很多博客爬蟲(chóng)都可以參考。
  googlereader.
  freegradleweekly
  api,github上有很多開(kāi)源的githubapis,都能抓googleapi;scrapy,scrapy是scrapy框架開(kāi)發(fā)的,可以抓googleapi;httplib,可以抓googleapi,不用翻墻;用python爬蟲(chóng)先看googleapi,googleapi沒(méi)有你想象的那么復雜,網(wǎng)上有例子,實(shí)踐下就知道了。
  推薦apifinder。而且目前來(lái)說(shuō),
  googleapidocs
  newmail-weeklyextension
  apifinder
  有使用過(guò),也有之前從網(wǎng)上找到的api,googleapisnewsreader這些,都大同小異。 查看全部

  文章采集調用googleapi,估計能免費獲取到文章的幾篇文章
  文章采集調用googleapi,估計能免費獲取到文章的幾篇文章,但注意看好文章?。?!推薦小黑胖收集整理的。那個(gè)都能看到,只是bug多。要不你弄弄這個(gè),一篇文章都能看到。其他平臺應該也行。但能不能一次采集到整個(gè)鏈接,就是另一回事了。需要用到文章收集軟件。把需要收集的文章都收集起來(lái),重點(diǎn)是還是一次性收集好后不能再次收集的,在返回收集文章的軟件就行。
  需要注冊一個(gè)谷歌賬號,
  采集云,一鍵登錄,
  如果是簡(jiǎn)單的博客爬蟲(chóng),可以直接下載相關(guān)api文檔即可。googleapidocs需要翻墻,很多博客爬蟲(chóng)都可以參考。
  googlereader.
  freegradleweekly
  api,github上有很多開(kāi)源的githubapis,都能抓googleapi;scrapy,scrapy是scrapy框架開(kāi)發(fā)的,可以抓googleapi;httplib,可以抓googleapi,不用翻墻;用python爬蟲(chóng)先看googleapi,googleapi沒(méi)有你想象的那么復雜,網(wǎng)上有例子,實(shí)踐下就知道了。
  推薦apifinder。而且目前來(lái)說(shuō),
  googleapidocs
  newmail-weeklyextension
  apifinder
  有使用過(guò),也有之前從網(wǎng)上找到的api,googleapisnewsreader這些,都大同小異。

【文末贈書(shū)】紅隊攻防之信息收集總結

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 114 次瀏覽 ? 2022-05-15 14:33 ? 來(lái)自相關(guān)話(huà)題

  【文末贈書(shū)】紅隊攻防之信息收集總結
  之前也總結過(guò)類(lèi)似的信息收集相關(guān)的文章,但是每隔一段時(shí)間理解和手法都會(huì )有所不同,本文以hvv或授權但僅提供公司名稱(chēng)域名等情況下滲透測試的視角總結一些自己最近做信息收集的流程套路。
  信息收集一、初始已知信息
  前言中提到的兩種情況,一般初始信息只有公司名稱(chēng)、個(gè)別官網(wǎng)域名、靶標名稱(chēng)等信息,以此為起點(diǎn)進(jìn)行信息收集。
  二、搜尋根域名
  此步驟個(gè)人的經(jīng)驗是,面對大公司優(yōu)先選擇工信部備案查詢(xún),小公司用搜索引擎做起點(diǎn),然后幾種方式都可以過(guò)一遍,查漏補缺,盡量獲取最全的信息。大部分公司根域名都不會(huì )很多,全部過(guò)一遍也不會(huì )用掉多少時(shí)間。
  1.搜索引擎
  搜索引擎直接搜索其公司名稱(chēng),獲取其相關(guān)根域名
  2.天眼查、企查查
  從天眼查、企查查等途徑,輸入公司名,查詢(xún)其域名以及全資控股子公司的域名
  3.工信部備案
  工信部備案查詢(xún)域名/ip地址(需要詳細且正確的公司名稱(chēng),結果也會(huì )很全面)
  #/Integrated/recordQuery
  4.fofa
  fofa查詢(xún)其公司名稱(chēng),獲取相關(guān)域名
  5.站長(cháng)之家
  使用其icp查詢(xún)功能查詢(xún)備案,當我們不知道公司完整名稱(chēng)的時(shí)候也可以使用此網(wǎng)站功能使用已知域名查詢(xún)完整備案公司名稱(chēng)
  6.反查域名
  用已知的某些ip反查域名
  三、子域名
  在子域名收集這步本人一般不喜歡爆破的方式,子域名爆破比較依賴(lài)字典,字典小就收集不全,字典大就很費時(shí)間,所以一般優(yōu)先在各類(lèi)解析記錄的網(wǎng)站查詢(xún)。
  1.各類(lèi)網(wǎng)站查詢(xún)解析記錄
  以bilibili為例:
  類(lèi)似的網(wǎng)站非常多,這兩個(gè)都是免費的,但是第二個(gè)要注冊登錄
  2.子域名爆破
  相關(guān)的工具很多,部分掃描器也自帶子域名爆破功能或可安裝相關(guān)插件。
  subDomainsBrute
  3.fofa、shodan
  利用這類(lèi)工具對域名資產(chǎn)進(jìn)行查詢(xún),如
  fofa語(yǔ)法domain=””
  4.OneForAll
  此工具會(huì )集成多種方式搜集子域名,包括dns查詢(xún)、證書(shū)查詢(xún)等,詳情見(jiàn)其項目中的readme
  安裝
  1<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />3<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />4<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />5<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  git clone https://github.com/shmilylty/OneForAll.git<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />cd OneForAll/<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />python3 -m pip install -U pip setuptools wheel -i https://mirrors.aliyun.com/pypi/simple/<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />python3 oneforall.py --help<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  1<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  python3 oneforall.py --target example.com run<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />python3 oneforall.py --targets ./example.txt run<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  四、ip
  ip列表不完全來(lái)源于域名解析,有一部分ip是直接使用ip地址提供服務(wù)的,需要提前收集這部分信息,另一部分是通過(guò)域名解析過(guò)來(lái)的。
  1.各類(lèi)網(wǎng)站查詢(xún)解析記錄
  同子域名查詢(xún)中的操作,但是需要做的是把ip列表導出
  2.解析域名
  將所有已收集到的子域名通過(guò)腳本批量調用dig或nslookup解析ip
  1<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  nslookup xxx.com<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />dig xxx.com @114.114.114.114<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  編寫(xiě)腳本批量調用dig命令,導出結果
  或將域名列表放在在線(xiàn)解析網(wǎng)站中,導出其解析結果
  這個(gè)步驟中需要額外關(guān)注cdn的情況,繞過(guò)cdn尋找其真實(shí)ip,可參考這篇文檔
  3.c段
  將前面已經(jīng)獲得的ip全部整理好,使用腳本進(jìn)行排序,懶得寫(xiě)腳本也可以使用在線(xiàn)的功能
  如ip地址排序計算器
  得到排序好的ip,可以先自己判斷哪些c段可能屬于目標,再進(jìn)行一些掃描和訪(fǎng)問(wèn),整理更全面的ip列表。
  五、端口
  使用masscan、nmap等工具對端口信息進(jìn)行收集
  六、web服務(wù)
  使用webfinder等工具掃描已整理ip列表的web常用端口,導出形如:port/以及:port/的web服務(wù)列表
  指紋識別
  1.
  2.
  七、漏掃1.主機掃描
  上文整理好的ip列表和域名列表,可以丟入主機掃描相關(guān)的掃描器中,如goby、Nessus等
  2.web掃描
  整理好web服務(wù)列表,可以丟入awvs等工具進(jìn)行掃描,同時(shí)可以聯(lián)動(dòng)xray批量掃描 查看全部

  【文末贈書(shū)】紅隊攻防之信息收集總結
  之前也總結過(guò)類(lèi)似的信息收集相關(guān)的文章,但是每隔一段時(shí)間理解和手法都會(huì )有所不同,本文以hvv或授權但僅提供公司名稱(chēng)域名等情況下滲透測試的視角總結一些自己最近做信息收集的流程套路。
  信息收集一、初始已知信息
  前言中提到的兩種情況,一般初始信息只有公司名稱(chēng)、個(gè)別官網(wǎng)域名、靶標名稱(chēng)等信息,以此為起點(diǎn)進(jìn)行信息收集。
  二、搜尋根域名
  此步驟個(gè)人的經(jīng)驗是,面對大公司優(yōu)先選擇工信部備案查詢(xún),小公司用搜索引擎做起點(diǎn),然后幾種方式都可以過(guò)一遍,查漏補缺,盡量獲取最全的信息。大部分公司根域名都不會(huì )很多,全部過(guò)一遍也不會(huì )用掉多少時(shí)間。
  1.搜索引擎
  搜索引擎直接搜索其公司名稱(chēng),獲取其相關(guān)根域名
  2.天眼查、企查查
  從天眼查、企查查等途徑,輸入公司名,查詢(xún)其域名以及全資控股子公司的域名
  3.工信部備案
  工信部備案查詢(xún)域名/ip地址(需要詳細且正確的公司名稱(chēng),結果也會(huì )很全面)
  #/Integrated/recordQuery
  4.fofa
  fofa查詢(xún)其公司名稱(chēng),獲取相關(guān)域名
  5.站長(cháng)之家
  使用其icp查詢(xún)功能查詢(xún)備案,當我們不知道公司完整名稱(chēng)的時(shí)候也可以使用此網(wǎng)站功能使用已知域名查詢(xún)完整備案公司名稱(chēng)
  6.反查域名
  用已知的某些ip反查域名
  三、子域名
  在子域名收集這步本人一般不喜歡爆破的方式,子域名爆破比較依賴(lài)字典,字典小就收集不全,字典大就很費時(shí)間,所以一般優(yōu)先在各類(lèi)解析記錄的網(wǎng)站查詢(xún)。
  1.各類(lèi)網(wǎng)站查詢(xún)解析記錄
  以bilibili為例:
  類(lèi)似的網(wǎng)站非常多,這兩個(gè)都是免費的,但是第二個(gè)要注冊登錄
  2.子域名爆破
  相關(guān)的工具很多,部分掃描器也自帶子域名爆破功能或可安裝相關(guān)插件。
  subDomainsBrute
  3.fofa、shodan
  利用這類(lèi)工具對域名資產(chǎn)進(jìn)行查詢(xún),如
  fofa語(yǔ)法domain=””
  4.OneForAll
  此工具會(huì )集成多種方式搜集子域名,包括dns查詢(xún)、證書(shū)查詢(xún)等,詳情見(jiàn)其項目中的readme
  安裝
  1<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />3<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />4<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />5<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  git clone https://github.com/shmilylty/OneForAll.git<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />cd OneForAll/<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />python3 -m pip install -U pip setuptools wheel -i https://mirrors.aliyun.com/pypi/simple/<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />python3 oneforall.py --help<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  1<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  python3 oneforall.py --target example.com run<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />python3 oneforall.py --targets ./example.txt run<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  四、ip
  ip列表不完全來(lái)源于域名解析,有一部分ip是直接使用ip地址提供服務(wù)的,需要提前收集這部分信息,另一部分是通過(guò)域名解析過(guò)來(lái)的。
  1.各類(lèi)網(wǎng)站查詢(xún)解析記錄
  同子域名查詢(xún)中的操作,但是需要做的是把ip列表導出
  2.解析域名
  將所有已收集到的子域名通過(guò)腳本批量調用dig或nslookup解析ip
  1<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  nslookup xxx.com<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />dig xxx.com @114.114.114.114<br style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;" />
  編寫(xiě)腳本批量調用dig命令,導出結果
  或將域名列表放在在線(xiàn)解析網(wǎng)站中,導出其解析結果
  這個(gè)步驟中需要額外關(guān)注cdn的情況,繞過(guò)cdn尋找其真實(shí)ip,可參考這篇文檔
  3.c段
  將前面已經(jīng)獲得的ip全部整理好,使用腳本進(jìn)行排序,懶得寫(xiě)腳本也可以使用在線(xiàn)的功能
  如ip地址排序計算器
  得到排序好的ip,可以先自己判斷哪些c段可能屬于目標,再進(jìn)行一些掃描和訪(fǎng)問(wèn),整理更全面的ip列表。
  五、端口
  使用masscan、nmap等工具對端口信息進(jìn)行收集
  六、web服務(wù)
  使用webfinder等工具掃描已整理ip列表的web常用端口,導出形如:port/以及:port/的web服務(wù)列表
  指紋識別
  1.
  2.
  七、漏掃1.主機掃描
  上文整理好的ip列表和域名列表,可以丟入主機掃描相關(guān)的掃描器中,如goby、Nessus等
  2.web掃描
  整理好web服務(wù)列表,可以丟入awvs等工具進(jìn)行掃描,同時(shí)可以聯(lián)動(dòng)xray批量掃描

一篇文章教會(huì )你利用Python網(wǎng)絡(luò )爬蟲(chóng)實(shí)現豆瓣電影采集

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 223 次瀏覽 ? 2022-05-14 11:03 ? 來(lái)自相關(guān)話(huà)題

  一篇文章教會(huì )你利用Python網(wǎng)絡(luò )爬蟲(chóng)實(shí)現豆瓣電影采集
  點(diǎn)擊上方“IT共享之家”,進(jìn)行關(guān)注
  回復“資料”可獲贈Python學(xué)習福利
  【一、項目背景】
  豆瓣電影提供最新的電影介紹及評論包括上映影片的影訊查詢(xún)及購票服務(wù)??梢杂涗浵肟?、在看和看過(guò)的電影電視劇 、順便打分、寫(xiě)影評。極大地方便了人們的生活。
  今天以電視?。绖。槔?,批量爬取對應的電影,寫(xiě)入csv文檔 。用戶(hù)可以通過(guò)評分,更好的選擇自己想要的電影。
  【二、項目目標】
  獲取對應的電影名稱(chēng),評分,詳情鏈接,下載 電影的圖片,保存文檔。
  【三、涉及的庫和網(wǎng)站】
  1、網(wǎng)址如下:
  https://movie.douban.com/j/sea ... rt%3D{}
  2、涉及的庫:requests、fake_useragent、json、csv
  3、軟件:PyCharm
  【四、項目分析】
  1、如何多網(wǎng)頁(yè)請求?
  點(diǎn)擊下一頁(yè)時(shí),每增加一頁(yè)paged自增加20,用{}代替變換的變量,再用for循環(huán)遍歷這網(wǎng)址,實(shí)現多個(gè)網(wǎng)址請求。
  2. 如何獲取真正請求的地址?
  請求數據時(shí),發(fā)現頁(yè)面上并沒(méi)有對應數據。其實(shí)豆瓣網(wǎng)采用javascript動(dòng)態(tài)加載內容,防止采集。
  1)F12右鍵檢查,找到Network,左邊菜單Name , 找到第五個(gè)數據,點(diǎn)擊Preview。
  
  2)點(diǎn)開(kāi)subjects,可以看到 title 就是對應電影名稱(chēng)。rate就是對應評分。通過(guò)js解析subjects字典,找到需要的字段。
  
  3. 如何網(wǎng)頁(yè)訪(fǎng)問(wèn)?
  https://movie.douban.com/j/sea ... %3Bbr />https://movie.douban.com/j/sea ... %3Bbr />https://movie.douban.com/j/sea ... %3Bbr />https://movie.douban.com/j/sea ... %3D60
  當點(diǎn)擊下一頁(yè)時(shí),每增加一頁(yè)page自增加20,用{}代替變換的變量,再用for循環(huán)遍歷這網(wǎng)址,實(shí)現多個(gè)網(wǎng)址請求。
  【五、項目實(shí)施】
  1、我們定義一個(gè)class類(lèi)繼承object,然后定義init方法繼承self,再定義一個(gè)主函數main繼承self。導入需要的庫和請求網(wǎng)址。
  import requests,jsonfrom fake_useragent import UserAgentimport csv<br />class Doban(object): def __init__(self): self.url = "https://movie.douban.com/j/sea ... rt%3D{}"<br /> def main(self): pass<br />if __name__ == '__main__': Siper = Doban() Siper.main()
  2、隨機產(chǎn)生UserAgent,構造請求頭,防止反爬。
   for i in range(1, 50): self.headers = { 'User-Agent': ua.random, }
  3、發(fā)送請求 ,獲取響應,頁(yè)面回調,方便下次請求。
   def get_page(self, url): res = requests.get(url=url, headers=self.headers) html = res.content.decode("utf-8") return html
  4、json解析頁(yè)面數據,獲取對應的字典。
   data = json.loads(html)['subjects'] # print(data[0])
  5、for遍歷,獲取對應的電影名、 評分、下詳情頁(yè)鏈接。
   print(name, goblin_herf) html2 = self.get_page(goblin_herf) # 第二個(gè)發(fā)生請求 parse_html2 = etree.HTML(html2) r = parse_html2.xpath('//div[@class="entry"]/p/text()')
  6、創(chuàng )建csv文件進(jìn)行寫(xiě)入,定義對應的標題頭內容,保存數據 。
   # 創(chuàng )建csv文件進(jìn)行寫(xiě)入 csv_file = open('scr.csv', 'a', encoding='gbk') csv_writer = csv.writer(csv_file) # 寫(xiě)入csv標題頭內容 csv_writerr.writerow(['電影', '評分', "詳情頁(yè)"]) #寫(xiě)入數據 csv_writer.writerow([id, rate, urll])
  7、圖片地址進(jìn)行請求。定義圖片名稱(chēng),保存文檔。
   html2 = requests.get(url=urll, headers=self.headers).content dirname = "./圖/" + id + ".jpg" with open(dirname, 'wb') as f: f.write(html2) print("%s 【下載成功?。。?!】" % id)
  8、調用方法,實(shí)現功能。
   html = self.get_page(url) self.parse_page(html)
  9、項目?jì)?yōu)化:1)設置時(shí)間延時(shí)。
   time.sleep(1.4)
  2)定義一個(gè)變量u, for遍歷,表示爬取的是第幾頁(yè)。(更清晰可觀(guān))。
   u = 0 self.u += 1;
  【六、效果展示】
  1、點(diǎn)擊綠色小三角運行輸入起始頁(yè),終止頁(yè)( 從0頁(yè)開(kāi)始 )。
  
  2、將下載成功信息顯示在控制臺。
  
  3、保存csv文檔。
  
  4、電影圖片展示。
  
  【七、總結】
  1、不建議抓取太多數據,容易對服務(wù)器造成負載,淺嘗輒止即可。
  2、本文章就Python爬取豆瓣網(wǎng),在應用中出現的難點(diǎn)和重點(diǎn),以及如何防止反爬,做出了相對于的解決方案。
  3、希望通過(guò)這個(gè)項目,能夠幫助了解json解析頁(yè)面的基本流程,字符串是如何拼接,format函數如何運用。
  4、本文基于Python網(wǎng)絡(luò )爬蟲(chóng),利用爬蟲(chóng)庫,實(shí)現豆瓣電影及其圖片的獲取。實(shí)現的時(shí)候,總會(huì )有各種各樣的問(wèn)題,切勿眼高手低,勤動(dòng)手,才可以理解的更加深刻。
  5、需要本文源碼的小伙伴,請在下方公眾號后臺回復“豆瓣電影”四個(gè)字,即可獲取。
  看完本文有收獲?請轉發(fā)分享給更多的人
  IT共享之家 查看全部

  一篇文章教會(huì )你利用Python網(wǎng)絡(luò )爬蟲(chóng)實(shí)現豆瓣電影采集
  點(diǎn)擊上方“IT共享之家”,進(jìn)行關(guān)注
  回復“資料”可獲贈Python學(xué)習福利
  【一、項目背景】
  豆瓣電影提供最新的電影介紹及評論包括上映影片的影訊查詢(xún)及購票服務(wù)??梢杂涗浵肟?、在看和看過(guò)的電影電視劇 、順便打分、寫(xiě)影評。極大地方便了人們的生活。
  今天以電視?。绖。槔?,批量爬取對應的電影,寫(xiě)入csv文檔 。用戶(hù)可以通過(guò)評分,更好的選擇自己想要的電影。
  【二、項目目標】
  獲取對應的電影名稱(chēng),評分,詳情鏈接,下載 電影的圖片,保存文檔。
  【三、涉及的庫和網(wǎng)站】
  1、網(wǎng)址如下:
  https://movie.douban.com/j/sea ... rt%3D{}
  2、涉及的庫:requests、fake_useragent、json、csv
  3、軟件:PyCharm
  【四、項目分析】
  1、如何多網(wǎng)頁(yè)請求?
  點(diǎn)擊下一頁(yè)時(shí),每增加一頁(yè)paged自增加20,用{}代替變換的變量,再用for循環(huán)遍歷這網(wǎng)址,實(shí)現多個(gè)網(wǎng)址請求。
  2. 如何獲取真正請求的地址?
  請求數據時(shí),發(fā)現頁(yè)面上并沒(méi)有對應數據。其實(shí)豆瓣網(wǎng)采用javascript動(dòng)態(tài)加載內容,防止采集。
  1)F12右鍵檢查,找到Network,左邊菜單Name , 找到第五個(gè)數據,點(diǎn)擊Preview。
  
  2)點(diǎn)開(kāi)subjects,可以看到 title 就是對應電影名稱(chēng)。rate就是對應評分。通過(guò)js解析subjects字典,找到需要的字段。
  
  3. 如何網(wǎng)頁(yè)訪(fǎng)問(wèn)?
  https://movie.douban.com/j/sea ... %3Bbr />https://movie.douban.com/j/sea ... %3Bbr />https://movie.douban.com/j/sea ... %3Bbr />https://movie.douban.com/j/sea ... %3D60
  當點(diǎn)擊下一頁(yè)時(shí),每增加一頁(yè)page自增加20,用{}代替變換的變量,再用for循環(huán)遍歷這網(wǎng)址,實(shí)現多個(gè)網(wǎng)址請求。
  【五、項目實(shí)施】
  1、我們定義一個(gè)class類(lèi)繼承object,然后定義init方法繼承self,再定義一個(gè)主函數main繼承self。導入需要的庫和請求網(wǎng)址。
  import requests,jsonfrom fake_useragent import UserAgentimport csv<br />class Doban(object): def __init__(self): self.url = "https://movie.douban.com/j/sea ... rt%3D{}"<br /> def main(self): pass<br />if __name__ == '__main__': Siper = Doban() Siper.main()
  2、隨機產(chǎn)生UserAgent,構造請求頭,防止反爬。
   for i in range(1, 50): self.headers = { 'User-Agent': ua.random, }
  3、發(fā)送請求 ,獲取響應,頁(yè)面回調,方便下次請求。
   def get_page(self, url): res = requests.get(url=url, headers=self.headers) html = res.content.decode("utf-8") return html
  4、json解析頁(yè)面數據,獲取對應的字典。
   data = json.loads(html)['subjects'] # print(data[0])
  5、for遍歷,獲取對應的電影名、 評分、下詳情頁(yè)鏈接。
   print(name, goblin_herf) html2 = self.get_page(goblin_herf) # 第二個(gè)發(fā)生請求 parse_html2 = etree.HTML(html2) r = parse_html2.xpath('//div[@class="entry"]/p/text()')
  6、創(chuàng )建csv文件進(jìn)行寫(xiě)入,定義對應的標題頭內容,保存數據 。
   # 創(chuàng )建csv文件進(jìn)行寫(xiě)入 csv_file = open('scr.csv', 'a', encoding='gbk') csv_writer = csv.writer(csv_file) # 寫(xiě)入csv標題頭內容 csv_writerr.writerow(['電影', '評分', "詳情頁(yè)"]) #寫(xiě)入數據 csv_writer.writerow([id, rate, urll])
  7、圖片地址進(jìn)行請求。定義圖片名稱(chēng),保存文檔。
   html2 = requests.get(url=urll, headers=self.headers).content dirname = "./圖/" + id + ".jpg" with open(dirname, 'wb') as f: f.write(html2) print("%s 【下載成功?。。?!】" % id)
  8、調用方法,實(shí)現功能。
   html = self.get_page(url) self.parse_page(html)
  9、項目?jì)?yōu)化:1)設置時(shí)間延時(shí)。
   time.sleep(1.4)
  2)定義一個(gè)變量u, for遍歷,表示爬取的是第幾頁(yè)。(更清晰可觀(guān))。
   u = 0 self.u += 1;
  【六、效果展示】
  1、點(diǎn)擊綠色小三角運行輸入起始頁(yè),終止頁(yè)( 從0頁(yè)開(kāi)始 )。
  
  2、將下載成功信息顯示在控制臺。
  
  3、保存csv文檔。
  
  4、電影圖片展示。
  
  【七、總結】
  1、不建議抓取太多數據,容易對服務(wù)器造成負載,淺嘗輒止即可。
  2、本文章就Python爬取豆瓣網(wǎng),在應用中出現的難點(diǎn)和重點(diǎn),以及如何防止反爬,做出了相對于的解決方案。
  3、希望通過(guò)這個(gè)項目,能夠幫助了解json解析頁(yè)面的基本流程,字符串是如何拼接,format函數如何運用。
  4、本文基于Python網(wǎng)絡(luò )爬蟲(chóng),利用爬蟲(chóng)庫,實(shí)現豆瓣電影及其圖片的獲取。實(shí)現的時(shí)候,總會(huì )有各種各樣的問(wèn)題,切勿眼高手低,勤動(dòng)手,才可以理解的更加深刻。
  5、需要本文源碼的小伙伴,請在下方公眾號后臺回復“豆瓣電影”四個(gè)字,即可獲取。
  看完本文有收獲?請轉發(fā)分享給更多的人
  IT共享之家

文章采集調用 好代碼和壞代碼

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 150 次瀏覽 ? 2022-05-14 06:28 ? 來(lái)自相關(guān)話(huà)題

  文章采集調用 好代碼和壞代碼
  關(guān)注后回復“進(jìn)群”,拉你進(jìn)程序員交流群
  要寫(xiě)出好代碼,首先需要提升品位。
  很多軟件工程師寫(xiě)不好代碼,在評審他人的代碼時(shí)也看不出問(wèn)題,就是因為缺乏對好代碼標準的認識。
  現在還有太多的軟件工程師認為,代碼只要可以正確執行就可以了。這是一種非常低的評價(jià)標準,很多重要的方面都被忽視了。
  好代碼的特性
  好代碼具有以下特性。
  1. 魯棒(Solid and Robust)
  代碼不僅要被正確執行,我們還要考慮對各種錯誤情況的處理,比如各種系統調用和函數調用的異常情況,系統相關(guān)組件的異常和錯誤。
  對很多產(chǎn)品級的程序來(lái)說(shuō),異常和錯誤處理的邏輯占了很大比例。
  2. 高效(Fast)
  程序的運行應使用盡量少的資源。資源不僅僅包括CPU,還可能包括存儲、I/O等。
  設計高效的程序,會(huì )運用到數據結構和算法方面的知識,同時(shí)要考慮到程序運行時(shí)的各種約束條件。
  3. 簡(jiǎn)潔(Maintainable and Simple)
  代碼的邏輯要盡量簡(jiǎn)明易懂,代碼要具有很好的可維護性。對于同樣的目標,能夠使用簡(jiǎn)單清楚的方法達成,就不要使用復雜晦澀的方法。
  “大道至簡(jiǎn)”,能否把復雜的問(wèn)題用簡(jiǎn)單的方式實(shí)現出來(lái),這是一種編程水平的體現。
  4. 簡(jiǎn)短(Small)
  在某種意義上,代碼的復雜度和維護成本是和代碼的規模直接相關(guān)的。在實(shí)現同樣功能的時(shí)候,要盡量將代碼寫(xiě)得簡(jiǎn)短一些。
  簡(jiǎn)潔高于簡(jiǎn)短。這里要注意,某些人為了能把代碼寫(xiě)得簡(jiǎn)短,使用了一些晦澀難懂的描述方式,降低了代碼的可讀性。這種方式是不可取的。
  5. 可測試(Testable)
  代碼的正確性要通過(guò)測試來(lái)保證,尤其是在敏捷的場(chǎng)景下,更需要依賴(lài)可自動(dòng)回歸執行的測試用例。
  在代碼的設計中,要考慮如何使代碼可測、易測。一個(gè)比較好的實(shí)踐是使用TDD(Test-Driven Development,測試驅動(dòng)開(kāi)發(fā))的方法,這樣在編寫(xiě)測試用例的時(shí)候會(huì )很快發(fā)現代碼在可測試性方面的問(wèn)題。
  6. 共享(Re-Usable)
  大量的程序實(shí)際上都使用了類(lèi)似的框架或邏輯。由于目前開(kāi)源代碼的大量普及,很多功能并不需要重復開(kāi)發(fā),只進(jìn)行引用和使用即可。
  在一個(gè)組織內部,應鼓勵共享和重用代碼,這樣可以有效降低代碼研發(fā)的成本,并提升代碼的質(zhì)量。
  實(shí)現代碼的共享,不僅需要在意識方面提升,還需要具有相關(guān)的能力(如編寫(xiě)獨立、高質(zhì)量的代碼庫)及相關(guān)基礎設施的支持(如代碼搜索、代碼引用機制)。
  7. 可移植(Portable)
  某些程序需要在多種操作系統下運行,在這種情況下,代碼的可移植性成為一種必需的能力。
  要讓代碼具有可移植性,需要對所運行的各種操作系統底層有充分的理解和統一抽象。一般會(huì )使用一個(gè)適配層來(lái)屏蔽操作系統底層的差異。
  一些編程語(yǔ)言也提供了多操作系統的可移植性,如很多基于Python語(yǔ)言、Java語(yǔ)言、Go語(yǔ)言編寫(xiě)的程序,都可以跨平臺運行。
  8. 可觀(guān)測(Observable)/ 可監控(Monitorable)
  面對目前大量存在的在線(xiàn)服務(wù)(Online Service)程序,需要具備對程序的運行狀態(tài)進(jìn)行細致而持續監控的能力。
  這要求在程序設計時(shí)就提供相關(guān)的機制,包括程序狀態(tài)的收集、保存和對外輸出。
  9. 可運維(Operational)
  可運維已經(jīng)成為軟件研發(fā)活動(dòng)的重要組成部分,可運維重點(diǎn)關(guān)注成本、效率和穩定性三個(gè)方面。
  程序的可運維性和程序的設計、編寫(xiě)緊密相關(guān),如果在程序設計階段就沒(méi)有考慮可運維性,那么程序運行的運維目標則難以達成。
  10. 可擴展(Scalable andExtensible)
  可擴展包含“容量可擴展”(Scalable)和“功能可擴展”(Extensible)兩方面。
  在互聯(lián)網(wǎng)公司的系統設計中,“容量可擴展”是重要的設計目標之一。系統要盡量支持通過(guò)增加資源來(lái)實(shí)現容量的線(xiàn)性提高。
  快速響應需求的變化,是互聯(lián)網(wǎng)公司的另外一個(gè)重要挑戰??煽紤]使用插件式的程序設計方式,以容納未來(lái)可能新增的功能,也可考慮使用類(lèi)似Protocol Buffer 這樣的工具,支持對協(xié)議新增字段。
  以上十條標準,如果要記住,可能有些困難。我們可以把它們歸納為四個(gè)方面,見(jiàn)表1。
  表1對一流代碼特性的匯總分類(lèi)
  
  壞代碼的例子
  關(guān)于好代碼,上面介紹了一些特性,本節也給出壞代碼(Bad Code)的幾個(gè)例子。關(guān)于壞代碼,本書(shū)沒(méi)有做系統性總結,只是希望通過(guò)以下這些例子的展示讓讀者對壞代碼有直觀(guān)的感覺(jué)。
  1.不好的函數名稱(chēng)(Bad Function Name)
  如do(),這樣的函數名稱(chēng)沒(méi)有多少信息量;又如myFunc(),這樣的函數名稱(chēng),個(gè)人色彩過(guò)于強烈,也沒(méi)有足夠的信息量。
  2.不好的變量名稱(chēng)(Bad Variable Name)
  如a、b、c、i、j、k、temp,這樣的變量名稱(chēng)在很多教科書(shū)中經(jīng)常出現,很多人在上學(xué)期間寫(xiě)代碼時(shí)也會(huì )經(jīng)常這樣用。如果作為局部變量,這樣的名稱(chēng)有時(shí)是可以接受的;但如果作為作用域稍微大的變量,這樣的名稱(chēng)就非常不可取了。
  3.沒(méi)有注釋?zhuān)∟o Comments)
  有寫(xiě)注釋習慣的軟件工程師很少,很多軟件工程師認為寫(xiě)注釋是浪費時(shí)間,是“額外”的工作。但是沒(méi)有注釋的代碼,閱讀的成本會(huì )比較高。
  4.函數不是單一目的(The Function has No Single Purpose)
  如LoadFromFileAndCalculate()。這個(gè)例子是我編造的,但現實(shí)中這樣的函數其實(shí)不少。很多函數在首次寫(xiě)出來(lái)的時(shí)候,就很難表述清楚其用途;還有一些函數隨著(zhù)功能的擴展,變得越來(lái)越龐雜,也就慢慢地說(shuō)不清它的目的了。
  這方面的問(wèn)題可能很多人都沒(méi)有充分地認識到——非單一目的的函數難以維護,也難以復用。
  5.不好的排版(Bad Layout)
  不少人認為,程序可以正常執行就行了,所以一些軟件工程師不重視對代碼的排版,認為這僅僅是一種“形式”。
  沒(méi)有排好版的程序,在閱讀效率方面會(huì )帶來(lái)嚴重問(wèn)題。這里舉一個(gè)極端的例子:對于C語(yǔ)言來(lái)說(shuō),“;”可作為語(yǔ)句的分割符,而“縮進(jìn)”和“換行”對于編譯器來(lái)說(shuō)是無(wú)用的,所以完全可以把一段C語(yǔ)言程序都“壓縮”在一行內。這樣的程序是可以運行的,但是對人來(lái)說(shuō),可讀性非常差。這樣的程序肯定是我們非常不希望看到的。
  6.無(wú)法測試(None Testable)
  程序的正確性要依賴(lài)測試來(lái)保證(雖然測試并不能保證程序完全無(wú)錯)。無(wú)法或不好為之編寫(xiě)測試用例的程序,是很難有質(zhì)量保證的。
  好代碼從哪里來(lái)
  上一節說(shuō)明了好代碼的特性,本節來(lái)分析好代碼是如何產(chǎn)出的。
  ▊ 好代碼不止于編碼
  好代碼從哪里來(lái)?
  對于這個(gè)問(wèn)題,很多讀者肯定會(huì )說(shuō):“好代碼肯定是寫(xiě)出來(lái)的呀?!?
  我曾做過(guò)多次調研,發(fā)現很多軟件工程師日常所讀的書(shū)確實(shí)是和“寫(xiě)代碼”緊密相關(guān)的。
  但是,這里要告訴讀者的是,代碼不只是“寫(xiě)”出來(lái)的。在很多年前,我所讀的軟件工程方面的教科書(shū)就告訴我,編碼的時(shí)間一般只占一個(gè)項目所花時(shí)間的 10%。我曾說(shuō)過(guò)一句比較有趣的話(huà):
  “如果一個(gè)從業(yè)者告訴你,他的大部分時(shí)間都在寫(xiě)代碼,那么他大概率不是一個(gè)高級軟件工程師?!?
  那么,軟件工程師的時(shí)間都花到哪里去了呢?軟件工程師的時(shí)間應該花在哪里呢?
  好的代碼是多個(gè)工作環(huán)節的綜合結果。
 ?。?)在編碼前,需要做好需求分析和系統設計。而這兩項工作是經(jīng)常被大量軟件工程師忽略或輕視的環(huán)節。
 ?。?)在編碼時(shí),需要編寫(xiě)代碼和編寫(xiě)單元測試。對于“編寫(xiě)代碼”,讀者都了解;而對于“編寫(xiě)單元測試”,有些軟件工程師就不認同了,甚至還有人誤以為單元測試是由測試工程師來(lái)編寫(xiě)的。
 ?。?)在編碼后,要做集成測試、上線(xiàn),以及持續運營(yíng)/迭代改進(jìn)。這幾件事情都是要花費不少精力的,比如上線(xiàn),不僅僅要做程序部署,而且要考慮程序是如何被監控的。有時(shí),為了一段程序的上線(xiàn),設計和實(shí)施監控的方案要花費好幾天才能完成。
  因此,一個(gè)好的系統或產(chǎn)品是以上這些環(huán)節持續循環(huán)執行的結果。
  ▊ 需求分析和系統設計
  1.幾種常見(jiàn)的錯誤現象
  相對于編碼工作,需求分析和系統設計是兩個(gè)經(jīng)常被忽視的環(huán)節。在現實(shí)工作中,我們經(jīng)常會(huì )看到以下這些現象。
 ?。?)很多人錯誤地認為,寫(xiě)代碼才是最重要的事情。不少軟件工程師如果一天沒(méi)有寫(xiě)出幾行代碼,就會(huì )認為工作沒(méi)有進(jìn)展;很多管理者也會(huì )以代碼的產(chǎn)出量作為衡量工作結果的主要標準,催促軟件工程師盡早開(kāi)始寫(xiě)代碼。
 ?。?)有太多的從業(yè)者,在沒(méi)有搞清楚項目目標之前就已經(jīng)開(kāi)始編碼了。在很多時(shí)候,項目目標都是通過(guò)并不準確的口頭溝通來(lái)確定的。例如:
  “需要做什么?”
  “就按照×××網(wǎng)站的做一個(gè)吧?!?
 ?。?)有太多的從業(yè)者,在代碼編寫(xiě)基本完成后,才發(fā)現設計思路是有問(wèn)題的。他們在很多項目上花費很少(甚至沒(méi)有花費)時(shí)間進(jìn)行系統設計,對于在設計中所隱藏的問(wèn)題并沒(méi)有仔細思考和求證?;谶@樣的設計投入和設計質(zhì)量,項目出現設計失誤也是很難避免的。而面對一個(gè)已經(jīng)完成了基本編碼的項目,如果要“動(dòng)大手術(shù)”來(lái)修改它,相信每個(gè)有過(guò)類(lèi)似經(jīng)歷的人都一定深知那種感受——越改越亂,越改越著(zhù)急。
  以上這幾種情況,很多讀者是不是都有過(guò)類(lèi)似經(jīng)歷?
  2.研發(fā)前期多投入,收益更大
  關(guān)于軟件研發(fā),首先我們需要建立一個(gè)非常重要的觀(guān)念。
  在研發(fā)前期(需求分析和系統設計)多投入資源,相對于把資源都投入在研發(fā)后期(編碼、測試等),其收益更大。
  這是為什么呢?
  要回答這個(gè)問(wèn)題,需要從軟件研發(fā)全生命周期的角度來(lái)考量軟件研發(fā)的成本。除編碼外,軟件測試、上線(xiàn)、調試等都需要很高成本。如果我們把需求搞錯了,那么與錯誤需求有關(guān)的設計、編碼、測試、上線(xiàn)等成本就都浪費了;如果我們把設計搞錯了,那么與錯誤設計相關(guān)的編碼、測試、上線(xiàn)的成本也就浪費了。
  如果仔細考量那些低效的項目,會(huì )發(fā)現有非常多的類(lèi)似于上面提到的“浪費”的地方。軟件工程師似乎都很忙,但是在錯誤方向上所做的所有努力并不會(huì )產(chǎn)生任何價(jià)值,而大部分的加班實(shí)際上是在做錯誤的事情,或者是為了補救錯誤而努力。在這種情況下,將更多的資源和注意力向研發(fā)前期傾斜會(huì )立刻收到良好的效果。
  3.修改代碼和修改文檔,哪個(gè)成本更高
  很多軟件工程師不愿意做需求分析和系統設計,是因為對“寫(xiě)文檔”有著(zhù)根深蒂固的偏見(jiàn)。這里問(wèn)大家一個(gè)問(wèn)題,如果大家對這個(gè)問(wèn)題能給出正確的回答,那么在“寫(xiě)文檔”的意識方面,一定會(huì )有很大的轉變。
  任何人都不是神仙,無(wú)法一次就把所有事情做對。對于一段程序來(lái)說(shuō),它一定要經(jīng)過(guò)一定周期的修改和迭代。這時(shí)有兩種選擇:
  選擇一:修改文檔。在設計文檔時(shí)完成迭代調整,待沒(méi)有大問(wèn)題后再開(kāi)始編碼。
  選擇二:修改代碼。只有粗略的設計文檔,或者沒(méi)有設計文檔,直接開(kāi)始編碼,所有的迭代調整都在代碼上完成。
  請大家判斷,修改代碼和修改文檔,哪個(gè)成本更高?
  在之前的一些分享交流會(huì )上,對于這個(gè)問(wèn)題,有人會(huì )說(shuō),修改文檔的成本更高。因為在修改文檔后還要修改代碼,多了一道手續。而直接修改代碼,只需要做一次,這樣更直接。
  這個(gè)回答說(shuō)明了回答者沒(méi)有充分理解“先寫(xiě)文檔,后寫(xiě)代碼”的設計方法。如果沒(méi)有充分重視設計文檔的工作,在輸出的設計文檔質(zhì)量不高的情況下就開(kāi)始編碼,確實(shí)會(huì )出現以上提到的問(wèn)題。但是,如果在設計文檔階段就已經(jīng)做了充分考慮,會(huì )減少對代碼的迭代和反復。
  對于同樣的設計修改,“修改代碼”的成本遠高于“修改文檔”。這是因為,在設計文檔中只會(huì )涉及主要的邏輯,那些細小的、顯而易見(jiàn)的邏輯不會(huì )在設計文檔中出現。在修改設計文檔時(shí),也只會(huì )影響到這些主要邏輯。而如果在代碼中做修改,不僅會(huì )涉及這些主要邏輯,而且會(huì )涉及那些在文檔中不會(huì )出現的細小邏輯。對于一段程序來(lái)說(shuō),任何一個(gè)邏輯出現問(wèn)題,程序都是無(wú)法正常運行的。
  4.需求分析和系統設計之間的差別
  很多讀者無(wú)法清楚地區分“需求分析”和“系統設計”之間的差別,于是會(huì )發(fā)現,在寫(xiě)出的文檔中,有些需求分析文檔里出現了系統設計的內容,而有些系統設計文檔里又混雜了需求分析的內容。
  我們用幾句話(huà)可以非常明確地給出二者的差異。
 ?。?)需求分析:定義系統/軟件的黑盒的行為,它是從外部(External)看到的,在說(shuō)明“是什么”(What)。
 ?。?)系統設計:設計系統/軟件的白盒的機制,它是從內部(Internal)看到的,要說(shuō)明“怎么做”(How)和“為什么”(Why)。
  比如,對一輛汽車(chē)來(lái)說(shuō),首先使用者從外部可以看到車(chē)廂、車(chē)輪,坐在車(chē)里可以看到方向盤(pán)、剎車(chē)踏板、油門(mén)踏板等;操作方向盤(pán)可以改變汽車(chē)的行駛方向,腳踩剎車(chē)踏板、油門(mén)踏板可用于減速和加速。以上這些是對汽車(chē)的“需求分析”。
  然后,我們想象汽車(chē)外殼和內部變成了透明的,可以看到汽車(chē)內部的發(fā)動(dòng)機、變速箱、傳動(dòng)桿、與剎車(chē)相關(guān)的內部裝置等。而這些對駕駛者來(lái)說(shuō)是不可見(jiàn)的,它們是對汽車(chē)的“系統設計”。
  -End-
  最近有一些小伙伴,讓我幫忙找一些面試題資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!
  
  點(diǎn)擊卡片,關(guān)注后回復【面試題】即可獲取
  在看點(diǎn)這里
   查看全部

  文章采集調用 好代碼和壞代碼
  關(guān)注后回復“進(jìn)群”,拉你進(jìn)程序員交流群
  要寫(xiě)出好代碼,首先需要提升品位。
  很多軟件工程師寫(xiě)不好代碼,在評審他人的代碼時(shí)也看不出問(wèn)題,就是因為缺乏對好代碼標準的認識。
  現在還有太多的軟件工程師認為,代碼只要可以正確執行就可以了。這是一種非常低的評價(jià)標準,很多重要的方面都被忽視了。
  好代碼的特性
  好代碼具有以下特性。
  1. 魯棒(Solid and Robust)
  代碼不僅要被正確執行,我們還要考慮對各種錯誤情況的處理,比如各種系統調用和函數調用的異常情況,系統相關(guān)組件的異常和錯誤。
  對很多產(chǎn)品級的程序來(lái)說(shuō),異常和錯誤處理的邏輯占了很大比例。
  2. 高效(Fast)
  程序的運行應使用盡量少的資源。資源不僅僅包括CPU,還可能包括存儲、I/O等。
  設計高效的程序,會(huì )運用到數據結構和算法方面的知識,同時(shí)要考慮到程序運行時(shí)的各種約束條件。
  3. 簡(jiǎn)潔(Maintainable and Simple)
  代碼的邏輯要盡量簡(jiǎn)明易懂,代碼要具有很好的可維護性。對于同樣的目標,能夠使用簡(jiǎn)單清楚的方法達成,就不要使用復雜晦澀的方法。
  “大道至簡(jiǎn)”,能否把復雜的問(wèn)題用簡(jiǎn)單的方式實(shí)現出來(lái),這是一種編程水平的體現。
  4. 簡(jiǎn)短(Small)
  在某種意義上,代碼的復雜度和維護成本是和代碼的規模直接相關(guān)的。在實(shí)現同樣功能的時(shí)候,要盡量將代碼寫(xiě)得簡(jiǎn)短一些。
  簡(jiǎn)潔高于簡(jiǎn)短。這里要注意,某些人為了能把代碼寫(xiě)得簡(jiǎn)短,使用了一些晦澀難懂的描述方式,降低了代碼的可讀性。這種方式是不可取的。
  5. 可測試(Testable)
  代碼的正確性要通過(guò)測試來(lái)保證,尤其是在敏捷的場(chǎng)景下,更需要依賴(lài)可自動(dòng)回歸執行的測試用例。
  在代碼的設計中,要考慮如何使代碼可測、易測。一個(gè)比較好的實(shí)踐是使用TDD(Test-Driven Development,測試驅動(dòng)開(kāi)發(fā))的方法,這樣在編寫(xiě)測試用例的時(shí)候會(huì )很快發(fā)現代碼在可測試性方面的問(wèn)題。
  6. 共享(Re-Usable)
  大量的程序實(shí)際上都使用了類(lèi)似的框架或邏輯。由于目前開(kāi)源代碼的大量普及,很多功能并不需要重復開(kāi)發(fā),只進(jìn)行引用和使用即可。
  在一個(gè)組織內部,應鼓勵共享和重用代碼,這樣可以有效降低代碼研發(fā)的成本,并提升代碼的質(zhì)量。
  實(shí)現代碼的共享,不僅需要在意識方面提升,還需要具有相關(guān)的能力(如編寫(xiě)獨立、高質(zhì)量的代碼庫)及相關(guān)基礎設施的支持(如代碼搜索、代碼引用機制)。
  7. 可移植(Portable)
  某些程序需要在多種操作系統下運行,在這種情況下,代碼的可移植性成為一種必需的能力。
  要讓代碼具有可移植性,需要對所運行的各種操作系統底層有充分的理解和統一抽象。一般會(huì )使用一個(gè)適配層來(lái)屏蔽操作系統底層的差異。
  一些編程語(yǔ)言也提供了多操作系統的可移植性,如很多基于Python語(yǔ)言、Java語(yǔ)言、Go語(yǔ)言編寫(xiě)的程序,都可以跨平臺運行。
  8. 可觀(guān)測(Observable)/ 可監控(Monitorable)
  面對目前大量存在的在線(xiàn)服務(wù)(Online Service)程序,需要具備對程序的運行狀態(tài)進(jìn)行細致而持續監控的能力。
  這要求在程序設計時(shí)就提供相關(guān)的機制,包括程序狀態(tài)的收集、保存和對外輸出。
  9. 可運維(Operational)
  可運維已經(jīng)成為軟件研發(fā)活動(dòng)的重要組成部分,可運維重點(diǎn)關(guān)注成本、效率和穩定性三個(gè)方面。
  程序的可運維性和程序的設計、編寫(xiě)緊密相關(guān),如果在程序設計階段就沒(méi)有考慮可運維性,那么程序運行的運維目標則難以達成。
  10. 可擴展(Scalable andExtensible)
  可擴展包含“容量可擴展”(Scalable)和“功能可擴展”(Extensible)兩方面。
  在互聯(lián)網(wǎng)公司的系統設計中,“容量可擴展”是重要的設計目標之一。系統要盡量支持通過(guò)增加資源來(lái)實(shí)現容量的線(xiàn)性提高。
  快速響應需求的變化,是互聯(lián)網(wǎng)公司的另外一個(gè)重要挑戰??煽紤]使用插件式的程序設計方式,以容納未來(lái)可能新增的功能,也可考慮使用類(lèi)似Protocol Buffer 這樣的工具,支持對協(xié)議新增字段。
  以上十條標準,如果要記住,可能有些困難。我們可以把它們歸納為四個(gè)方面,見(jiàn)表1。
  表1對一流代碼特性的匯總分類(lèi)
  
  壞代碼的例子
  關(guān)于好代碼,上面介紹了一些特性,本節也給出壞代碼(Bad Code)的幾個(gè)例子。關(guān)于壞代碼,本書(shū)沒(méi)有做系統性總結,只是希望通過(guò)以下這些例子的展示讓讀者對壞代碼有直觀(guān)的感覺(jué)。
  1.不好的函數名稱(chēng)(Bad Function Name)
  如do(),這樣的函數名稱(chēng)沒(méi)有多少信息量;又如myFunc(),這樣的函數名稱(chēng),個(gè)人色彩過(guò)于強烈,也沒(méi)有足夠的信息量。
  2.不好的變量名稱(chēng)(Bad Variable Name)
  如a、b、c、i、j、k、temp,這樣的變量名稱(chēng)在很多教科書(shū)中經(jīng)常出現,很多人在上學(xué)期間寫(xiě)代碼時(shí)也會(huì )經(jīng)常這樣用。如果作為局部變量,這樣的名稱(chēng)有時(shí)是可以接受的;但如果作為作用域稍微大的變量,這樣的名稱(chēng)就非常不可取了。
  3.沒(méi)有注釋?zhuān)∟o Comments)
  有寫(xiě)注釋習慣的軟件工程師很少,很多軟件工程師認為寫(xiě)注釋是浪費時(shí)間,是“額外”的工作。但是沒(méi)有注釋的代碼,閱讀的成本會(huì )比較高。
  4.函數不是單一目的(The Function has No Single Purpose)
  如LoadFromFileAndCalculate()。這個(gè)例子是我編造的,但現實(shí)中這樣的函數其實(shí)不少。很多函數在首次寫(xiě)出來(lái)的時(shí)候,就很難表述清楚其用途;還有一些函數隨著(zhù)功能的擴展,變得越來(lái)越龐雜,也就慢慢地說(shuō)不清它的目的了。
  這方面的問(wèn)題可能很多人都沒(méi)有充分地認識到——非單一目的的函數難以維護,也難以復用。
  5.不好的排版(Bad Layout)
  不少人認為,程序可以正常執行就行了,所以一些軟件工程師不重視對代碼的排版,認為這僅僅是一種“形式”。
  沒(méi)有排好版的程序,在閱讀效率方面會(huì )帶來(lái)嚴重問(wèn)題。這里舉一個(gè)極端的例子:對于C語(yǔ)言來(lái)說(shuō),“;”可作為語(yǔ)句的分割符,而“縮進(jìn)”和“換行”對于編譯器來(lái)說(shuō)是無(wú)用的,所以完全可以把一段C語(yǔ)言程序都“壓縮”在一行內。這樣的程序是可以運行的,但是對人來(lái)說(shuō),可讀性非常差。這樣的程序肯定是我們非常不希望看到的。
  6.無(wú)法測試(None Testable)
  程序的正確性要依賴(lài)測試來(lái)保證(雖然測試并不能保證程序完全無(wú)錯)。無(wú)法或不好為之編寫(xiě)測試用例的程序,是很難有質(zhì)量保證的。
  好代碼從哪里來(lái)
  上一節說(shuō)明了好代碼的特性,本節來(lái)分析好代碼是如何產(chǎn)出的。
  ▊ 好代碼不止于編碼
  好代碼從哪里來(lái)?
  對于這個(gè)問(wèn)題,很多讀者肯定會(huì )說(shuō):“好代碼肯定是寫(xiě)出來(lái)的呀?!?
  我曾做過(guò)多次調研,發(fā)現很多軟件工程師日常所讀的書(shū)確實(shí)是和“寫(xiě)代碼”緊密相關(guān)的。
  但是,這里要告訴讀者的是,代碼不只是“寫(xiě)”出來(lái)的。在很多年前,我所讀的軟件工程方面的教科書(shū)就告訴我,編碼的時(shí)間一般只占一個(gè)項目所花時(shí)間的 10%。我曾說(shuō)過(guò)一句比較有趣的話(huà):
  “如果一個(gè)從業(yè)者告訴你,他的大部分時(shí)間都在寫(xiě)代碼,那么他大概率不是一個(gè)高級軟件工程師?!?
  那么,軟件工程師的時(shí)間都花到哪里去了呢?軟件工程師的時(shí)間應該花在哪里呢?
  好的代碼是多個(gè)工作環(huán)節的綜合結果。
 ?。?)在編碼前,需要做好需求分析和系統設計。而這兩項工作是經(jīng)常被大量軟件工程師忽略或輕視的環(huán)節。
 ?。?)在編碼時(shí),需要編寫(xiě)代碼和編寫(xiě)單元測試。對于“編寫(xiě)代碼”,讀者都了解;而對于“編寫(xiě)單元測試”,有些軟件工程師就不認同了,甚至還有人誤以為單元測試是由測試工程師來(lái)編寫(xiě)的。
 ?。?)在編碼后,要做集成測試、上線(xiàn),以及持續運營(yíng)/迭代改進(jìn)。這幾件事情都是要花費不少精力的,比如上線(xiàn),不僅僅要做程序部署,而且要考慮程序是如何被監控的。有時(shí),為了一段程序的上線(xiàn),設計和實(shí)施監控的方案要花費好幾天才能完成。
  因此,一個(gè)好的系統或產(chǎn)品是以上這些環(huán)節持續循環(huán)執行的結果。
  ▊ 需求分析和系統設計
  1.幾種常見(jiàn)的錯誤現象
  相對于編碼工作,需求分析和系統設計是兩個(gè)經(jīng)常被忽視的環(huán)節。在現實(shí)工作中,我們經(jīng)常會(huì )看到以下這些現象。
 ?。?)很多人錯誤地認為,寫(xiě)代碼才是最重要的事情。不少軟件工程師如果一天沒(méi)有寫(xiě)出幾行代碼,就會(huì )認為工作沒(méi)有進(jìn)展;很多管理者也會(huì )以代碼的產(chǎn)出量作為衡量工作結果的主要標準,催促軟件工程師盡早開(kāi)始寫(xiě)代碼。
 ?。?)有太多的從業(yè)者,在沒(méi)有搞清楚項目目標之前就已經(jīng)開(kāi)始編碼了。在很多時(shí)候,項目目標都是通過(guò)并不準確的口頭溝通來(lái)確定的。例如:
  “需要做什么?”
  “就按照×××網(wǎng)站的做一個(gè)吧?!?
 ?。?)有太多的從業(yè)者,在代碼編寫(xiě)基本完成后,才發(fā)現設計思路是有問(wèn)題的。他們在很多項目上花費很少(甚至沒(méi)有花費)時(shí)間進(jìn)行系統設計,對于在設計中所隱藏的問(wèn)題并沒(méi)有仔細思考和求證?;谶@樣的設計投入和設計質(zhì)量,項目出現設計失誤也是很難避免的。而面對一個(gè)已經(jīng)完成了基本編碼的項目,如果要“動(dòng)大手術(shù)”來(lái)修改它,相信每個(gè)有過(guò)類(lèi)似經(jīng)歷的人都一定深知那種感受——越改越亂,越改越著(zhù)急。
  以上這幾種情況,很多讀者是不是都有過(guò)類(lèi)似經(jīng)歷?
  2.研發(fā)前期多投入,收益更大
  關(guān)于軟件研發(fā),首先我們需要建立一個(gè)非常重要的觀(guān)念。
  在研發(fā)前期(需求分析和系統設計)多投入資源,相對于把資源都投入在研發(fā)后期(編碼、測試等),其收益更大。
  這是為什么呢?
  要回答這個(gè)問(wèn)題,需要從軟件研發(fā)全生命周期的角度來(lái)考量軟件研發(fā)的成本。除編碼外,軟件測試、上線(xiàn)、調試等都需要很高成本。如果我們把需求搞錯了,那么與錯誤需求有關(guān)的設計、編碼、測試、上線(xiàn)等成本就都浪費了;如果我們把設計搞錯了,那么與錯誤設計相關(guān)的編碼、測試、上線(xiàn)的成本也就浪費了。
  如果仔細考量那些低效的項目,會(huì )發(fā)現有非常多的類(lèi)似于上面提到的“浪費”的地方。軟件工程師似乎都很忙,但是在錯誤方向上所做的所有努力并不會(huì )產(chǎn)生任何價(jià)值,而大部分的加班實(shí)際上是在做錯誤的事情,或者是為了補救錯誤而努力。在這種情況下,將更多的資源和注意力向研發(fā)前期傾斜會(huì )立刻收到良好的效果。
  3.修改代碼和修改文檔,哪個(gè)成本更高
  很多軟件工程師不愿意做需求分析和系統設計,是因為對“寫(xiě)文檔”有著(zhù)根深蒂固的偏見(jiàn)。這里問(wèn)大家一個(gè)問(wèn)題,如果大家對這個(gè)問(wèn)題能給出正確的回答,那么在“寫(xiě)文檔”的意識方面,一定會(huì )有很大的轉變。
  任何人都不是神仙,無(wú)法一次就把所有事情做對。對于一段程序來(lái)說(shuō),它一定要經(jīng)過(guò)一定周期的修改和迭代。這時(shí)有兩種選擇:
  選擇一:修改文檔。在設計文檔時(shí)完成迭代調整,待沒(méi)有大問(wèn)題后再開(kāi)始編碼。
  選擇二:修改代碼。只有粗略的設計文檔,或者沒(méi)有設計文檔,直接開(kāi)始編碼,所有的迭代調整都在代碼上完成。
  請大家判斷,修改代碼和修改文檔,哪個(gè)成本更高?
  在之前的一些分享交流會(huì )上,對于這個(gè)問(wèn)題,有人會(huì )說(shuō),修改文檔的成本更高。因為在修改文檔后還要修改代碼,多了一道手續。而直接修改代碼,只需要做一次,這樣更直接。
  這個(gè)回答說(shuō)明了回答者沒(méi)有充分理解“先寫(xiě)文檔,后寫(xiě)代碼”的設計方法。如果沒(méi)有充分重視設計文檔的工作,在輸出的設計文檔質(zhì)量不高的情況下就開(kāi)始編碼,確實(shí)會(huì )出現以上提到的問(wèn)題。但是,如果在設計文檔階段就已經(jīng)做了充分考慮,會(huì )減少對代碼的迭代和反復。
  對于同樣的設計修改,“修改代碼”的成本遠高于“修改文檔”。這是因為,在設計文檔中只會(huì )涉及主要的邏輯,那些細小的、顯而易見(jiàn)的邏輯不會(huì )在設計文檔中出現。在修改設計文檔時(shí),也只會(huì )影響到這些主要邏輯。而如果在代碼中做修改,不僅會(huì )涉及這些主要邏輯,而且會(huì )涉及那些在文檔中不會(huì )出現的細小邏輯。對于一段程序來(lái)說(shuō),任何一個(gè)邏輯出現問(wèn)題,程序都是無(wú)法正常運行的。
  4.需求分析和系統設計之間的差別
  很多讀者無(wú)法清楚地區分“需求分析”和“系統設計”之間的差別,于是會(huì )發(fā)現,在寫(xiě)出的文檔中,有些需求分析文檔里出現了系統設計的內容,而有些系統設計文檔里又混雜了需求分析的內容。
  我們用幾句話(huà)可以非常明確地給出二者的差異。
 ?。?)需求分析:定義系統/軟件的黑盒的行為,它是從外部(External)看到的,在說(shuō)明“是什么”(What)。
 ?。?)系統設計:設計系統/軟件的白盒的機制,它是從內部(Internal)看到的,要說(shuō)明“怎么做”(How)和“為什么”(Why)。
  比如,對一輛汽車(chē)來(lái)說(shuō),首先使用者從外部可以看到車(chē)廂、車(chē)輪,坐在車(chē)里可以看到方向盤(pán)、剎車(chē)踏板、油門(mén)踏板等;操作方向盤(pán)可以改變汽車(chē)的行駛方向,腳踩剎車(chē)踏板、油門(mén)踏板可用于減速和加速。以上這些是對汽車(chē)的“需求分析”。
  然后,我們想象汽車(chē)外殼和內部變成了透明的,可以看到汽車(chē)內部的發(fā)動(dòng)機、變速箱、傳動(dòng)桿、與剎車(chē)相關(guān)的內部裝置等。而這些對駕駛者來(lái)說(shuō)是不可見(jiàn)的,它們是對汽車(chē)的“系統設計”。
  -End-
  最近有一些小伙伴,讓我幫忙找一些面試題資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!
  
  點(diǎn)擊卡片,關(guān)注后回復【面試題】即可獲取
  在看點(diǎn)這里
  

V8 中的垃圾收集(GC),圖文指南

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 108 次瀏覽 ? 2022-05-14 06:27 ? 來(lái)自相關(guān)話(huà)題

  V8 中的垃圾收集(GC),圖文指南
  原文標題:Garbage collection in V8, an illustrated guide原文鏈接:@_lrlna/garbage-collection-in-v8-an-illustrated-guide-d24a952ee3b8
  本指南與我迄今為止所寫(xiě)的其他指南都不同,我在里面添加了一些草圖。我用草圖描繪了垃圾收集(GC)的整個(gè)概念以及它是如何在 javascript 中被處理的,更確切地說(shuō)是在運行 javascript 的引擎中。順便提一下,這個(gè)指南是面向初學(xué)者的,不包括 V8 內存管理的各個(gè)方面以及 V8 的內部原理。我添加了一些資源,可以幫助你更深入地了解。本指南重點(diǎn)介紹?javascript?,對于某些語(yǔ)言而言,垃圾收集是完全不一樣的,比如 C 語(yǔ)言.
  好的,我們開(kāi)始吧。
  什么是v8?
  V8,是一個(gè) JavaScript 的運行時(shí)引擎,不要與你最喜愛(ài)的番茄汁混淆了,它負責編譯并執行你精美的javascript。V8 帶有分代垃圾收集器,我將在后文解釋。它與 Chrome 一起,而 SpiderMonkey 是 Mozilla 的引擎 Chakra 是微軟的?;旧袭斶\行 javascript 時(shí),您需要一個(gè)引擎來(lái)處理它,而且 V8 是您的選擇之一,無(wú)論是在瀏覽器還是在 node.js 環(huán)境中。(P.S. V8 是? 開(kāi)源的 ?。)
  什么是垃圾收集?
  垃圾收集的重點(diǎn)是通過(guò)使用特定的程序來(lái)管理內存的使用。諸如 C 之類(lèi)的語(yǔ)言通??梢灾苯硬僮鞒绦蛑械膬却?,并在程序的上下文中分配和釋放對象。另一方面,ECMAScript 缺少訪(fǎng)問(wèn)內存管理的特定接口(是的,這意味著(zhù)沒(méi)有API)。這基本上意味著(zhù)程序中的所有內存管理權限都被轉移到了 V8。
  由于我們無(wú)法訪(fǎng)問(wèn)無(wú)限量的內存,因此垃圾收集器的工作是通過(guò)內存中分配的對象來(lái)確定它們是否死亡或是活動(dòng)。那些活著(zhù)的對象會(huì )留在內存中,那些死亡的對象被刪除,內存被分配回堆。
  什么是堆?堆是非結構化區域,堆中的對象占用分配的內存。這種分配是動(dòng)態(tài)的,因為對象的大小/壽命/數量是未知的,所以需要在運行時(shí)分配和釋放。
  如果我們看一下并發(fā)模型,堆直接與調用棧一起工作,因為堆棧中的對象需要進(jìn)行內存分配。它看起來(lái)像這樣:
  
  Dead or alive?
  如何檢查對象的生死,是通過(guò)客戶(hù)機或者程序代碼是否可以到達此對象。您可以想到的最容易達到的對象可能是根范圍中定義的對象。
  一些 C++ 綁定(或客戶(hù)端上的 Web API)也是根的一部分,因此您可以通過(guò)例如 setInterval 直接訪(fǎng)問(wèn)。
  可達性(Reachability)還可以這么理解:另一個(gè)對象或根是否可以獲得它,如果可以的話(huà),該對象所需的內存被保留。
  那么我們怎么可以做到垃圾收集呢?(告訴我!告訴我?。?
  創(chuàng )建新對象或新的“指針”時(shí),V8 會(huì )在堆中分配內存。(javascript 沒(méi)有真正的指針,所以'指針'在技術(shù)上只是復制對原始對象的引用)。堆中的不同類(lèi)型的對象會(huì )占用不同的空間,它將被組織成如下:
  
  為了垃圾回收的目的,V8 將堆分為兩部分:新生區和老生區。當您執行需要 V8 分配內存的操作時(shí),V8 將在新生區中分配空間。當你繼續添加到堆,你最終會(huì )耗盡內存,所以 V8 將不得不運行一個(gè) GC 來(lái)清理。新創(chuàng )建的對象被分配得很快,并且當對象死亡時(shí)被清理(更短和更快的收集)。一旦對象“生存”了一些(確切的說(shuō)是2個(gè)周期)回收掃描周期時(shí),它們被提升到老生區,在一個(gè)單獨的循環(huán)中收集垃圾。
  
  較舊的對象是幸存多于一個(gè)垃圾收集掃描的對象,這意味著(zhù)它們仍然被其他對象引用,并且仍然需要占用該內存。他們通常不引用較年輕的對象,只是引用較舊的對象。大周期進(jìn)行的并不頻繁。一次大周期通常是在移動(dòng)足夠多的對象至老生區后才會(huì )發(fā)生。
  sources.js
  This guide is crossposted from lrlna’s sketchin guide on github ? . 查看全部

  V8 中的垃圾收集(GC),圖文指南
  原文標題:Garbage collection in V8, an illustrated guide原文鏈接:@_lrlna/garbage-collection-in-v8-an-illustrated-guide-d24a952ee3b8
  本指南與我迄今為止所寫(xiě)的其他指南都不同,我在里面添加了一些草圖。我用草圖描繪了垃圾收集(GC)的整個(gè)概念以及它是如何在 javascript 中被處理的,更確切地說(shuō)是在運行 javascript 的引擎中。順便提一下,這個(gè)指南是面向初學(xué)者的,不包括 V8 內存管理的各個(gè)方面以及 V8 的內部原理。我添加了一些資源,可以幫助你更深入地了解。本指南重點(diǎn)介紹?javascript?,對于某些語(yǔ)言而言,垃圾收集是完全不一樣的,比如 C 語(yǔ)言.
  好的,我們開(kāi)始吧。
  什么是v8?
  V8,是一個(gè) JavaScript 的運行時(shí)引擎,不要與你最喜愛(ài)的番茄汁混淆了,它負責編譯并執行你精美的javascript。V8 帶有分代垃圾收集器,我將在后文解釋。它與 Chrome 一起,而 SpiderMonkey 是 Mozilla 的引擎 Chakra 是微軟的?;旧袭斶\行 javascript 時(shí),您需要一個(gè)引擎來(lái)處理它,而且 V8 是您的選擇之一,無(wú)論是在瀏覽器還是在 node.js 環(huán)境中。(P.S. V8 是? 開(kāi)源的 ?。)
  什么是垃圾收集?
  垃圾收集的重點(diǎn)是通過(guò)使用特定的程序來(lái)管理內存的使用。諸如 C 之類(lèi)的語(yǔ)言通??梢灾苯硬僮鞒绦蛑械膬却?,并在程序的上下文中分配和釋放對象。另一方面,ECMAScript 缺少訪(fǎng)問(wèn)內存管理的特定接口(是的,這意味著(zhù)沒(méi)有API)。這基本上意味著(zhù)程序中的所有內存管理權限都被轉移到了 V8。
  由于我們無(wú)法訪(fǎng)問(wèn)無(wú)限量的內存,因此垃圾收集器的工作是通過(guò)內存中分配的對象來(lái)確定它們是否死亡或是活動(dòng)。那些活著(zhù)的對象會(huì )留在內存中,那些死亡的對象被刪除,內存被分配回堆。
  什么是堆?堆是非結構化區域,堆中的對象占用分配的內存。這種分配是動(dòng)態(tài)的,因為對象的大小/壽命/數量是未知的,所以需要在運行時(shí)分配和釋放。
  如果我們看一下并發(fā)模型,堆直接與調用棧一起工作,因為堆棧中的對象需要進(jìn)行內存分配。它看起來(lái)像這樣:
  
  Dead or alive?
  如何檢查對象的生死,是通過(guò)客戶(hù)機或者程序代碼是否可以到達此對象。您可以想到的最容易達到的對象可能是根范圍中定義的對象。
  一些 C++ 綁定(或客戶(hù)端上的 Web API)也是根的一部分,因此您可以通過(guò)例如 setInterval 直接訪(fǎng)問(wèn)。
  可達性(Reachability)還可以這么理解:另一個(gè)對象或根是否可以獲得它,如果可以的話(huà),該對象所需的內存被保留。
  那么我們怎么可以做到垃圾收集呢?(告訴我!告訴我?。?
  創(chuàng )建新對象或新的“指針”時(shí),V8 會(huì )在堆中分配內存。(javascript 沒(méi)有真正的指針,所以'指針'在技術(shù)上只是復制對原始對象的引用)。堆中的不同類(lèi)型的對象會(huì )占用不同的空間,它將被組織成如下:
  
  為了垃圾回收的目的,V8 將堆分為兩部分:新生區和老生區。當您執行需要 V8 分配內存的操作時(shí),V8 將在新生區中分配空間。當你繼續添加到堆,你最終會(huì )耗盡內存,所以 V8 將不得不運行一個(gè) GC 來(lái)清理。新創(chuàng )建的對象被分配得很快,并且當對象死亡時(shí)被清理(更短和更快的收集)。一旦對象“生存”了一些(確切的說(shuō)是2個(gè)周期)回收掃描周期時(shí),它們被提升到老生區,在一個(gè)單獨的循環(huán)中收集垃圾。
  
  較舊的對象是幸存多于一個(gè)垃圾收集掃描的對象,這意味著(zhù)它們仍然被其他對象引用,并且仍然需要占用該內存。他們通常不引用較年輕的對象,只是引用較舊的對象。大周期進(jìn)行的并不頻繁。一次大周期通常是在移動(dòng)足夠多的對象至老生區后才會(huì )發(fā)生。
  sources.js
  This guide is crossposted from lrlna’s sketchin guide on github ? .

文章采集調用 好代碼和壞代碼

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 177 次瀏覽 ? 2022-05-10 05:21 ? 來(lái)自相關(guān)話(huà)題

  文章采集調用 好代碼和壞代碼
  關(guān)注后回復“進(jìn)群”,拉你進(jìn)程序員交流群
  要寫(xiě)出好代碼,首先需要提升品位。
  很多軟件工程師寫(xiě)不好代碼,在評審他人的代碼時(shí)也看不出問(wèn)題,就是因為缺乏對好代碼標準的認識。
  現在還有太多的軟件工程師認為,代碼只要可以正確執行就可以了。這是一種非常低的評價(jià)標準,很多重要的方面都被忽視了。
  好代碼的特性
  好代碼具有以下特性。
  1. 魯棒(Solid and Robust)
  代碼不僅要被正確執行,我們還要考慮對各種錯誤情況的處理,比如各種系統調用和函數調用的異常情況,系統相關(guān)組件的異常和錯誤。
  對很多產(chǎn)品級的程序來(lái)說(shuō),異常和錯誤處理的邏輯占了很大比例。
  2. 高效(Fast)
  程序的運行應使用盡量少的資源。資源不僅僅包括CPU,還可能包括存儲、I/O等。
  設計高效的程序,會(huì )運用到數據結構和算法方面的知識,同時(shí)要考慮到程序運行時(shí)的各種約束條件。
  3. 簡(jiǎn)潔(Maintainable and Simple)
  代碼的邏輯要盡量簡(jiǎn)明易懂,代碼要具有很好的可維護性。對于同樣的目標,能夠使用簡(jiǎn)單清楚的方法達成,就不要使用復雜晦澀的方法。
  “大道至簡(jiǎn)”,能否把復雜的問(wèn)題用簡(jiǎn)單的方式實(shí)現出來(lái),這是一種編程水平的體現。
  4. 簡(jiǎn)短(Small)
  在某種意義上,代碼的復雜度和維護成本是和代碼的規模直接相關(guān)的。在實(shí)現同樣功能的時(shí)候,要盡量將代碼寫(xiě)得簡(jiǎn)短一些。
  簡(jiǎn)潔高于簡(jiǎn)短。這里要注意,某些人為了能把代碼寫(xiě)得簡(jiǎn)短,使用了一些晦澀難懂的描述方式,降低了代碼的可讀性。這種方式是不可取的。
  5. 可測試(Testable)
  代碼的正確性要通過(guò)測試來(lái)保證,尤其是在敏捷的場(chǎng)景下,更需要依賴(lài)可自動(dòng)回歸執行的測試用例。
  在代碼的設計中,要考慮如何使代碼可測、易測。一個(gè)比較好的實(shí)踐是使用TDD(Test-Driven Development,測試驅動(dòng)開(kāi)發(fā))的方法,這樣在編寫(xiě)測試用例的時(shí)候會(huì )很快發(fā)現代碼在可測試性方面的問(wèn)題。
  6. 共享(Re-Usable)
  大量的程序實(shí)際上都使用了類(lèi)似的框架或邏輯。由于目前開(kāi)源代碼的大量普及,很多功能并不需要重復開(kāi)發(fā),只進(jìn)行引用和使用即可。
  在一個(gè)組織內部,應鼓勵共享和重用代碼,這樣可以有效降低代碼研發(fā)的成本,并提升代碼的質(zhì)量。
  實(shí)現代碼的共享,不僅需要在意識方面提升,還需要具有相關(guān)的能力(如編寫(xiě)獨立、高質(zhì)量的代碼庫)及相關(guān)基礎設施的支持(如代碼搜索、代碼引用機制)。
  7. 可移植(Portable)
  某些程序需要在多種操作系統下運行,在這種情況下,代碼的可移植性成為一種必需的能力。
  要讓代碼具有可移植性,需要對所運行的各種操作系統底層有充分的理解和統一抽象。一般會(huì )使用一個(gè)適配層來(lái)屏蔽操作系統底層的差異。
  一些編程語(yǔ)言也提供了多操作系統的可移植性,如很多基于Python語(yǔ)言、Java語(yǔ)言、Go語(yǔ)言編寫(xiě)的程序,都可以跨平臺運行。
  8. 可觀(guān)測(Observable)/ 可監控(Monitorable)
  面對目前大量存在的在線(xiàn)服務(wù)(Online Service)程序,需要具備對程序的運行狀態(tài)進(jìn)行細致而持續監控的能力。
  這要求在程序設計時(shí)就提供相關(guān)的機制,包括程序狀態(tài)的收集、保存和對外輸出。
  9. 可運維(Operational)
  可運維已經(jīng)成為軟件研發(fā)活動(dòng)的重要組成部分,可運維重點(diǎn)關(guān)注成本、效率和穩定性三個(gè)方面。
  程序的可運維性和程序的設計、編寫(xiě)緊密相關(guān),如果在程序設計階段就沒(méi)有考慮可運維性,那么程序運行的運維目標則難以達成。
  10. 可擴展(Scalable andExtensible)
  可擴展包含“容量可擴展”(Scalable)和“功能可擴展”(Extensible)兩方面。
  在互聯(lián)網(wǎng)公司的系統設計中,“容量可擴展”是重要的設計目標之一。系統要盡量支持通過(guò)增加資源來(lái)實(shí)現容量的線(xiàn)性提高。
  快速響應需求的變化,是互聯(lián)網(wǎng)公司的另外一個(gè)重要挑戰??煽紤]使用插件式的程序設計方式,以容納未來(lái)可能新增的功能,也可考慮使用類(lèi)似Protocol Buffer 這樣的工具,支持對協(xié)議新增字段。
  以上十條標準,如果要記住,可能有些困難。我們可以把它們歸納為四個(gè)方面,見(jiàn)表1。
  表1對一流代碼特性的匯總分類(lèi)
  
  壞代碼的例子
  關(guān)于好代碼,上面介紹了一些特性,本節也給出壞代碼(Bad Code)的幾個(gè)例子。關(guān)于壞代碼,本書(shū)沒(méi)有做系統性總結,只是希望通過(guò)以下這些例子的展示讓讀者對壞代碼有直觀(guān)的感覺(jué)。
  1.不好的函數名稱(chēng)(Bad Function Name)
  如do(),這樣的函數名稱(chēng)沒(méi)有多少信息量;又如myFunc(),這樣的函數名稱(chēng),個(gè)人色彩過(guò)于強烈,也沒(méi)有足夠的信息量。
  2.不好的變量名稱(chēng)(Bad Variable Name)
  如a、b、c、i、j、k、temp,這樣的變量名稱(chēng)在很多教科書(shū)中經(jīng)常出現,很多人在上學(xué)期間寫(xiě)代碼時(shí)也會(huì )經(jīng)常這樣用。如果作為局部變量,這樣的名稱(chēng)有時(shí)是可以接受的;但如果作為作用域稍微大的變量,這樣的名稱(chēng)就非常不可取了。
  3.沒(méi)有注釋?zhuān)∟o Comments)
  有寫(xiě)注釋習慣的軟件工程師很少,很多軟件工程師認為寫(xiě)注釋是浪費時(shí)間,是“額外”的工作。但是沒(méi)有注釋的代碼,閱讀的成本會(huì )比較高。
  4.函數不是單一目的(The Function has No Single Purpose)
  如LoadFromFileAndCalculate()。這個(gè)例子是我編造的,但現實(shí)中這樣的函數其實(shí)不少。很多函數在首次寫(xiě)出來(lái)的時(shí)候,就很難表述清楚其用途;還有一些函數隨著(zhù)功能的擴展,變得越來(lái)越龐雜,也就慢慢地說(shuō)不清它的目的了。
  這方面的問(wèn)題可能很多人都沒(méi)有充分地認識到——非單一目的的函數難以維護,也難以復用。
  5.不好的排版(Bad Layout)
  不少人認為,程序可以正常執行就行了,所以一些軟件工程師不重視對代碼的排版,認為這僅僅是一種“形式”。
  沒(méi)有排好版的程序,在閱讀效率方面會(huì )帶來(lái)嚴重問(wèn)題。這里舉一個(gè)極端的例子:對于C語(yǔ)言來(lái)說(shuō),“;”可作為語(yǔ)句的分割符,而“縮進(jìn)”和“換行”對于編譯器來(lái)說(shuō)是無(wú)用的,所以完全可以把一段C語(yǔ)言程序都“壓縮”在一行內。這樣的程序是可以運行的,但是對人來(lái)說(shuō),可讀性非常差。這樣的程序肯定是我們非常不希望看到的。
  6.無(wú)法測試(None Testable)
  程序的正確性要依賴(lài)測試來(lái)保證(雖然測試并不能保證程序完全無(wú)錯)。無(wú)法或不好為之編寫(xiě)測試用例的程序,是很難有質(zhì)量保證的。
  好代碼從哪里來(lái)
  上一節說(shuō)明了好代碼的特性,本節來(lái)分析好代碼是如何產(chǎn)出的。
  ▊ 好代碼不止于編碼
  好代碼從哪里來(lái)?
  對于這個(gè)問(wèn)題,很多讀者肯定會(huì )說(shuō):“好代碼肯定是寫(xiě)出來(lái)的呀?!?
  我曾做過(guò)多次調研,發(fā)現很多軟件工程師日常所讀的書(shū)確實(shí)是和“寫(xiě)代碼”緊密相關(guān)的。
  但是,這里要告訴讀者的是,代碼不只是“寫(xiě)”出來(lái)的。在很多年前,我所讀的軟件工程方面的教科書(shū)就告訴我,編碼的時(shí)間一般只占一個(gè)項目所花時(shí)間的 10%。我曾說(shuō)過(guò)一句比較有趣的話(huà):
  “如果一個(gè)從業(yè)者告訴你,他的大部分時(shí)間都在寫(xiě)代碼,那么他大概率不是一個(gè)高級軟件工程師?!?
  那么,軟件工程師的時(shí)間都花到哪里去了呢?軟件工程師的時(shí)間應該花在哪里呢?
  好的代碼是多個(gè)工作環(huán)節的綜合結果。
 ?。?)在編碼前,需要做好需求分析和系統設計。而這兩項工作是經(jīng)常被大量軟件工程師忽略或輕視的環(huán)節。
 ?。?)在編碼時(shí),需要編寫(xiě)代碼和編寫(xiě)單元測試。對于“編寫(xiě)代碼”,讀者都了解;而對于“編寫(xiě)單元測試”,有些軟件工程師就不認同了,甚至還有人誤以為單元測試是由測試工程師來(lái)編寫(xiě)的。
 ?。?)在編碼后,要做集成測試、上線(xiàn),以及持續運營(yíng)/迭代改進(jìn)。這幾件事情都是要花費不少精力的,比如上線(xiàn),不僅僅要做程序部署,而且要考慮程序是如何被監控的。有時(shí),為了一段程序的上線(xiàn),設計和實(shí)施監控的方案要花費好幾天才能完成。
  因此,一個(gè)好的系統或產(chǎn)品是以上這些環(huán)節持續循環(huán)執行的結果。
  ▊ 需求分析和系統設計
  1.幾種常見(jiàn)的錯誤現象
  相對于編碼工作,需求分析和系統設計是兩個(gè)經(jīng)常被忽視的環(huán)節。在現實(shí)工作中,我們經(jīng)常會(huì )看到以下這些現象。
 ?。?)很多人錯誤地認為,寫(xiě)代碼才是最重要的事情。不少軟件工程師如果一天沒(méi)有寫(xiě)出幾行代碼,就會(huì )認為工作沒(méi)有進(jìn)展;很多管理者也會(huì )以代碼的產(chǎn)出量作為衡量工作結果的主要標準,催促軟件工程師盡早開(kāi)始寫(xiě)代碼。
 ?。?)有太多的從業(yè)者,在沒(méi)有搞清楚項目目標之前就已經(jīng)開(kāi)始編碼了。在很多時(shí)候,項目目標都是通過(guò)并不準確的口頭溝通來(lái)確定的。例如:
  “需要做什么?”
  “就按照×××網(wǎng)站的做一個(gè)吧?!?
 ?。?)有太多的從業(yè)者,在代碼編寫(xiě)基本完成后,才發(fā)現設計思路是有問(wèn)題的。他們在很多項目上花費很少(甚至沒(méi)有花費)時(shí)間進(jìn)行系統設計,對于在設計中所隱藏的問(wèn)題并沒(méi)有仔細思考和求證?;谶@樣的設計投入和設計質(zhì)量,項目出現設計失誤也是很難避免的。而面對一個(gè)已經(jīng)完成了基本編碼的項目,如果要“動(dòng)大手術(shù)”來(lái)修改它,相信每個(gè)有過(guò)類(lèi)似經(jīng)歷的人都一定深知那種感受——越改越亂,越改越著(zhù)急。
  以上這幾種情況,很多讀者是不是都有過(guò)類(lèi)似經(jīng)歷?
  2.研發(fā)前期多投入,收益更大
  關(guān)于軟件研發(fā),首先我們需要建立一個(gè)非常重要的觀(guān)念。
  在研發(fā)前期(需求分析和系統設計)多投入資源,相對于把資源都投入在研發(fā)后期(編碼、測試等),其收益更大。
  這是為什么呢?
  要回答這個(gè)問(wèn)題,需要從軟件研發(fā)全生命周期的角度來(lái)考量軟件研發(fā)的成本。除編碼外,軟件測試、上線(xiàn)、調試等都需要很高成本。如果我們把需求搞錯了,那么與錯誤需求有關(guān)的設計、編碼、測試、上線(xiàn)等成本就都浪費了;如果我們把設計搞錯了,那么與錯誤設計相關(guān)的編碼、測試、上線(xiàn)的成本也就浪費了。
  如果仔細考量那些低效的項目,會(huì )發(fā)現有非常多的類(lèi)似于上面提到的“浪費”的地方。軟件工程師似乎都很忙,但是在錯誤方向上所做的所有努力并不會(huì )產(chǎn)生任何價(jià)值,而大部分的加班實(shí)際上是在做錯誤的事情,或者是為了補救錯誤而努力。在這種情況下,將更多的資源和注意力向研發(fā)前期傾斜會(huì )立刻收到良好的效果。
  3.修改代碼和修改文檔,哪個(gè)成本更高
  很多軟件工程師不愿意做需求分析和系統設計,是因為對“寫(xiě)文檔”有著(zhù)根深蒂固的偏見(jiàn)。這里問(wèn)大家一個(gè)問(wèn)題,如果大家對這個(gè)問(wèn)題能給出正確的回答,那么在“寫(xiě)文檔”的意識方面,一定會(huì )有很大的轉變。
  任何人都不是神仙,無(wú)法一次就把所有事情做對。對于一段程序來(lái)說(shuō),它一定要經(jīng)過(guò)一定周期的修改和迭代。這時(shí)有兩種選擇:
  選擇一:修改文檔。在設計文檔時(shí)完成迭代調整,待沒(méi)有大問(wèn)題后再開(kāi)始編碼。
  選擇二:修改代碼。只有粗略的設計文檔,或者沒(méi)有設計文檔,直接開(kāi)始編碼,所有的迭代調整都在代碼上完成。
  請大家判斷,修改代碼和修改文檔,哪個(gè)成本更高?
  在之前的一些分享交流會(huì )上,對于這個(gè)問(wèn)題,有人會(huì )說(shuō),修改文檔的成本更高。因為在修改文檔后還要修改代碼,多了一道手續。而直接修改代碼,只需要做一次,這樣更直接。
  這個(gè)回答說(shuō)明了回答者沒(méi)有充分理解“先寫(xiě)文檔,后寫(xiě)代碼”的設計方法。如果沒(méi)有充分重視設計文檔的工作,在輸出的設計文檔質(zhì)量不高的情況下就開(kāi)始編碼,確實(shí)會(huì )出現以上提到的問(wèn)題。但是,如果在設計文檔階段就已經(jīng)做了充分考慮,會(huì )減少對代碼的迭代和反復。
  對于同樣的設計修改,“修改代碼”的成本遠高于“修改文檔”。這是因為,在設計文檔中只會(huì )涉及主要的邏輯,那些細小的、顯而易見(jiàn)的邏輯不會(huì )在設計文檔中出現。在修改設計文檔時(shí),也只會(huì )影響到這些主要邏輯。而如果在代碼中做修改,不僅會(huì )涉及這些主要邏輯,而且會(huì )涉及那些在文檔中不會(huì )出現的細小邏輯。對于一段程序來(lái)說(shuō),任何一個(gè)邏輯出現問(wèn)題,程序都是無(wú)法正常運行的。
  4.需求分析和系統設計之間的差別
  很多讀者無(wú)法清楚地區分“需求分析”和“系統設計”之間的差別,于是會(huì )發(fā)現,在寫(xiě)出的文檔中,有些需求分析文檔里出現了系統設計的內容,而有些系統設計文檔里又混雜了需求分析的內容。
  我們用幾句話(huà)可以非常明確地給出二者的差異。
 ?。?)需求分析:定義系統/軟件的黑盒的行為,它是從外部(External)看到的,在說(shuō)明“是什么”(What)。
 ?。?)系統設計:設計系統/軟件的白盒的機制,它是從內部(Internal)看到的,要說(shuō)明“怎么做”(How)和“為什么”(Why)。
  比如,對一輛汽車(chē)來(lái)說(shuō),首先使用者從外部可以看到車(chē)廂、車(chē)輪,坐在車(chē)里可以看到方向盤(pán)、剎車(chē)踏板、油門(mén)踏板等;操作方向盤(pán)可以改變汽車(chē)的行駛方向,腳踩剎車(chē)踏板、油門(mén)踏板可用于減速和加速。以上這些是對汽車(chē)的“需求分析”。
  然后,我們想象汽車(chē)外殼和內部變成了透明的,可以看到汽車(chē)內部的發(fā)動(dòng)機、變速箱、傳動(dòng)桿、與剎車(chē)相關(guān)的內部裝置等。而這些對駕駛者來(lái)說(shuō)是不可見(jiàn)的,它們是對汽車(chē)的“系統設計”。
  -End-
  最近有一些小伙伴,讓我幫忙找一些面試題資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!
  
  點(diǎn)擊卡片,關(guān)注后回復【面試題】即可獲取
  在看點(diǎn)這里
   查看全部

  文章采集調用 好代碼和壞代碼
  關(guān)注后回復“進(jìn)群”,拉你進(jìn)程序員交流群
  要寫(xiě)出好代碼,首先需要提升品位。
  很多軟件工程師寫(xiě)不好代碼,在評審他人的代碼時(shí)也看不出問(wèn)題,就是因為缺乏對好代碼標準的認識。
  現在還有太多的軟件工程師認為,代碼只要可以正確執行就可以了。這是一種非常低的評價(jià)標準,很多重要的方面都被忽視了。
  好代碼的特性
  好代碼具有以下特性。
  1. 魯棒(Solid and Robust)
  代碼不僅要被正確執行,我們還要考慮對各種錯誤情況的處理,比如各種系統調用和函數調用的異常情況,系統相關(guān)組件的異常和錯誤。
  對很多產(chǎn)品級的程序來(lái)說(shuō),異常和錯誤處理的邏輯占了很大比例。
  2. 高效(Fast)
  程序的運行應使用盡量少的資源。資源不僅僅包括CPU,還可能包括存儲、I/O等。
  設計高效的程序,會(huì )運用到數據結構和算法方面的知識,同時(shí)要考慮到程序運行時(shí)的各種約束條件。
  3. 簡(jiǎn)潔(Maintainable and Simple)
  代碼的邏輯要盡量簡(jiǎn)明易懂,代碼要具有很好的可維護性。對于同樣的目標,能夠使用簡(jiǎn)單清楚的方法達成,就不要使用復雜晦澀的方法。
  “大道至簡(jiǎn)”,能否把復雜的問(wèn)題用簡(jiǎn)單的方式實(shí)現出來(lái),這是一種編程水平的體現。
  4. 簡(jiǎn)短(Small)
  在某種意義上,代碼的復雜度和維護成本是和代碼的規模直接相關(guān)的。在實(shí)現同樣功能的時(shí)候,要盡量將代碼寫(xiě)得簡(jiǎn)短一些。
  簡(jiǎn)潔高于簡(jiǎn)短。這里要注意,某些人為了能把代碼寫(xiě)得簡(jiǎn)短,使用了一些晦澀難懂的描述方式,降低了代碼的可讀性。這種方式是不可取的。
  5. 可測試(Testable)
  代碼的正確性要通過(guò)測試來(lái)保證,尤其是在敏捷的場(chǎng)景下,更需要依賴(lài)可自動(dòng)回歸執行的測試用例。
  在代碼的設計中,要考慮如何使代碼可測、易測。一個(gè)比較好的實(shí)踐是使用TDD(Test-Driven Development,測試驅動(dòng)開(kāi)發(fā))的方法,這樣在編寫(xiě)測試用例的時(shí)候會(huì )很快發(fā)現代碼在可測試性方面的問(wèn)題。
  6. 共享(Re-Usable)
  大量的程序實(shí)際上都使用了類(lèi)似的框架或邏輯。由于目前開(kāi)源代碼的大量普及,很多功能并不需要重復開(kāi)發(fā),只進(jìn)行引用和使用即可。
  在一個(gè)組織內部,應鼓勵共享和重用代碼,這樣可以有效降低代碼研發(fā)的成本,并提升代碼的質(zhì)量。
  實(shí)現代碼的共享,不僅需要在意識方面提升,還需要具有相關(guān)的能力(如編寫(xiě)獨立、高質(zhì)量的代碼庫)及相關(guān)基礎設施的支持(如代碼搜索、代碼引用機制)。
  7. 可移植(Portable)
  某些程序需要在多種操作系統下運行,在這種情況下,代碼的可移植性成為一種必需的能力。
  要讓代碼具有可移植性,需要對所運行的各種操作系統底層有充分的理解和統一抽象。一般會(huì )使用一個(gè)適配層來(lái)屏蔽操作系統底層的差異。
  一些編程語(yǔ)言也提供了多操作系統的可移植性,如很多基于Python語(yǔ)言、Java語(yǔ)言、Go語(yǔ)言編寫(xiě)的程序,都可以跨平臺運行。
  8. 可觀(guān)測(Observable)/ 可監控(Monitorable)
  面對目前大量存在的在線(xiàn)服務(wù)(Online Service)程序,需要具備對程序的運行狀態(tài)進(jìn)行細致而持續監控的能力。
  這要求在程序設計時(shí)就提供相關(guān)的機制,包括程序狀態(tài)的收集、保存和對外輸出。
  9. 可運維(Operational)
  可運維已經(jīng)成為軟件研發(fā)活動(dòng)的重要組成部分,可運維重點(diǎn)關(guān)注成本、效率和穩定性三個(gè)方面。
  程序的可運維性和程序的設計、編寫(xiě)緊密相關(guān),如果在程序設計階段就沒(méi)有考慮可運維性,那么程序運行的運維目標則難以達成。
  10. 可擴展(Scalable andExtensible)
  可擴展包含“容量可擴展”(Scalable)和“功能可擴展”(Extensible)兩方面。
  在互聯(lián)網(wǎng)公司的系統設計中,“容量可擴展”是重要的設計目標之一。系統要盡量支持通過(guò)增加資源來(lái)實(shí)現容量的線(xiàn)性提高。
  快速響應需求的變化,是互聯(lián)網(wǎng)公司的另外一個(gè)重要挑戰??煽紤]使用插件式的程序設計方式,以容納未來(lái)可能新增的功能,也可考慮使用類(lèi)似Protocol Buffer 這樣的工具,支持對協(xié)議新增字段。
  以上十條標準,如果要記住,可能有些困難。我們可以把它們歸納為四個(gè)方面,見(jiàn)表1。
  表1對一流代碼特性的匯總分類(lèi)
  
  壞代碼的例子
  關(guān)于好代碼,上面介紹了一些特性,本節也給出壞代碼(Bad Code)的幾個(gè)例子。關(guān)于壞代碼,本書(shū)沒(méi)有做系統性總結,只是希望通過(guò)以下這些例子的展示讓讀者對壞代碼有直觀(guān)的感覺(jué)。
  1.不好的函數名稱(chēng)(Bad Function Name)
  如do(),這樣的函數名稱(chēng)沒(méi)有多少信息量;又如myFunc(),這樣的函數名稱(chēng),個(gè)人色彩過(guò)于強烈,也沒(méi)有足夠的信息量。
  2.不好的變量名稱(chēng)(Bad Variable Name)
  如a、b、c、i、j、k、temp,這樣的變量名稱(chēng)在很多教科書(shū)中經(jīng)常出現,很多人在上學(xué)期間寫(xiě)代碼時(shí)也會(huì )經(jīng)常這樣用。如果作為局部變量,這樣的名稱(chēng)有時(shí)是可以接受的;但如果作為作用域稍微大的變量,這樣的名稱(chēng)就非常不可取了。
  3.沒(méi)有注釋?zhuān)∟o Comments)
  有寫(xiě)注釋習慣的軟件工程師很少,很多軟件工程師認為寫(xiě)注釋是浪費時(shí)間,是“額外”的工作。但是沒(méi)有注釋的代碼,閱讀的成本會(huì )比較高。
  4.函數不是單一目的(The Function has No Single Purpose)
  如LoadFromFileAndCalculate()。這個(gè)例子是我編造的,但現實(shí)中這樣的函數其實(shí)不少。很多函數在首次寫(xiě)出來(lái)的時(shí)候,就很難表述清楚其用途;還有一些函數隨著(zhù)功能的擴展,變得越來(lái)越龐雜,也就慢慢地說(shuō)不清它的目的了。
  這方面的問(wèn)題可能很多人都沒(méi)有充分地認識到——非單一目的的函數難以維護,也難以復用。
  5.不好的排版(Bad Layout)
  不少人認為,程序可以正常執行就行了,所以一些軟件工程師不重視對代碼的排版,認為這僅僅是一種“形式”。
  沒(méi)有排好版的程序,在閱讀效率方面會(huì )帶來(lái)嚴重問(wèn)題。這里舉一個(gè)極端的例子:對于C語(yǔ)言來(lái)說(shuō),“;”可作為語(yǔ)句的分割符,而“縮進(jìn)”和“換行”對于編譯器來(lái)說(shuō)是無(wú)用的,所以完全可以把一段C語(yǔ)言程序都“壓縮”在一行內。這樣的程序是可以運行的,但是對人來(lái)說(shuō),可讀性非常差。這樣的程序肯定是我們非常不希望看到的。
  6.無(wú)法測試(None Testable)
  程序的正確性要依賴(lài)測試來(lái)保證(雖然測試并不能保證程序完全無(wú)錯)。無(wú)法或不好為之編寫(xiě)測試用例的程序,是很難有質(zhì)量保證的。
  好代碼從哪里來(lái)
  上一節說(shuō)明了好代碼的特性,本節來(lái)分析好代碼是如何產(chǎn)出的。
  ▊ 好代碼不止于編碼
  好代碼從哪里來(lái)?
  對于這個(gè)問(wèn)題,很多讀者肯定會(huì )說(shuō):“好代碼肯定是寫(xiě)出來(lái)的呀?!?
  我曾做過(guò)多次調研,發(fā)現很多軟件工程師日常所讀的書(shū)確實(shí)是和“寫(xiě)代碼”緊密相關(guān)的。
  但是,這里要告訴讀者的是,代碼不只是“寫(xiě)”出來(lái)的。在很多年前,我所讀的軟件工程方面的教科書(shū)就告訴我,編碼的時(shí)間一般只占一個(gè)項目所花時(shí)間的 10%。我曾說(shuō)過(guò)一句比較有趣的話(huà):
  “如果一個(gè)從業(yè)者告訴你,他的大部分時(shí)間都在寫(xiě)代碼,那么他大概率不是一個(gè)高級軟件工程師?!?
  那么,軟件工程師的時(shí)間都花到哪里去了呢?軟件工程師的時(shí)間應該花在哪里呢?
  好的代碼是多個(gè)工作環(huán)節的綜合結果。
 ?。?)在編碼前,需要做好需求分析和系統設計。而這兩項工作是經(jīng)常被大量軟件工程師忽略或輕視的環(huán)節。
 ?。?)在編碼時(shí),需要編寫(xiě)代碼和編寫(xiě)單元測試。對于“編寫(xiě)代碼”,讀者都了解;而對于“編寫(xiě)單元測試”,有些軟件工程師就不認同了,甚至還有人誤以為單元測試是由測試工程師來(lái)編寫(xiě)的。
 ?。?)在編碼后,要做集成測試、上線(xiàn),以及持續運營(yíng)/迭代改進(jìn)。這幾件事情都是要花費不少精力的,比如上線(xiàn),不僅僅要做程序部署,而且要考慮程序是如何被監控的。有時(shí),為了一段程序的上線(xiàn),設計和實(shí)施監控的方案要花費好幾天才能完成。
  因此,一個(gè)好的系統或產(chǎn)品是以上這些環(huán)節持續循環(huán)執行的結果。
  ▊ 需求分析和系統設計
  1.幾種常見(jiàn)的錯誤現象
  相對于編碼工作,需求分析和系統設計是兩個(gè)經(jīng)常被忽視的環(huán)節。在現實(shí)工作中,我們經(jīng)常會(huì )看到以下這些現象。
 ?。?)很多人錯誤地認為,寫(xiě)代碼才是最重要的事情。不少軟件工程師如果一天沒(méi)有寫(xiě)出幾行代碼,就會(huì )認為工作沒(méi)有進(jìn)展;很多管理者也會(huì )以代碼的產(chǎn)出量作為衡量工作結果的主要標準,催促軟件工程師盡早開(kāi)始寫(xiě)代碼。
 ?。?)有太多的從業(yè)者,在沒(méi)有搞清楚項目目標之前就已經(jīng)開(kāi)始編碼了。在很多時(shí)候,項目目標都是通過(guò)并不準確的口頭溝通來(lái)確定的。例如:
  “需要做什么?”
  “就按照×××網(wǎng)站的做一個(gè)吧?!?
 ?。?)有太多的從業(yè)者,在代碼編寫(xiě)基本完成后,才發(fā)現設計思路是有問(wèn)題的。他們在很多項目上花費很少(甚至沒(méi)有花費)時(shí)間進(jìn)行系統設計,對于在設計中所隱藏的問(wèn)題并沒(méi)有仔細思考和求證?;谶@樣的設計投入和設計質(zhì)量,項目出現設計失誤也是很難避免的。而面對一個(gè)已經(jīng)完成了基本編碼的項目,如果要“動(dòng)大手術(shù)”來(lái)修改它,相信每個(gè)有過(guò)類(lèi)似經(jīng)歷的人都一定深知那種感受——越改越亂,越改越著(zhù)急。
  以上這幾種情況,很多讀者是不是都有過(guò)類(lèi)似經(jīng)歷?
  2.研發(fā)前期多投入,收益更大
  關(guān)于軟件研發(fā),首先我們需要建立一個(gè)非常重要的觀(guān)念。
  在研發(fā)前期(需求分析和系統設計)多投入資源,相對于把資源都投入在研發(fā)后期(編碼、測試等),其收益更大。
  這是為什么呢?
  要回答這個(gè)問(wèn)題,需要從軟件研發(fā)全生命周期的角度來(lái)考量軟件研發(fā)的成本。除編碼外,軟件測試、上線(xiàn)、調試等都需要很高成本。如果我們把需求搞錯了,那么與錯誤需求有關(guān)的設計、編碼、測試、上線(xiàn)等成本就都浪費了;如果我們把設計搞錯了,那么與錯誤設計相關(guān)的編碼、測試、上線(xiàn)的成本也就浪費了。
  如果仔細考量那些低效的項目,會(huì )發(fā)現有非常多的類(lèi)似于上面提到的“浪費”的地方。軟件工程師似乎都很忙,但是在錯誤方向上所做的所有努力并不會(huì )產(chǎn)生任何價(jià)值,而大部分的加班實(shí)際上是在做錯誤的事情,或者是為了補救錯誤而努力。在這種情況下,將更多的資源和注意力向研發(fā)前期傾斜會(huì )立刻收到良好的效果。
  3.修改代碼和修改文檔,哪個(gè)成本更高
  很多軟件工程師不愿意做需求分析和系統設計,是因為對“寫(xiě)文檔”有著(zhù)根深蒂固的偏見(jiàn)。這里問(wèn)大家一個(gè)問(wèn)題,如果大家對這個(gè)問(wèn)題能給出正確的回答,那么在“寫(xiě)文檔”的意識方面,一定會(huì )有很大的轉變。
  任何人都不是神仙,無(wú)法一次就把所有事情做對。對于一段程序來(lái)說(shuō),它一定要經(jīng)過(guò)一定周期的修改和迭代。這時(shí)有兩種選擇:
  選擇一:修改文檔。在設計文檔時(shí)完成迭代調整,待沒(méi)有大問(wèn)題后再開(kāi)始編碼。
  選擇二:修改代碼。只有粗略的設計文檔,或者沒(méi)有設計文檔,直接開(kāi)始編碼,所有的迭代調整都在代碼上完成。
  請大家判斷,修改代碼和修改文檔,哪個(gè)成本更高?
  在之前的一些分享交流會(huì )上,對于這個(gè)問(wèn)題,有人會(huì )說(shuō),修改文檔的成本更高。因為在修改文檔后還要修改代碼,多了一道手續。而直接修改代碼,只需要做一次,這樣更直接。
  這個(gè)回答說(shuō)明了回答者沒(méi)有充分理解“先寫(xiě)文檔,后寫(xiě)代碼”的設計方法。如果沒(méi)有充分重視設計文檔的工作,在輸出的設計文檔質(zhì)量不高的情況下就開(kāi)始編碼,確實(shí)會(huì )出現以上提到的問(wèn)題。但是,如果在設計文檔階段就已經(jīng)做了充分考慮,會(huì )減少對代碼的迭代和反復。
  對于同樣的設計修改,“修改代碼”的成本遠高于“修改文檔”。這是因為,在設計文檔中只會(huì )涉及主要的邏輯,那些細小的、顯而易見(jiàn)的邏輯不會(huì )在設計文檔中出現。在修改設計文檔時(shí),也只會(huì )影響到這些主要邏輯。而如果在代碼中做修改,不僅會(huì )涉及這些主要邏輯,而且會(huì )涉及那些在文檔中不會(huì )出現的細小邏輯。對于一段程序來(lái)說(shuō),任何一個(gè)邏輯出現問(wèn)題,程序都是無(wú)法正常運行的。
  4.需求分析和系統設計之間的差別
  很多讀者無(wú)法清楚地區分“需求分析”和“系統設計”之間的差別,于是會(huì )發(fā)現,在寫(xiě)出的文檔中,有些需求分析文檔里出現了系統設計的內容,而有些系統設計文檔里又混雜了需求分析的內容。
  我們用幾句話(huà)可以非常明確地給出二者的差異。
 ?。?)需求分析:定義系統/軟件的黑盒的行為,它是從外部(External)看到的,在說(shuō)明“是什么”(What)。
 ?。?)系統設計:設計系統/軟件的白盒的機制,它是從內部(Internal)看到的,要說(shuō)明“怎么做”(How)和“為什么”(Why)。
  比如,對一輛汽車(chē)來(lái)說(shuō),首先使用者從外部可以看到車(chē)廂、車(chē)輪,坐在車(chē)里可以看到方向盤(pán)、剎車(chē)踏板、油門(mén)踏板等;操作方向盤(pán)可以改變汽車(chē)的行駛方向,腳踩剎車(chē)踏板、油門(mén)踏板可用于減速和加速。以上這些是對汽車(chē)的“需求分析”。
  然后,我們想象汽車(chē)外殼和內部變成了透明的,可以看到汽車(chē)內部的發(fā)動(dòng)機、變速箱、傳動(dòng)桿、與剎車(chē)相關(guān)的內部裝置等。而這些對駕駛者來(lái)說(shuō)是不可見(jiàn)的,它們是對汽車(chē)的“系統設計”。
  -End-
  最近有一些小伙伴,讓我幫忙找一些面試題資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!
  
  點(diǎn)擊卡片,關(guān)注后回復【面試題】即可獲取
  在看點(diǎn)這里
  

Spring Boot 如何監控 SQL 運行情況?

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 112 次瀏覽 ? 2022-05-09 12:17 ? 來(lái)自相關(guān)話(huà)題

  Spring Boot 如何監控 SQL 運行情況?
  關(guān)注后回復“進(jìn)群”,拉你進(jìn)程序員交流群
  今天想和大家聊一聊 Druid 中的監控功能。
  Druid 數據庫連接池相信很多小伙伴都用過(guò),個(gè)人感覺(jué) Druid 是阿里比較成功的開(kāi)源項目了,不像 Fastjson 那么多槽點(diǎn),Druid 各方面一直都比較出色,功能齊全,使用也方便,基本的用法就不說(shuō)了,今天我們來(lái)看看 Druid 中的監控功能。
  1. 準備工作
  首先我們來(lái)創(chuàng )建一個(gè) Spring Boot 工程,引入 MyBatis 等,如下:
  
  選一下 MyBatis 和 MySQL 驅動(dòng),做一個(gè)簡(jiǎn)單的測試案例。
  先來(lái)連接一下數據庫:
  spring.datasource.username=root<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.password=123<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.url=jdbc:mysql:///test05?serverTimezone=Asia/Shanghai<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  創(chuàng )建一個(gè) User 實(shí)體類(lèi),做一個(gè)簡(jiǎn)單的查詢(xún)案例,如下:
  public?class?User?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?Integer?id;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?username;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?address;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?password;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?email;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????//省略?getter/setter<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@Mapper<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?interface?UserMapper?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????List?getUserByUsername(String?username);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@Service<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?UserService?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@Autowired<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????UserMapper?userMapper;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?List?getUserByUsername(String?username){<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????return?userMapper.getUserByUsername(username);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@RestController<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?UserController?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@Autowired<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????UserService?userService;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@GetMapping("/user")<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?List?getUser(String?username)?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????return?userService.getUserByUsername(username);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  UserMapper.xml 如下:
  <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????select?*?from?user?where?username=#{username}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  一個(gè)很簡(jiǎn)單的測試,沒(méi)啥好說(shuō)的。
  這個(gè)環(huán)境搭建大家隨意,如果你已經(jīng)有持久化的案例了,那就直接看第二小節引入 Druid。
  現在這個(gè)工程默認的使用的數據庫連接池是 HikariDataSource,這是 Spring Boot 中默認的一個(gè)數據庫連接池,其實(shí)這個(gè)也還不錯。
  2. 引入 Druid
  接下來(lái)我們引入 Druid:
  <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????com.alibaba<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????druid-spring-boot-starter<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????1.2.8<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  注意,Spring Boot 引入的 Druid 是上面這個(gè),這個(gè)將來(lái)配置監控的時(shí)候方便一些。
  接下來(lái)我們在 application.properties 中配置 WebStatFilter,WebStatFilter 用于采集 web-jdbc 關(guān)聯(lián)監控的數據:
  # 啟用 WebStatFilter<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.enabled=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 配置攔截規則<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.url-pattern=/*<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 排除一些不必要的 url,這些 URL 不會(huì )涉及到 SQL 查詢(xún)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 開(kāi)啟 session 統計功能<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.session-stat-enable=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 缺省 sessionStatMaxCount 是 1000 個(gè),我們可以按需要進(jìn)行配置<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.session-stat-max-count=1000<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 配置 principalSessionName,使得 druid 能夠知道當前的 session 的用戶(hù)是誰(shuí)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 根據需要,這個(gè)參數的值是 user 信息保存在 session 中的 sessionName<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />#spring.datasource.druid.web-stat-filter.principal-session-name=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 下面這個(gè)配置的作用和上面配置的作用類(lèi)似,這個(gè)是通過(guò) Cookie 來(lái)識別用戶(hù)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />#spring.datasource.druid.web-stat-filter.principal-cookie-name=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 開(kāi)啟 profile 后就能夠監控單個(gè) URL 地址調用列表<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />#spring.datasource.druid.web-stat-filter.profile-enable=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  我們配置前面五個(gè)就可以了,后面三個(gè)可以不用配置,各項配置的含義松哥已經(jīng)在代碼中列出來(lái)了。
  接下來(lái)開(kāi)啟 StatViewServlet 的配置,如下:
  # 啟用內置的監控頁(yè)面<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.enabled=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 內置監控頁(yè)面的地址<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 開(kāi)啟 Reset All 功能<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.reset-enable=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 設置登錄用戶(hù)名<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.login-username=javaboy<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 設置登錄密碼<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.login-password=123<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 白名單(如果allow沒(méi)有配置或者為空,則允許所有訪(fǎng)問(wèn))<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.allow=127.0.0.1<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 黑名單(deny 優(yōu)先于 allow,如果在 deny 列表中,就算在 allow 列表中,也會(huì )被拒絕)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.deny=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  配置一下頁(yè)面地址,配置一下黑白名單即可。
  需要注意的是,reset-enable 屬性即使設置為 false,重置按鈕也會(huì )顯示,只是點(diǎn)擊該按鈕并不會(huì )重置而已。
  好啦,這就完事了。
  3. 測試
  好啦,接下來(lái)我們啟動(dòng) Spring Boot 項目進(jìn)行測試。
  Spring Boot 項目啟動(dòng)成功后,首先訪(fǎng)問(wèn)如下地址:
  此時(shí)我們會(huì )看到登錄認證頁(yè)面,如下:
  
  輸入我們前面配置的用戶(hù)名/密碼(javaboy/123)進(jìn)行登錄,登錄成功后,可以看到如下頁(yè)面:
  
  從標題欄就可以看到,數據源、SQL 監控、SQL 防火墻等功能都是一應俱全。
  接下來(lái)我們訪(fǎng)問(wèn):8080/user?username=aaa地址,執行一條 SQL,執行完成后,我們來(lái)查看 SQL 監控:
  
  可以看到,此時(shí)就有 SQL 執行的監控記錄了。
  其他的監控數據也都可以看到,我就不一一列舉了。如果小伙伴們覺(jué)得這里展示的數據不直觀(guān),想自己畫(huà) HTML 頁(yè)面,那也是可以的,點(diǎn)擊最后面的 JSON API,可以看到每一個(gè)監控項的 JSON 地址,拿著(zhù) JSON 自己想怎么顯示就怎么顯示。
  4. 去廣告
  如果想直接用這個(gè)監控頁(yè)面,這個(gè)上面有阿里的廣告,如下圖,公司用的話(huà)就特別別扭:
  
  我們可能想去掉這個(gè)廣告,這也很容易。
  首先,經(jīng)過(guò)分析,我們發(fā)現廣告是由一個(gè)叫做 common.js 的文件構建出來(lái)的,該文件位于druid-1.2.8.jar!/support/http/resources/js/common.js這里,common.js 文件中有如下幾行:
  init?:?function()?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?this.buildFooter();<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?druid.lang.init();<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />},<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />buildFooter?:?function()?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?var?html?='';<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?$(document.body).append(html);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />},<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  大概邏輯就是上面這樣,buildFooter 方法負責構建頁(yè)面末尾的廣告,在 init 方法中完成對 buildFooter 方法的調用。
  那么想要去除廣告,就別調用 buildFooter 方法就行了。
  所以我們的去廣告思路也很簡(jiǎn)單,寫(xiě)一個(gè)過(guò)濾器,攔截下對 common.js 的請求,然后做一點(diǎn)點(diǎn)修改,如下:
  @WebFilter(urlPatterns?=?"/druid/js/common.js")<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?RemoveAdFilter?implements?Filter?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@Override<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?void?doFilter(ServletRequest?servletRequest,?ServletResponse?servletResponse,?FilterChain?filterChain)?throws?IOException,?ServletException?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????String?text?=?Utils.readFromResource("support/http/resources/js/common.js");<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????text?=?text.replace("this.buildFooter();",?"");<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????servletResponse.getWriter().write(text);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  可以看到,這個(gè)過(guò)濾器就是攔截/druid/js/common.js請求,攔截到之后,自己去文件中讀取 common.js 文件,然后手動(dòng)替換掉this.buildFooter();這一句就行了,最后再把文件寫(xiě)出去就行了。
  當然,記得在啟動(dòng)類(lèi)中掃描 Filter,如下:
  @SpringBootApplication<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@ServletComponentScan("org.javaboy.druid_monitor.filter")<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?DruidMonitorApplication?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?static?void?main(String[]?args)?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????SpringApplication.run(DruidMonitorApplication.class,?args);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  好啦,這就可以啦,有了這個(gè)過(guò)濾器,廣告就沒(méi)了。
  -End-
  最近有一些小伙伴,讓我幫忙找一些面試題資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!
  
  點(diǎn)擊卡片,關(guān)注后回復【面試題】即可獲取
  在看點(diǎn)這里
   查看全部

  Spring Boot 如何監控 SQL 運行情況?
  關(guān)注后回復“進(jìn)群”,拉你進(jìn)程序員交流群
  今天想和大家聊一聊 Druid 中的監控功能。
  Druid 數據庫連接池相信很多小伙伴都用過(guò),個(gè)人感覺(jué) Druid 是阿里比較成功的開(kāi)源項目了,不像 Fastjson 那么多槽點(diǎn),Druid 各方面一直都比較出色,功能齊全,使用也方便,基本的用法就不說(shuō)了,今天我們來(lái)看看 Druid 中的監控功能。
  1. 準備工作
  首先我們來(lái)創(chuàng )建一個(gè) Spring Boot 工程,引入 MyBatis 等,如下:
  
  選一下 MyBatis 和 MySQL 驅動(dòng),做一個(gè)簡(jiǎn)單的測試案例。
  先來(lái)連接一下數據庫:
  spring.datasource.username=root<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.password=123<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.url=jdbc:mysql:///test05?serverTimezone=Asia/Shanghai<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  創(chuàng )建一個(gè) User 實(shí)體類(lèi),做一個(gè)簡(jiǎn)單的查詢(xún)案例,如下:
  public?class?User?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?Integer?id;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?username;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?address;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?password;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????private?String?email;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????//省略?getter/setter<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@Mapper<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?interface?UserMapper?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????List?getUserByUsername(String?username);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@Service<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?UserService?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@Autowired<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????UserMapper?userMapper;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?List?getUserByUsername(String?username){<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????return?userMapper.getUserByUsername(username);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@RestController<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?UserController?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@Autowired<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????UserService?userService;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@GetMapping("/user")<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?List?getUser(String?username)?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????return?userService.getUserByUsername(username);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  UserMapper.xml 如下:
  <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????select?*?from?user?where?username=#{username}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  一個(gè)很簡(jiǎn)單的測試,沒(méi)啥好說(shuō)的。
  這個(gè)環(huán)境搭建大家隨意,如果你已經(jīng)有持久化的案例了,那就直接看第二小節引入 Druid。
  現在這個(gè)工程默認的使用的數據庫連接池是 HikariDataSource,這是 Spring Boot 中默認的一個(gè)數據庫連接池,其實(shí)這個(gè)也還不錯。
  2. 引入 Druid
  接下來(lái)我們引入 Druid:
  <br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????com.alibaba<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????druid-spring-boot-starter<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????1.2.8<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  注意,Spring Boot 引入的 Druid 是上面這個(gè),這個(gè)將來(lái)配置監控的時(shí)候方便一些。
  接下來(lái)我們在 application.properties 中配置 WebStatFilter,WebStatFilter 用于采集 web-jdbc 關(guān)聯(lián)監控的數據:
  # 啟用 WebStatFilter<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.enabled=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 配置攔截規則<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.url-pattern=/*<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 排除一些不必要的 url,這些 URL 不會(huì )涉及到 SQL 查詢(xún)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 開(kāi)啟 session 統計功能<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.session-stat-enable=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 缺省 sessionStatMaxCount 是 1000 個(gè),我們可以按需要進(jìn)行配置<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.web-stat-filter.session-stat-max-count=1000<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 配置 principalSessionName,使得 druid 能夠知道當前的 session 的用戶(hù)是誰(shuí)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 根據需要,這個(gè)參數的值是 user 信息保存在 session 中的 sessionName<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />#spring.datasource.druid.web-stat-filter.principal-session-name=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 下面這個(gè)配置的作用和上面配置的作用類(lèi)似,這個(gè)是通過(guò) Cookie 來(lái)識別用戶(hù)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />#spring.datasource.druid.web-stat-filter.principal-cookie-name=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 開(kāi)啟 profile 后就能夠監控單個(gè) URL 地址調用列表<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />#spring.datasource.druid.web-stat-filter.profile-enable=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  我們配置前面五個(gè)就可以了,后面三個(gè)可以不用配置,各項配置的含義松哥已經(jīng)在代碼中列出來(lái)了。
  接下來(lái)開(kāi)啟 StatViewServlet 的配置,如下:
  # 啟用內置的監控頁(yè)面<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.enabled=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 內置監控頁(yè)面的地址<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 開(kāi)啟 Reset All 功能<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.reset-enable=true<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 設置登錄用戶(hù)名<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.login-username=javaboy<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 設置登錄密碼<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.login-password=123<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 白名單(如果allow沒(méi)有配置或者為空,則允許所有訪(fǎng)問(wèn))<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.allow=127.0.0.1<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 黑名單(deny 優(yōu)先于 allow,如果在 deny 列表中,就算在 allow 列表中,也會(huì )被拒絕)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />spring.datasource.druid.stat-view-servlet.deny=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  配置一下頁(yè)面地址,配置一下黑白名單即可。
  需要注意的是,reset-enable 屬性即使設置為 false,重置按鈕也會(huì )顯示,只是點(diǎn)擊該按鈕并不會(huì )重置而已。
  好啦,這就完事了。
  3. 測試
  好啦,接下來(lái)我們啟動(dòng) Spring Boot 項目進(jìn)行測試。
  Spring Boot 項目啟動(dòng)成功后,首先訪(fǎng)問(wèn)如下地址:
  此時(shí)我們會(huì )看到登錄認證頁(yè)面,如下:
  
  輸入我們前面配置的用戶(hù)名/密碼(javaboy/123)進(jìn)行登錄,登錄成功后,可以看到如下頁(yè)面:
  
  從標題欄就可以看到,數據源、SQL 監控、SQL 防火墻等功能都是一應俱全。
  接下來(lái)我們訪(fǎng)問(wèn):8080/user?username=aaa地址,執行一條 SQL,執行完成后,我們來(lái)查看 SQL 監控:
  
  可以看到,此時(shí)就有 SQL 執行的監控記錄了。
  其他的監控數據也都可以看到,我就不一一列舉了。如果小伙伴們覺(jué)得這里展示的數據不直觀(guān),想自己畫(huà) HTML 頁(yè)面,那也是可以的,點(diǎn)擊最后面的 JSON API,可以看到每一個(gè)監控項的 JSON 地址,拿著(zhù) JSON 自己想怎么顯示就怎么顯示。
  4. 去廣告
  如果想直接用這個(gè)監控頁(yè)面,這個(gè)上面有阿里的廣告,如下圖,公司用的話(huà)就特別別扭:
  
  我們可能想去掉這個(gè)廣告,這也很容易。
  首先,經(jīng)過(guò)分析,我們發(fā)現廣告是由一個(gè)叫做 common.js 的文件構建出來(lái)的,該文件位于druid-1.2.8.jar!/support/http/resources/js/common.js這里,common.js 文件中有如下幾行:
  init?:?function()?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?this.buildFooter();<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?druid.lang.init();<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />},<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />buildFooter?:?function()?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?var?html?='';<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />?$(document.body).append(html);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />},<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  大概邏輯就是上面這樣,buildFooter 方法負責構建頁(yè)面末尾的廣告,在 init 方法中完成對 buildFooter 方法的調用。
  那么想要去除廣告,就別調用 buildFooter 方法就行了。
  所以我們的去廣告思路也很簡(jiǎn)單,寫(xiě)一個(gè)過(guò)濾器,攔截下對 common.js 的請求,然后做一點(diǎn)點(diǎn)修改,如下:
  @WebFilter(urlPatterns?=?"/druid/js/common.js")<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?RemoveAdFilter?implements?Filter?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????@Override<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?void?doFilter(ServletRequest?servletRequest,?ServletResponse?servletResponse,?FilterChain?filterChain)?throws?IOException,?ServletException?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????String?text?=?Utils.readFromResource("support/http/resources/js/common.js");<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????text?=?text.replace("this.buildFooter();",?"");<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????servletResponse.getWriter().write(text);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  可以看到,這個(gè)過(guò)濾器就是攔截/druid/js/common.js請求,攔截到之后,自己去文件中讀取 common.js 文件,然后手動(dòng)替換掉this.buildFooter();這一句就行了,最后再把文件寫(xiě)出去就行了。
  當然,記得在啟動(dòng)類(lèi)中掃描 Filter,如下:
  @SpringBootApplication<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />@ServletComponentScan("org.javaboy.druid_monitor.filter")<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />public?class?DruidMonitorApplication?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????public?static?void?main(String[]?args)?{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????????SpringApplication.run(DruidMonitorApplication.class,?args);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  好啦,這就可以啦,有了這個(gè)過(guò)濾器,廣告就沒(méi)了。
  -End-
  最近有一些小伙伴,讓我幫忙找一些面試題資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!
  
  點(diǎn)擊卡片,關(guān)注后回復【面試題】即可獲取
  在看點(diǎn)這里
  

一文詳解JVM垃圾收集機制,動(dòng)圖幫你輕松理解大廠(chǎng)面試難點(diǎn)

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 120 次瀏覽 ? 2022-05-09 12:05 ? 來(lái)自相關(guān)話(huà)題

  一文詳解JVM垃圾收集機制,動(dòng)圖幫你輕松理解大廠(chǎng)面試難點(diǎn)
  前言
  上篇文章已經(jīng)給大家介紹了 JVM 的架構和運行時(shí)數據區 (內存區域),本篇文章將給大家介紹 JVM 的重點(diǎn)內容——垃圾收集。眾所周知,相比 C / C++ 等語(yǔ)言,Java 可以省去手動(dòng)管理內存的繁瑣操作,很大程度上解放了 Java 程序員的生產(chǎn)力,而這正是得益于 JVM 的垃圾收集機制和內存分配策略。我們平時(shí)寫(xiě)程序時(shí)并感知不到這一點(diǎn),但是如果是在生產(chǎn)環(huán)境中,JVM 的不同配置對于服務(wù)器性能的影響是非常大的,所以掌握 JVM 調優(yōu)是高級 Java 工程師的必備技能。正所謂“基礎不牢,地動(dòng)山搖”,在這之前我們先來(lái)了解一下底層的 JVM 垃圾收集機制。
  既然要介紹垃圾收集機制,就要搞清楚以下幾個(gè)問(wèn)題:
  哪些內存區域需要進(jìn)行垃圾收集?
  如何判斷對象是否可回收?
  新的對象是如何進(jìn)行內存分配的?
  如何進(jìn)行垃圾收集?
  本文將按以下行文結構展開(kāi),對上述問(wèn)題一一解答。
  需要進(jìn)行垃圾收集的內存區域;
  判斷對象是否可回收的方法;
  主流的垃圾收集算法介紹;
  JVM 的內存分配與垃圾收集機制。
  下面開(kāi)始正文,還是圖文并茂的老配方,走起。
  一、需要進(jìn)行垃圾收集的內存區域
  先來(lái)回顧一下 JVM 的運行時(shí)數據區:
  
  JVM 運行時(shí)數據區
  其中程序計數器、Java 虛擬機棧和本地方法棧都是線(xiàn)程私有的,與其對應的線(xiàn)程是共生關(guān)系,隨線(xiàn)程而生,隨線(xiàn)程而滅,棧中的棧幀也隨著(zhù)方法的進(jìn)入和退出井然有序地進(jìn)行入棧和出棧操作。所以這幾個(gè)區域的內存分配和回收都是有很大確定性的,在方法結束或線(xiàn)程結束時(shí),內存也會(huì )隨之釋放,因此也就不需要考慮這幾個(gè)區域的內存回收問(wèn)題了。
  而堆和方法區就不一樣了,Java 的對象幾乎都是在堆上創(chuàng )建出來(lái)的,方法區則存儲了被虛擬機加載的類(lèi)型信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼緩存等數據,方法區中的運行時(shí)常量池則存放了各種字面量與符號引用,上述的這些數據大部分都是在運行時(shí)才能確定的,所以需要進(jìn)行動(dòng)態(tài)的內存管理。
  還要說(shuō)明一點(diǎn),JVM 中的垃圾收集器的最主要的關(guān)注對象是 Java 堆,因為這里進(jìn)行垃圾收集的“性?xún)r(jià)比”是最高的,尤其是在新生代 (后文對分代算法進(jìn)行介紹) 中的垃圾收集,一次就可以回收 70% - 99% 的內存。而方法區由于垃圾收集判定條件,尤其是類(lèi)型卸載的判定條件相當苛刻,其回收性?xún)r(jià)比是非常低的,因此有些垃圾收集器就干脆不支持或不完全支持方法區的垃圾收集,比如 JDK 11 中的 ZGC 收集器就不支持類(lèi)型卸載。
  二、判斷對象是否可回收的方法2.1 引用計數法
  引用計數法的實(shí)現很簡(jiǎn)單,在對象中添加一個(gè)引用計數器,每當有一個(gè)地方引用它時(shí),計數器值就加一;當引用失效時(shí),計數器值就減一;任何時(shí)刻計數器為零的對象就是不可能再被使用的。大部分情況下這個(gè)方法是可以發(fā)揮作用的,但是在存在循環(huán)引用的情況下,引用計數法就無(wú)能為力了。比如下面這種情況:
  public class Student {<br /> // friend 字段<br /> public Student friend = null;<br /> <br /> public static void test() {<br /> Student a = new Student();<br /> Student b = new Student();<br /> a.friend = b;<br /> b.friend = a;<br /> a = null;<br /> b = null;<br /> System.gc();<br /> }<br />}
  上述代碼創(chuàng )建了 a 和 b 兩個(gè) Student 實(shí)例,并把它們各自的 friend 字段賦值為對方,除此之外,這兩個(gè)對象再無(wú)任何引用,然后將它們都賦值為 null,在這種情況下,這兩個(gè)對象已經(jīng)不可能再被訪(fǎng)問(wèn),但是它們因為互相引用著(zhù)對方,導致它們的引用計數都不為零,引用計數算法也就無(wú)法回收它們。如下圖所示:
  
  循環(huán)引用
  但是在 Java 程序中,a 和 b 是可以被回收的,因為 JVM 并沒(méi)有使用引用計數法判定對象是否可回收,而是采用了可達性分析法。
  2.2 可達性分析法
  這個(gè)算法的基本思路就是通過(guò)一系列稱(chēng)為“GC Roots”的根對象作為起始節點(diǎn)集 (GC Root Set),從這些節點(diǎn)開(kāi)始,根據引用關(guān)系向下搜索,搜索過(guò)程所走過(guò)的路徑稱(chēng)為“引用鏈” (Reference Chain),如果某個(gè)對象到GC Roots間沒(méi)有任何引用鏈相連,則說(shuō)明此對象不再被使用,也就可以被回收了。要進(jìn)行可達性分析就需要先枚舉根節點(diǎn) (GC Roots),在枚舉根節點(diǎn)過(guò)程中,為防止對象的引用關(guān)系發(fā)生變化,需要暫停所有用戶(hù)線(xiàn)程 (垃圾收集之外的線(xiàn)程),這種暫停全部用戶(hù)線(xiàn)程的行為被稱(chēng)為 (Stop The World)??蛇_性分析法如下圖所示:
  
  可達性分析法
  圖中綠色的都是位于 GC Root Set 中的 GC Roots,所有與其有關(guān)聯(lián)的對象都是可達的,被標記為藍色,而所有與其沒(méi)有任何關(guān)聯(lián)的對象都是不可達的,被標記為灰色。即使是不可達對象,也并非一定會(huì )被回收,如果該對象同時(shí)滿(mǎn)足以下幾個(gè)條件,那么它仍有“逃生”的可能:
  該對象有重寫(xiě)的finalize()方法 (Object 類(lèi)中的方法);
  finalize()方法中將其自身鏈接到了引用鏈上;
  JVM 此前沒(méi)有調用過(guò)該對象的finalize()方法 (因為 JVM 在收集可回收對象時(shí)會(huì )調用且僅調用一次該對象的finalize()方法)。
  不過(guò)由于finalize()方法的運行代價(jià)高昂,不確定性大,且無(wú)法保證各個(gè)對象的調用順序,所以并不推薦使用。那么 GC Roots 又是何方神圣呢?在 Java 語(yǔ)言中,固定可作為GC Roots的對象包括以下幾種:
  在虛擬機棧 (棧幀中的本地變量表) 中引用的對象,比如各個(gè)線(xiàn)程被調用的方法堆棧中使用到的參數、局部變量、臨時(shí)變量等。
  在方法區中類(lèi)靜態(tài)屬性引用的對象,比如Java類(lèi)的引用類(lèi)型靜態(tài)變量。
  在方法區中常量引用的對象,比如字符串常量池(String Table)里的引用。
  在本地方法棧中JNI (即通常所說(shuō)的Native方法) 引用的對象。
  Java虛擬機內部的引用,如基本數據類(lèi)型對應的Class對象,一些常見(jiàn)的異常對象 (比如
  NullPointExcepiton、OutOfMemoryError) 等,還有系統類(lèi)加載器。
  所有被同步鎖 (synchronized關(guān)鍵字) 持有的對象。
  反映Java虛擬機內部情況的 JM XBean、JVM TI 中注冊的回調、本地代碼緩存等。
  三、垃圾收集算法介紹3.1 標記-清除算法
  標記-清除算法的思想很簡(jiǎn)單,顧名思義,該算法的過(guò)程分為標記和清除兩個(gè)階段:首先標記出所有需要回收的對象,其中標記過(guò)程就是使用可達性分析法判斷對象是否屬于垃圾的過(guò)程。在標記完成后,統一回收掉所有被標記的對象,也可以反過(guò)來(lái),標記存活的對象,統一回收所有未被標記的對象。示意圖如下:
  
  標記清除算法
  這個(gè)算法雖然很簡(jiǎn)單,但是有兩個(gè)明顯的缺點(diǎn):
  執行效率不穩定。如果 Java 堆中包含大量對象,而且其中大部分是需要被回收的,這時(shí)必須進(jìn)行大量標記和清除的動(dòng)作,導致標記和清除兩個(gè)過(guò)程的執行效率都隨對象數量增長(cháng)而降低;
  導致內存空間碎片化。標記、清除之后會(huì )產(chǎn)生大量不連續的內存碎片,空間碎片太多可能會(huì )導致當以后在程序運行過(guò)程中需要分配較大對象時(shí)無(wú)法找到足夠的連續內存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作,非常影響程序運行效率。
  3.2 標記-復制算法
  標記-復制算法常簡(jiǎn)稱(chēng)復制算法,這一算法正好解決了標記-清除算法在面對大量可回收對象時(shí)執行效率低下的問(wèn)題。其實(shí)現方法也很易懂:在可用內存中劃分出兩塊大小相同的區域,每次只使用其中一塊,另一塊保持空閑狀態(tài),第一塊用完的時(shí)候,就把存活的對象全部復制到第二塊區域,然后把第一塊全部清空。如下圖所示:
  
  標記-復制算法
  這個(gè)算法很適合用于對象存活率低的情況,因為它只關(guān)注存活對象而無(wú)需理會(huì )可回收對象,所以 JVM 中新生代的垃圾收集正是采用的這一算法。但是其缺點(diǎn)也很明顯,每次都要浪費一半的內存,未免太過(guò)奢侈,不過(guò) JVM 中的新生代有更精細的內存劃分,比較好地解決了這個(gè)問(wèn)題,見(jiàn)下文。
  3.3 標記-整理算法
  這個(gè)算法完美解決了標記-清除算法的空間碎片化問(wèn)題,其標記過(guò)程與“標記-清除”算法一樣,但后續步驟不是直接對可回收對象進(jìn)行清理,而是讓所有存活的對象都向內存空間一端移動(dòng),然后直接清理掉邊界以外的內存。
  
  標記整理算法
  這個(gè)算法雖然可以很好地解決空間碎片化問(wèn)題,但是每次垃圾回收都要移動(dòng)存活的對象,還要對引用這些對象的地方進(jìn)行更新,對象移動(dòng)的操作也需要全程暫停用戶(hù)線(xiàn)程 (Stop The World)。
  3.4 分代收集算法
  與其說(shuō)是算法,不如說(shuō)是理論。如今大多數虛擬機的實(shí)現版本都遵循了“分代收集”的理論進(jìn)行設計,這個(gè)理論可以看作是經(jīng)驗之談,因為開(kāi)發(fā)人員在開(kāi)發(fā)過(guò)程中發(fā)現了 JVM 中存活對象的數量和它們的年齡之間有著(zhù)某種規律,如下圖:
  
  JVM 中存活對象數量與年齡之間的關(guān)系
  在此基礎上,人們提出了以下假說(shuō):
  絕大多數對象都是朝生夕滅的。
  熬過(guò)越多次垃圾收集過(guò)程的對象就越難以消亡。
  根據這兩個(gè)假說(shuō),可以把 JVM 的堆內存大致分為新生代和老年代,新生代對象大多存活時(shí)間短,每次回收時(shí)只關(guān)注如何保留少量存活而不是去標記那些大量將要被回收的對象,就能以較低代價(jià)回收到大量的空間,所以這一區域一般采用標記-復制算法進(jìn)行垃圾收集,頻率比較高。而老年代則是一些難以消亡的對象,可以采用標記-清除和標記整理算法進(jìn)行垃圾收集,頻率可以低一些。
  按照 Hotspot 虛擬機的實(shí)現,針對新生代和老年代的垃圾收集又分為不同的類(lèi)型,也有不同的名詞,如下:
  部分收集 (Partial GC):指目標不是完整收集整個(gè)Java堆的垃圾收集,其中又分為:新生代收集 (Minor GC / Young GC):指目標只是新生代的垃圾收集。老年代收集 (Major GC / Old GC):指目標只是老年代的垃圾收集,目前只有CMS收集器的并發(fā)收集階段是單獨收集老年代的行為?;旌鲜占?(Mixed GC):指目標是收集整個(gè)新生代以及部分老年代的垃圾收集,目前只有G1收集器會(huì )有這種行為。
  整堆收集 (Full GC):收集整個(gè)Java堆和方法區的垃圾收集。
  人們經(jīng)常會(huì )混淆 Major GC 和 Full GC,不過(guò)這也有情可原,因為這兩種 GC 行為都包含了老年代的垃圾收集,而單獨的老年代收集 (Major GC) 又比較少見(jiàn),大多數情況下只要包含老年代收集,就會(huì )是整堆收集 (Full GC),不過(guò)還是分得清楚一點(diǎn)比較好哈。
  四、JVM 的內存分配和垃圾收集機制
  經(jīng)過(guò)前面的鋪墊,現在終于可以一窺 JVM 的內存分配和垃圾收集機制的真面目了。
  4.1 JVM 堆內存的劃分
  
  JVM 堆內存劃分,從Java 8開(kāi)始不再有永久代
  Java 堆是 JVM 所管理的內存中最大的一塊,也是垃圾收集器的管理區域。大多數垃圾收集器都會(huì )將堆內存劃分為上圖所示的幾個(gè)區域,整體分為新生代和老年代,比例為 1 : 2,新生代又進(jìn)一步分為 Eden、From Survivor 和 To Survivor,默認比例為 8 : 1 : 1,請注意,可通過(guò) SurvivorRatio 參數進(jìn)行設置。請注意,從 JDK 8 開(kāi)始,JVM 中已經(jīng)不再有永久代的概念了。Java 堆上的無(wú)論哪個(gè)區域,存儲的都只能是對象的實(shí)例,將Java 堆細分的目的只是為了更好地回收內存,或者更快地分配內存。
  4.2 分代收集原理4.2.1 新生代中對象的分配與回收
  大多數情況下,對象優(yōu)先在新生代 Eden 區中分配,當 Eden 區沒(méi)有足夠空間進(jìn)行分配時(shí),虛擬機將發(fā)起一次 Minor GC。Eden、From Survivor 和 To Survivor 的比例為 8 : 1 : 1,之所以按這個(gè)比例是因為絕大多數對象都是朝生夕滅的,垃圾收集時(shí) Eden 存活的對象數量不會(huì )太多,Survivor 空間小一點(diǎn)也足以容納,每次新生代中可用內存空間為整個(gè)新生代容量的90% (Eden 的 80% 加上 To Survivor 的 10%),只有From Survivor 空間,即 10% 的新生代是會(huì )被“浪費”的。不會(huì )像原始的標記-復制算法那樣浪費一半的內存空間。From Survivor 和 To Survivor 的空間并不是固定的,而是在 S0 和 S1 之間動(dòng)態(tài)轉換的,第一次 Minor GC 時(shí)會(huì )選擇 S1 作為 To Survivor,并將 Eden 中存活的對象復制到其中,并將對象的年齡加1,注意新生代使用的垃圾收集算法是標記-復制算法的改良版。下面是示意圖,請注意其中第一步的變色是為了醒目,虛擬機只做了標記存活對象的操作。
  第一次 Minor GC 示意圖
  在后續的 Minor GC 中,S0 和 S1會(huì )交替轉化為 From Survivor 和 To Survivor,Eden 和 From Survivor 中的存活對象會(huì )復制到 To Survivor 中,并將年齡加大 1。如下圖所示:
  
  后續 Minor GC 示意圖
  4.2.2 對象晉升老年代
  在以下這些情況下,對象會(huì )晉升到老年代。
  長(cháng)期存活對象將進(jìn)入老年代
  對象在 Survivor 區中每熬過(guò)一次Minor GC,年齡就增加1歲,當它的年齡增加到一定程度 (默認為15),就會(huì )被晉升到老年代中。對象晉升老年代的年齡閾值,可以通過(guò)參數 -XX:MaxTenuringThreshold 設置,這個(gè)參數的最大值是15,因為對象年齡信息儲存在對象頭中,占4個(gè)比特 (bit)的內存,所能表示最大數字就是15。
  長(cháng)期存活對象晉升老年代示意圖
  2.大對象可以直接進(jìn)入老年代
  對于大對象,尤其是很長(cháng)的字符串,或者元素數量很多的數組,如果分配在 Eden 中,會(huì )很容易過(guò)早占滿(mǎn) Eden 空間導致 Minor GC,而且大對象在 Eden 和兩個(gè) Survivor 之間的來(lái)回復制也還會(huì )有很大的內存復制開(kāi)銷(xiāo)。所以我們可以通過(guò)設置 -XX:PretenureSizeThreshold 的虛擬機參數讓大對象直接進(jìn)入老年代。
  3.動(dòng)態(tài)對象年齡判斷
  為了能更好地適應不同程序的內存狀況,HotSpot 虛擬機并不是永遠要求對象的年齡必須達到 -XX:MaxTenuringThreshold 才能晉升老年代,如果在 Survivor 空間中相同年齡所有對象大小的總和大于 Survivor 空間的一半,年齡大于或等于該年齡的對象就可以直接進(jìn)入老年代,無(wú)須等到 -XX:MaxTenuringThreshold 中要求的年齡。
  4.空間分配擔保 (Handle Promotion)
  當 Survivor 空間不足以容納一次 Minor GC 之后存活的對象時(shí),就需要依賴(lài)其他內存區域 (實(shí)際上大多數情況下就是老年代) 進(jìn)行分配擔保。在發(fā)生 Minor GC 之前,虛擬機必須先檢查老年代最大可用的連續空間是否大于新生代所有對象總空間,如果這個(gè)條件成立,那這一次 Minor GC 可以確保是安全的。如果不成立,則虛擬機會(huì )先查看 - XX:HandlePromotionFailure 參數的設置值是否允許擔保失敗 (Handle Promotion Failure);如果允許,那會(huì )繼續檢查老年代最大可用的連續空間是否大于歷次晉升到老年代對象的平均大小,如果大于,將嘗試進(jìn)行一次 Minor GC,盡管這次 Minor GC 是有風(fēng)險的;如果小于,或者-XX: HandlePromotionFailure設置不允許冒險,那這時(shí)就要改為進(jìn)行一次 Full GC。 查看全部

  一文詳解JVM垃圾收集機制,動(dòng)圖幫你輕松理解大廠(chǎng)面試難點(diǎn)
  前言
  上篇文章已經(jīng)給大家介紹了 JVM 的架構和運行時(shí)數據區 (內存區域),本篇文章將給大家介紹 JVM 的重點(diǎn)內容——垃圾收集。眾所周知,相比 C / C++ 等語(yǔ)言,Java 可以省去手動(dòng)管理內存的繁瑣操作,很大程度上解放了 Java 程序員的生產(chǎn)力,而這正是得益于 JVM 的垃圾收集機制和內存分配策略。我們平時(shí)寫(xiě)程序時(shí)并感知不到這一點(diǎn),但是如果是在生產(chǎn)環(huán)境中,JVM 的不同配置對于服務(wù)器性能的影響是非常大的,所以掌握 JVM 調優(yōu)是高級 Java 工程師的必備技能。正所謂“基礎不牢,地動(dòng)山搖”,在這之前我們先來(lái)了解一下底層的 JVM 垃圾收集機制。
  既然要介紹垃圾收集機制,就要搞清楚以下幾個(gè)問(wèn)題:
  哪些內存區域需要進(jìn)行垃圾收集?
  如何判斷對象是否可回收?
  新的對象是如何進(jìn)行內存分配的?
  如何進(jìn)行垃圾收集?
  本文將按以下行文結構展開(kāi),對上述問(wèn)題一一解答。
  需要進(jìn)行垃圾收集的內存區域;
  判斷對象是否可回收的方法;
  主流的垃圾收集算法介紹;
  JVM 的內存分配與垃圾收集機制。
  下面開(kāi)始正文,還是圖文并茂的老配方,走起。
  一、需要進(jìn)行垃圾收集的內存區域
  先來(lái)回顧一下 JVM 的運行時(shí)數據區:
  
  JVM 運行時(shí)數據區
  其中程序計數器、Java 虛擬機棧和本地方法棧都是線(xiàn)程私有的,與其對應的線(xiàn)程是共生關(guān)系,隨線(xiàn)程而生,隨線(xiàn)程而滅,棧中的棧幀也隨著(zhù)方法的進(jìn)入和退出井然有序地進(jìn)行入棧和出棧操作。所以這幾個(gè)區域的內存分配和回收都是有很大確定性的,在方法結束或線(xiàn)程結束時(shí),內存也會(huì )隨之釋放,因此也就不需要考慮這幾個(gè)區域的內存回收問(wèn)題了。
  而堆和方法區就不一樣了,Java 的對象幾乎都是在堆上創(chuàng )建出來(lái)的,方法區則存儲了被虛擬機加載的類(lèi)型信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼緩存等數據,方法區中的運行時(shí)常量池則存放了各種字面量與符號引用,上述的這些數據大部分都是在運行時(shí)才能確定的,所以需要進(jìn)行動(dòng)態(tài)的內存管理。
  還要說(shuō)明一點(diǎn),JVM 中的垃圾收集器的最主要的關(guān)注對象是 Java 堆,因為這里進(jìn)行垃圾收集的“性?xún)r(jià)比”是最高的,尤其是在新生代 (后文對分代算法進(jìn)行介紹) 中的垃圾收集,一次就可以回收 70% - 99% 的內存。而方法區由于垃圾收集判定條件,尤其是類(lèi)型卸載的判定條件相當苛刻,其回收性?xún)r(jià)比是非常低的,因此有些垃圾收集器就干脆不支持或不完全支持方法區的垃圾收集,比如 JDK 11 中的 ZGC 收集器就不支持類(lèi)型卸載。
  二、判斷對象是否可回收的方法2.1 引用計數法
  引用計數法的實(shí)現很簡(jiǎn)單,在對象中添加一個(gè)引用計數器,每當有一個(gè)地方引用它時(shí),計數器值就加一;當引用失效時(shí),計數器值就減一;任何時(shí)刻計數器為零的對象就是不可能再被使用的。大部分情況下這個(gè)方法是可以發(fā)揮作用的,但是在存在循環(huán)引用的情況下,引用計數法就無(wú)能為力了。比如下面這種情況:
  public class Student {<br /> // friend 字段<br /> public Student friend = null;<br /> <br /> public static void test() {<br /> Student a = new Student();<br /> Student b = new Student();<br /> a.friend = b;<br /> b.friend = a;<br /> a = null;<br /> b = null;<br /> System.gc();<br /> }<br />}
  上述代碼創(chuàng )建了 a 和 b 兩個(gè) Student 實(shí)例,并把它們各自的 friend 字段賦值為對方,除此之外,這兩個(gè)對象再無(wú)任何引用,然后將它們都賦值為 null,在這種情況下,這兩個(gè)對象已經(jīng)不可能再被訪(fǎng)問(wèn),但是它們因為互相引用著(zhù)對方,導致它們的引用計數都不為零,引用計數算法也就無(wú)法回收它們。如下圖所示:
  
  循環(huán)引用
  但是在 Java 程序中,a 和 b 是可以被回收的,因為 JVM 并沒(méi)有使用引用計數法判定對象是否可回收,而是采用了可達性分析法。
  2.2 可達性分析法
  這個(gè)算法的基本思路就是通過(guò)一系列稱(chēng)為“GC Roots”的根對象作為起始節點(diǎn)集 (GC Root Set),從這些節點(diǎn)開(kāi)始,根據引用關(guān)系向下搜索,搜索過(guò)程所走過(guò)的路徑稱(chēng)為“引用鏈” (Reference Chain),如果某個(gè)對象到GC Roots間沒(méi)有任何引用鏈相連,則說(shuō)明此對象不再被使用,也就可以被回收了。要進(jìn)行可達性分析就需要先枚舉根節點(diǎn) (GC Roots),在枚舉根節點(diǎn)過(guò)程中,為防止對象的引用關(guān)系發(fā)生變化,需要暫停所有用戶(hù)線(xiàn)程 (垃圾收集之外的線(xiàn)程),這種暫停全部用戶(hù)線(xiàn)程的行為被稱(chēng)為 (Stop The World)??蛇_性分析法如下圖所示:
  
  可達性分析法
  圖中綠色的都是位于 GC Root Set 中的 GC Roots,所有與其有關(guān)聯(lián)的對象都是可達的,被標記為藍色,而所有與其沒(méi)有任何關(guān)聯(lián)的對象都是不可達的,被標記為灰色。即使是不可達對象,也并非一定會(huì )被回收,如果該對象同時(shí)滿(mǎn)足以下幾個(gè)條件,那么它仍有“逃生”的可能:
  該對象有重寫(xiě)的finalize()方法 (Object 類(lèi)中的方法);
  finalize()方法中將其自身鏈接到了引用鏈上;
  JVM 此前沒(méi)有調用過(guò)該對象的finalize()方法 (因為 JVM 在收集可回收對象時(shí)會(huì )調用且僅調用一次該對象的finalize()方法)。
  不過(guò)由于finalize()方法的運行代價(jià)高昂,不確定性大,且無(wú)法保證各個(gè)對象的調用順序,所以并不推薦使用。那么 GC Roots 又是何方神圣呢?在 Java 語(yǔ)言中,固定可作為GC Roots的對象包括以下幾種:
  在虛擬機棧 (棧幀中的本地變量表) 中引用的對象,比如各個(gè)線(xiàn)程被調用的方法堆棧中使用到的參數、局部變量、臨時(shí)變量等。
  在方法區中類(lèi)靜態(tài)屬性引用的對象,比如Java類(lèi)的引用類(lèi)型靜態(tài)變量。
  在方法區中常量引用的對象,比如字符串常量池(String Table)里的引用。
  在本地方法棧中JNI (即通常所說(shuō)的Native方法) 引用的對象。
  Java虛擬機內部的引用,如基本數據類(lèi)型對應的Class對象,一些常見(jiàn)的異常對象 (比如
  NullPointExcepiton、OutOfMemoryError) 等,還有系統類(lèi)加載器。
  所有被同步鎖 (synchronized關(guān)鍵字) 持有的對象。
  反映Java虛擬機內部情況的 JM XBean、JVM TI 中注冊的回調、本地代碼緩存等。
  三、垃圾收集算法介紹3.1 標記-清除算法
  標記-清除算法的思想很簡(jiǎn)單,顧名思義,該算法的過(guò)程分為標記和清除兩個(gè)階段:首先標記出所有需要回收的對象,其中標記過(guò)程就是使用可達性分析法判斷對象是否屬于垃圾的過(guò)程。在標記完成后,統一回收掉所有被標記的對象,也可以反過(guò)來(lái),標記存活的對象,統一回收所有未被標記的對象。示意圖如下:
  
  標記清除算法
  這個(gè)算法雖然很簡(jiǎn)單,但是有兩個(gè)明顯的缺點(diǎn):
  執行效率不穩定。如果 Java 堆中包含大量對象,而且其中大部分是需要被回收的,這時(shí)必須進(jìn)行大量標記和清除的動(dòng)作,導致標記和清除兩個(gè)過(guò)程的執行效率都隨對象數量增長(cháng)而降低;
  導致內存空間碎片化。標記、清除之后會(huì )產(chǎn)生大量不連續的內存碎片,空間碎片太多可能會(huì )導致當以后在程序運行過(guò)程中需要分配較大對象時(shí)無(wú)法找到足夠的連續內存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作,非常影響程序運行效率。
  3.2 標記-復制算法
  標記-復制算法常簡(jiǎn)稱(chēng)復制算法,這一算法正好解決了標記-清除算法在面對大量可回收對象時(shí)執行效率低下的問(wèn)題。其實(shí)現方法也很易懂:在可用內存中劃分出兩塊大小相同的區域,每次只使用其中一塊,另一塊保持空閑狀態(tài),第一塊用完的時(shí)候,就把存活的對象全部復制到第二塊區域,然后把第一塊全部清空。如下圖所示:
  
  標記-復制算法
  這個(gè)算法很適合用于對象存活率低的情況,因為它只關(guān)注存活對象而無(wú)需理會(huì )可回收對象,所以 JVM 中新生代的垃圾收集正是采用的這一算法。但是其缺點(diǎn)也很明顯,每次都要浪費一半的內存,未免太過(guò)奢侈,不過(guò) JVM 中的新生代有更精細的內存劃分,比較好地解決了這個(gè)問(wèn)題,見(jiàn)下文。
  3.3 標記-整理算法
  這個(gè)算法完美解決了標記-清除算法的空間碎片化問(wèn)題,其標記過(guò)程與“標記-清除”算法一樣,但后續步驟不是直接對可回收對象進(jìn)行清理,而是讓所有存活的對象都向內存空間一端移動(dòng),然后直接清理掉邊界以外的內存。
  
  標記整理算法
  這個(gè)算法雖然可以很好地解決空間碎片化問(wèn)題,但是每次垃圾回收都要移動(dòng)存活的對象,還要對引用這些對象的地方進(jìn)行更新,對象移動(dòng)的操作也需要全程暫停用戶(hù)線(xiàn)程 (Stop The World)。
  3.4 分代收集算法
  與其說(shuō)是算法,不如說(shuō)是理論。如今大多數虛擬機的實(shí)現版本都遵循了“分代收集”的理論進(jìn)行設計,這個(gè)理論可以看作是經(jīng)驗之談,因為開(kāi)發(fā)人員在開(kāi)發(fā)過(guò)程中發(fā)現了 JVM 中存活對象的數量和它們的年齡之間有著(zhù)某種規律,如下圖:
  
  JVM 中存活對象數量與年齡之間的關(guān)系
  在此基礎上,人們提出了以下假說(shuō):
  絕大多數對象都是朝生夕滅的。
  熬過(guò)越多次垃圾收集過(guò)程的對象就越難以消亡。
  根據這兩個(gè)假說(shuō),可以把 JVM 的堆內存大致分為新生代和老年代,新生代對象大多存活時(shí)間短,每次回收時(shí)只關(guān)注如何保留少量存活而不是去標記那些大量將要被回收的對象,就能以較低代價(jià)回收到大量的空間,所以這一區域一般采用標記-復制算法進(jìn)行垃圾收集,頻率比較高。而老年代則是一些難以消亡的對象,可以采用標記-清除和標記整理算法進(jìn)行垃圾收集,頻率可以低一些。
  按照 Hotspot 虛擬機的實(shí)現,針對新生代和老年代的垃圾收集又分為不同的類(lèi)型,也有不同的名詞,如下:
  部分收集 (Partial GC):指目標不是完整收集整個(gè)Java堆的垃圾收集,其中又分為:新生代收集 (Minor GC / Young GC):指目標只是新生代的垃圾收集。老年代收集 (Major GC / Old GC):指目標只是老年代的垃圾收集,目前只有CMS收集器的并發(fā)收集階段是單獨收集老年代的行為?;旌鲜占?(Mixed GC):指目標是收集整個(gè)新生代以及部分老年代的垃圾收集,目前只有G1收集器會(huì )有這種行為。
  整堆收集 (Full GC):收集整個(gè)Java堆和方法區的垃圾收集。
  人們經(jīng)常會(huì )混淆 Major GC 和 Full GC,不過(guò)這也有情可原,因為這兩種 GC 行為都包含了老年代的垃圾收集,而單獨的老年代收集 (Major GC) 又比較少見(jiàn),大多數情況下只要包含老年代收集,就會(huì )是整堆收集 (Full GC),不過(guò)還是分得清楚一點(diǎn)比較好哈。
  四、JVM 的內存分配和垃圾收集機制
  經(jīng)過(guò)前面的鋪墊,現在終于可以一窺 JVM 的內存分配和垃圾收集機制的真面目了。
  4.1 JVM 堆內存的劃分
  
  JVM 堆內存劃分,從Java 8開(kāi)始不再有永久代
  Java 堆是 JVM 所管理的內存中最大的一塊,也是垃圾收集器的管理區域。大多數垃圾收集器都會(huì )將堆內存劃分為上圖所示的幾個(gè)區域,整體分為新生代和老年代,比例為 1 : 2,新生代又進(jìn)一步分為 Eden、From Survivor 和 To Survivor,默認比例為 8 : 1 : 1,請注意,可通過(guò) SurvivorRatio 參數進(jìn)行設置。請注意,從 JDK 8 開(kāi)始,JVM 中已經(jīng)不再有永久代的概念了。Java 堆上的無(wú)論哪個(gè)區域,存儲的都只能是對象的實(shí)例,將Java 堆細分的目的只是為了更好地回收內存,或者更快地分配內存。
  4.2 分代收集原理4.2.1 新生代中對象的分配與回收
  大多數情況下,對象優(yōu)先在新生代 Eden 區中分配,當 Eden 區沒(méi)有足夠空間進(jìn)行分配時(shí),虛擬機將發(fā)起一次 Minor GC。Eden、From Survivor 和 To Survivor 的比例為 8 : 1 : 1,之所以按這個(gè)比例是因為絕大多數對象都是朝生夕滅的,垃圾收集時(shí) Eden 存活的對象數量不會(huì )太多,Survivor 空間小一點(diǎn)也足以容納,每次新生代中可用內存空間為整個(gè)新生代容量的90% (Eden 的 80% 加上 To Survivor 的 10%),只有From Survivor 空間,即 10% 的新生代是會(huì )被“浪費”的。不會(huì )像原始的標記-復制算法那樣浪費一半的內存空間。From Survivor 和 To Survivor 的空間并不是固定的,而是在 S0 和 S1 之間動(dòng)態(tài)轉換的,第一次 Minor GC 時(shí)會(huì )選擇 S1 作為 To Survivor,并將 Eden 中存活的對象復制到其中,并將對象的年齡加1,注意新生代使用的垃圾收集算法是標記-復制算法的改良版。下面是示意圖,請注意其中第一步的變色是為了醒目,虛擬機只做了標記存活對象的操作。
  第一次 Minor GC 示意圖
  在后續的 Minor GC 中,S0 和 S1會(huì )交替轉化為 From Survivor 和 To Survivor,Eden 和 From Survivor 中的存活對象會(huì )復制到 To Survivor 中,并將年齡加大 1。如下圖所示:
  
  后續 Minor GC 示意圖
  4.2.2 對象晉升老年代
  在以下這些情況下,對象會(huì )晉升到老年代。
  長(cháng)期存活對象將進(jìn)入老年代
  對象在 Survivor 區中每熬過(guò)一次Minor GC,年齡就增加1歲,當它的年齡增加到一定程度 (默認為15),就會(huì )被晉升到老年代中。對象晉升老年代的年齡閾值,可以通過(guò)參數 -XX:MaxTenuringThreshold 設置,這個(gè)參數的最大值是15,因為對象年齡信息儲存在對象頭中,占4個(gè)比特 (bit)的內存,所能表示最大數字就是15。
  長(cháng)期存活對象晉升老年代示意圖
  2.大對象可以直接進(jìn)入老年代
  對于大對象,尤其是很長(cháng)的字符串,或者元素數量很多的數組,如果分配在 Eden 中,會(huì )很容易過(guò)早占滿(mǎn) Eden 空間導致 Minor GC,而且大對象在 Eden 和兩個(gè) Survivor 之間的來(lái)回復制也還會(huì )有很大的內存復制開(kāi)銷(xiāo)。所以我們可以通過(guò)設置 -XX:PretenureSizeThreshold 的虛擬機參數讓大對象直接進(jìn)入老年代。
  3.動(dòng)態(tài)對象年齡判斷
  為了能更好地適應不同程序的內存狀況,HotSpot 虛擬機并不是永遠要求對象的年齡必須達到 -XX:MaxTenuringThreshold 才能晉升老年代,如果在 Survivor 空間中相同年齡所有對象大小的總和大于 Survivor 空間的一半,年齡大于或等于該年齡的對象就可以直接進(jìn)入老年代,無(wú)須等到 -XX:MaxTenuringThreshold 中要求的年齡。
  4.空間分配擔保 (Handle Promotion)
  當 Survivor 空間不足以容納一次 Minor GC 之后存活的對象時(shí),就需要依賴(lài)其他內存區域 (實(shí)際上大多數情況下就是老年代) 進(jìn)行分配擔保。在發(fā)生 Minor GC 之前,虛擬機必須先檢查老年代最大可用的連續空間是否大于新生代所有對象總空間,如果這個(gè)條件成立,那這一次 Minor GC 可以確保是安全的。如果不成立,則虛擬機會(huì )先查看 - XX:HandlePromotionFailure 參數的設置值是否允許擔保失敗 (Handle Promotion Failure);如果允許,那會(huì )繼續檢查老年代最大可用的連續空間是否大于歷次晉升到老年代對象的平均大小,如果大于,將嘗試進(jìn)行一次 Minor GC,盡管這次 Minor GC 是有風(fēng)險的;如果小于,或者-XX: HandlePromotionFailure設置不允許冒險,那這時(shí)就要改為進(jìn)行一次 Full GC。

如何有效的采集微信公眾號文章

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 157 次瀏覽 ? 2022-05-05 05:10 ? 來(lái)自相關(guān)話(huà)題

  如何有效的采集微信公眾號文章
  有個(gè)人公眾號的用戶(hù)一般也有其他自媒體賬號,在花費了很長(cháng)時(shí)間寫(xiě)好了一篇文章以后終于可以放松一下,但是想著(zhù)還有好幾個(gè)其他平臺需要復制粘貼著(zhù)實(shí)頭大,這個(gè)工作既沒(méi)有技術(shù)含量也提現不出文筆。我們下面分享幾種獲取公眾號的方式。
  第一種:人工-手動(dòng)復制粘貼
  這也是最笨的一種方法,手動(dòng)切換平臺登錄,然后打開(kāi)編輯器復制粘貼,然后手動(dòng)發(fā)布。
  優(yōu)勢:如果有錯誤一目了然,不同的平臺有不同的限制,比如標題字數,封面圖片等。
  劣勢:手動(dòng)操作浪費人力,效率低下。
  第二種:手動(dòng)-用數據采集工具
  下載數據采集工具,手動(dòng)輸入鏈接可以下載文章內容,然后導出成word或者其他格式。
  優(yōu)勢:無(wú)需技術(shù)配合
  劣勢:需要手動(dòng)操作,先獲取內容,然后導出,然后登錄自己平臺手動(dòng)導入。
  第三種:技術(shù)-抓?。ㄗ咚压罚?br />   可以在程序中做定時(shí)獲取固定公眾號同步文章操作,通過(guò)搜狗瀏覽器搜索微信號,得到文章列表,然后文章列表中有文章的url,得到url后通過(guò)程序下載文章內容然后可以繼續后續操作。
  優(yōu)勢:減少人工操作,可實(shí)現半自動(dòng)化自動(dòng)發(fā)布
  劣勢:如果公眾號很多,請求太頻繁會(huì )有驗證碼出現,程序很難自動(dòng)識別,一旦驗證碼還需要手動(dòng)驗證。而且無(wú)法實(shí)時(shí)同步,即公眾號發(fā)完后不會(huì )立馬同步。具體延時(shí)多少根據自己的請求頻率決定。還有一點(diǎn)就是獲取的鏈接是臨時(shí)鏈接,必須在有效時(shí)間范圍內把文章內容請求下來(lái)。
  第四種:技術(shù)-抓?。ㄗ呶⑿藕笈_)
  在微信后臺文章編輯中有一個(gè)插入超鏈接的功能。通過(guò)這個(gè)接口可以搜索公眾號,然后得到歷史文章,得到文章鏈接后可以通過(guò)后臺程序下載下來(lái)。
  優(yōu)勢:減少人工操作,可實(shí)現半自動(dòng)化自動(dòng)發(fā)布。此鏈接是永久鏈接,任何時(shí)候都可用。
  劣勢:同第二種如果請求太頻繁也會(huì )有攔截導致接口無(wú)法調用。
  第五種:技術(shù)-通過(guò)一鍵建站推送
  你只需要在一鍵建站平臺中開(kāi)通數據采集功能,配置好你接收數據的接口,一旦公眾號中有新的文章發(fā)布將給你推送最新的內容
  優(yōu)勢:延時(shí)時(shí)間短。
  操作簡(jiǎn)單代碼量少再也不用擔心技術(shù)無(wú)法實(shí)現。
  真正實(shí)現完全托管,全自動(dòng)化。
  劣勢:付費版,免費額度很少。但是價(jià)格也好像不貴幾分錢(qián)一條吧。
  以上幾種方式都已經(jīng)親測過(guò)了,如果有更多更好的方式記得聯(lián)系我,我也好去試試,如果有需要幫助或者技術(shù)不明白的可以加我溝通交流。 查看全部

  如何有效的采集微信公眾號文章
  有個(gè)人公眾號的用戶(hù)一般也有其他自媒體賬號,在花費了很長(cháng)時(shí)間寫(xiě)好了一篇文章以后終于可以放松一下,但是想著(zhù)還有好幾個(gè)其他平臺需要復制粘貼著(zhù)實(shí)頭大,這個(gè)工作既沒(méi)有技術(shù)含量也提現不出文筆。我們下面分享幾種獲取公眾號的方式。
  第一種:人工-手動(dòng)復制粘貼
  這也是最笨的一種方法,手動(dòng)切換平臺登錄,然后打開(kāi)編輯器復制粘貼,然后手動(dòng)發(fā)布。
  優(yōu)勢:如果有錯誤一目了然,不同的平臺有不同的限制,比如標題字數,封面圖片等。
  劣勢:手動(dòng)操作浪費人力,效率低下。
  第二種:手動(dòng)-用數據采集工具
  下載數據采集工具,手動(dòng)輸入鏈接可以下載文章內容,然后導出成word或者其他格式。
  優(yōu)勢:無(wú)需技術(shù)配合
  劣勢:需要手動(dòng)操作,先獲取內容,然后導出,然后登錄自己平臺手動(dòng)導入。
  第三種:技術(shù)-抓?。ㄗ咚压罚?br />   可以在程序中做定時(shí)獲取固定公眾號同步文章操作,通過(guò)搜狗瀏覽器搜索微信號,得到文章列表,然后文章列表中有文章的url,得到url后通過(guò)程序下載文章內容然后可以繼續后續操作。
  優(yōu)勢:減少人工操作,可實(shí)現半自動(dòng)化自動(dòng)發(fā)布
  劣勢:如果公眾號很多,請求太頻繁會(huì )有驗證碼出現,程序很難自動(dòng)識別,一旦驗證碼還需要手動(dòng)驗證。而且無(wú)法實(shí)時(shí)同步,即公眾號發(fā)完后不會(huì )立馬同步。具體延時(shí)多少根據自己的請求頻率決定。還有一點(diǎn)就是獲取的鏈接是臨時(shí)鏈接,必須在有效時(shí)間范圍內把文章內容請求下來(lái)。
  第四種:技術(shù)-抓?。ㄗ呶⑿藕笈_)
  在微信后臺文章編輯中有一個(gè)插入超鏈接的功能。通過(guò)這個(gè)接口可以搜索公眾號,然后得到歷史文章,得到文章鏈接后可以通過(guò)后臺程序下載下來(lái)。
  優(yōu)勢:減少人工操作,可實(shí)現半自動(dòng)化自動(dòng)發(fā)布。此鏈接是永久鏈接,任何時(shí)候都可用。
  劣勢:同第二種如果請求太頻繁也會(huì )有攔截導致接口無(wú)法調用。
  第五種:技術(shù)-通過(guò)一鍵建站推送
  你只需要在一鍵建站平臺中開(kāi)通數據采集功能,配置好你接收數據的接口,一旦公眾號中有新的文章發(fā)布將給你推送最新的內容
  優(yōu)勢:延時(shí)時(shí)間短。
  操作簡(jiǎn)單代碼量少再也不用擔心技術(shù)無(wú)法實(shí)現。
  真正實(shí)現完全托管,全自動(dòng)化。
  劣勢:付費版,免費額度很少。但是價(jià)格也好像不貴幾分錢(qián)一條吧。
  以上幾種方式都已經(jīng)親測過(guò)了,如果有更多更好的方式記得聯(lián)系我,我也好去試試,如果有需要幫助或者技術(shù)不明白的可以加我溝通交流。

網(wǎng)絡(luò )爬蟲(chóng)實(shí)例系列 —— 搜狗微信文章采集方案

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 277 次瀏覽 ? 2022-05-05 04:05 ? 來(lái)自相關(guān)話(huà)題

  網(wǎng)絡(luò )爬蟲(chóng)實(shí)例系列 —— 搜狗微信文章采集方案
  微信和搜狗合作推出微信搜索后,讓各家媒體檢測平臺如獲珍寶,終于可以獲取到微信的文章數據了;但好景不長(cháng),由于大家基本只有這一個(gè)接口來(lái)獲取微信的數據,導致搜狗的壓力倍增,先后出現減少返回搜索結果條數,反爬蟲(chóng)輸入驗證碼的情況,導致大家又都很難抓取微信數據;不過(guò)后來(lái)搜狗又針對登錄用戶(hù)推出了關(guān)鍵字訂閱功能,結合這些情況,下面梳理了采集搜狗微信文章的兩個(gè)方案,僅供參考。
  方案一:利用搜狗微信文章搜索接口
  調用如下的URL便可訪(fǎng)問(wèn)搜狗微信關(guān)鍵字搜索文章的結果,其中參數query為搜索關(guān)鍵字,值為UTF-8編碼的字符串。該URL返回發(fā)結果為10條文章的搜索結果,可以直接解析到搜索出的文章名、文章URL、公眾號及摘要等信息。修改page參數的值,便可以進(jìn)行翻頁(yè),得到更多的搜索結果。
  %E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB&sut=13695&lkt=0%2C0%2C0&type=2&sst0=68&page=1&ie=utf8&w=01019900&dr=1
  優(yōu)點(diǎn):
  實(shí)現簡(jiǎn)單,搜索的結果和關(guān)鍵字匹配程度比較高
  缺點(diǎn):
  最多只能翻10頁(yè),而且網(wǎng)站反爬蟲(chóng)導致屏蔽比較嚴重
  方案二:利用搜狗微信提供的關(guān)鍵字訂閱接口
  在搜狗微信中,對于登錄的用戶(hù),搜索的關(guān)鍵字可以添加到訂閱列表中,下次可以直接查看自己訂閱關(guān)鍵字相關(guān)的文章。該功能限制每個(gè)登錄用戶(hù)最多可以添加20個(gè)訂閱關(guān)鍵字。
  A. 下面這個(gè)URL為獲取登錄用戶(hù)已經(jīng)訂閱關(guān)鍵字的列表,其中包括關(guān)鍵字和對應的wordId,該ID可以用于后續訪(fǎng)問(wèn)該關(guān)鍵字在訂閱中相關(guān)的文章。其中登錄用戶(hù)的標識是注冊的帳號,一參數uid傳入。
  uid=yongzhong15%&_=28
  B. 下面這個(gè)URL為指定的帳號添加訂閱關(guān)鍵字的接口,通過(guò)該接口,可以填加帳號訂閱的關(guān)鍵字。
  uid=yongzhong15%&word=%E5%9B%BE%E8%AE%BA&_=71
  C. 下面這個(gè)URL為指定的帳號刪除指定的關(guān)鍵字,其中關(guān)鍵字是指訂閱關(guān)鍵字列表中對應的wordId來(lái)指定
  uid=yongzhong15%&id=49529&_=72
  D. 下面這個(gè)URL返回指定用戶(hù)訂閱的其中一個(gè)關(guān)鍵字的文章列表,其中文件列表根據start參數進(jìn)行翻頁(yè),關(guān)鍵字是使用wordid來(lái)指定。
  uid=yongzhong15%&start=0&num=10&wordid=49528&clear=1&_=41
  根據以上A/B/C/D四類(lèi)API接口,便可以查詢(xún)注冊的帳號下的訂閱關(guān)鍵字列表,添加、刪除帳號下的關(guān)鍵字,并可以獲取到各個(gè)訂閱關(guān)鍵字對應的文章信息。
  優(yōu)點(diǎn):
  訂閱API由搜狗官方免費提供,不會(huì )被屏蔽
  缺點(diǎn):
  每個(gè)帳號最多只能訂閱20個(gè)關(guān)鍵字,使用不方便;而且對于每個(gè)關(guān)鍵字,返回的文章個(gè)數遠少于直接從搜索接口搜索到的相關(guān)文章數,即獲取到的數據不全。 查看全部

  網(wǎng)絡(luò )爬蟲(chóng)實(shí)例系列 —— 搜狗微信文章采集方案
  微信和搜狗合作推出微信搜索后,讓各家媒體檢測平臺如獲珍寶,終于可以獲取到微信的文章數據了;但好景不長(cháng),由于大家基本只有這一個(gè)接口來(lái)獲取微信的數據,導致搜狗的壓力倍增,先后出現減少返回搜索結果條數,反爬蟲(chóng)輸入驗證碼的情況,導致大家又都很難抓取微信數據;不過(guò)后來(lái)搜狗又針對登錄用戶(hù)推出了關(guān)鍵字訂閱功能,結合這些情況,下面梳理了采集搜狗微信文章的兩個(gè)方案,僅供參考。
  方案一:利用搜狗微信文章搜索接口
  調用如下的URL便可訪(fǎng)問(wèn)搜狗微信關(guān)鍵字搜索文章的結果,其中參數query為搜索關(guān)鍵字,值為UTF-8編碼的字符串。該URL返回發(fā)結果為10條文章的搜索結果,可以直接解析到搜索出的文章名、文章URL、公眾號及摘要等信息。修改page參數的值,便可以進(jìn)行翻頁(yè),得到更多的搜索結果。
  %E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB&sut=13695&lkt=0%2C0%2C0&type=2&sst0=68&page=1&ie=utf8&w=01019900&dr=1
  優(yōu)點(diǎn):
  實(shí)現簡(jiǎn)單,搜索的結果和關(guān)鍵字匹配程度比較高
  缺點(diǎn):
  最多只能翻10頁(yè),而且網(wǎng)站反爬蟲(chóng)導致屏蔽比較嚴重
  方案二:利用搜狗微信提供的關(guān)鍵字訂閱接口
  在搜狗微信中,對于登錄的用戶(hù),搜索的關(guān)鍵字可以添加到訂閱列表中,下次可以直接查看自己訂閱關(guān)鍵字相關(guān)的文章。該功能限制每個(gè)登錄用戶(hù)最多可以添加20個(gè)訂閱關(guān)鍵字。
  A. 下面這個(gè)URL為獲取登錄用戶(hù)已經(jīng)訂閱關(guān)鍵字的列表,其中包括關(guān)鍵字和對應的wordId,該ID可以用于后續訪(fǎng)問(wèn)該關(guān)鍵字在訂閱中相關(guān)的文章。其中登錄用戶(hù)的標識是注冊的帳號,一參數uid傳入。
  uid=yongzhong15%&_=28
  B. 下面這個(gè)URL為指定的帳號添加訂閱關(guān)鍵字的接口,通過(guò)該接口,可以填加帳號訂閱的關(guān)鍵字。
  uid=yongzhong15%&word=%E5%9B%BE%E8%AE%BA&_=71
  C. 下面這個(gè)URL為指定的帳號刪除指定的關(guān)鍵字,其中關(guān)鍵字是指訂閱關(guān)鍵字列表中對應的wordId來(lái)指定
  uid=yongzhong15%&id=49529&_=72
  D. 下面這個(gè)URL返回指定用戶(hù)訂閱的其中一個(gè)關(guān)鍵字的文章列表,其中文件列表根據start參數進(jìn)行翻頁(yè),關(guān)鍵字是使用wordid來(lái)指定。
  uid=yongzhong15%&start=0&num=10&wordid=49528&clear=1&_=41
  根據以上A/B/C/D四類(lèi)API接口,便可以查詢(xún)注冊的帳號下的訂閱關(guān)鍵字列表,添加、刪除帳號下的關(guān)鍵字,并可以獲取到各個(gè)訂閱關(guān)鍵字對應的文章信息。
  優(yōu)點(diǎn):
  訂閱API由搜狗官方免費提供,不會(huì )被屏蔽
  缺點(diǎn):
  每個(gè)帳號最多只能訂閱20個(gè)關(guān)鍵字,使用不方便;而且對于每個(gè)關(guān)鍵字,返回的文章個(gè)數遠少于直接從搜索接口搜索到的相關(guān)文章數,即獲取到的數據不全。

連載|淺談紅隊中的外網(wǎng)信息收集(一)

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 102 次瀏覽 ? 2022-05-01 14:10 ? 來(lái)自相關(guān)話(huà)題

  連載|淺談紅隊中的外網(wǎng)信息收集(一)
  前言
  最近在對以往所學(xué)習的有關(guān)紅隊的知識點(diǎn)進(jìn)行梳理總結,這里主要參考了 ATT&amp;CK 矩陣模型,不過(guò)對其進(jìn)行了簡(jiǎn)化,同時(shí)加入了一些國內特有的情況放了進(jìn)去。
  大體上會(huì )按照外網(wǎng)信息收集、打點(diǎn)、權限維持、提權、內網(wǎng)信息收集、橫向移動(dòng)、痕跡清理這樣的順序展開(kāi)。
  
  因為是梳理總結性的文章,所以文章的側重點(diǎn)在于「面」而不在于具體的某個(gè)「點(diǎn)」,因此文中的具體技術(shù)細節不會(huì )展開(kāi)去談,不然內容會(huì )很多。
  想了解具體細節的讀者可以去看看我的個(gè)人公眾號 TeamsSix 里的歷史文章,里面會(huì )有針對一些點(diǎn)展開(kāi)描述的文章。
  受限于個(gè)人水平,文中難免會(huì )出現錯誤或者描述不當的地方,還望在評論處指出,望諒解。
  確定目標
  當開(kāi)始做信息收集之前,肯定是要先確定目標的,在紅隊項目或者 HW 項目中,一般目標都是一個(gè)公司的名稱(chēng),然后通過(guò)這個(gè)公司的名稱(chēng)獲取各種信息,接著(zhù)開(kāi)展外網(wǎng)打點(diǎn)、內網(wǎng)滲透等等工作。
  在我們得知目標公司名稱(chēng)后,就可以開(kāi)展信息收集的工作了。
  外網(wǎng)信息收集
  我這里梳理了大概以下這些信息收集的方式,當然肯定是不全的,歡迎大家在評論區一起補充討論:
  
  1、組織股權結構
  拿到公司名稱(chēng)后,先不用急著(zhù)查備案、找域名,而是先看看這家公司的股權構成,因為一般一家公司的子公司也是可以作為目標去打的,不過(guò)有時(shí)是要求 50% 持股或者 100% 持股,這個(gè)就要看具體實(shí)際情況了。
  比較常見(jiàn)的查詢(xún)公司組織股權結構的網(wǎng)站有天眼查、企查查、愛(ài)企查、小藍本、釘釘企典等等。
  如果目標持股公司不多,可以直接看股權穿透圖,比較直觀(guān);如果持股公司比較多,股權穿透圖看著(zhù)就比較費力了。
  
  除了股權穿透之外,還可以看它的對外投資信息
  
  這兩個(gè)地方都可以查到有目標持股的公司。
  如果目標比較少一個(gè)一個(gè)子公司的去看還好,但如果目標很多,那這效率就很低了,好在現在也有了現成的工具。
  ENScanGo
  ENScanGo 是現有開(kāi)源項目 ENScan 的升級版本,工具地址:
  
  這是一款由狼組安全團隊的 Keac 師傅寫(xiě)的專(zhuān)門(mén)用來(lái)解決企業(yè)信息收集難的問(wèn)題的工具,可以一鍵收集目標及其控股公司的 ICP 備案、APP、小程序、微信公眾號等信息然后聚合導出。
  例如我這里搜集「北京百度網(wǎng)訊科技有限公司」以及他持股了 50% 的公司信息。
  enscan -n 北京百度網(wǎng)訊科技有限公司 -invest-num 50
  收集后的結果如下:
  
  這樣一來(lái),直接省去了收集 ICP 備案的步驟,一鍵獲得了目標公司及其子公司的公司名稱(chēng)、app、微信公眾號、ICP 備案等信息。
  2、主域名查詢(xún)
  主域名查詢(xún)可以分為備案域名查詢(xún)和未備案域名查詢(xún)。
  備案域名查詢(xún)
  除了上面從企業(yè)信息查詢(xún)網(wǎng)站中獲取到備案信息外,最全也是最準確的方法就是去國家的備案信息查詢(xún)網(wǎng)站里查詢(xún)了,地址為:。
  除了官方的渠道外,還有一些第三方的備案域名查詢(xún)站點(diǎn),比如站長(cháng)之家等等。
  未備案域名查詢(xún)
  有些企業(yè)會(huì )把自己的其他業(yè)務(wù)站點(diǎn)放在網(wǎng)站尾部,里面也許會(huì )包含未備案的站點(diǎn)。
  
  
  3、子域獲取
  比較常見(jiàn)的工具就是 OneForAll,除此之外還有 amass、subfinder、xray、ksubdomain 等等。
  如果提前知道目標,還可以提前收集一波子域,然后項目快開(kāi)始的時(shí)候,再收集一波子域,將兩次收集的結果做下對比,優(yōu)先打新增子域。
  4、端口掃描
  一般比較常見(jiàn)的可能就是 nmap 和 masscan 了,這里分享一個(gè) nmap 快速掃描全部端口的命令。
  nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -p 1-65535 -iL ip.txt -oX output.xml
  不過(guò)除了這些方法外,fscan 其實(shí)也可以拿來(lái)做外網(wǎng)的端口掃描,而且速度更快。
  比如用 fscan 只掃描端口,不做漏洞掃描
  fscan -hf hosts.txt --nopoc -t 100
  fscan 默認線(xiàn)程是 600,因為是外網(wǎng)掃描 600 的線(xiàn)程就比較大了,所以這里設置了 100,如果感覺(jué)掃描結果不理想,線(xiàn)程還可以設置的再小點(diǎn)。
  端口掃描可以結合空間搜索引擎的結果,如果通過(guò)空間搜索引擎發(fā)現目標存在很多的高位端口,那么在進(jìn)行端口掃描的時(shí)候就要記得也把這些高位端口加上。
  5、指紋識別
  指紋識別是我個(gè)人覺(jué)著(zhù)非常重要的一點(diǎn),因為指紋識別的結果對打點(diǎn)的幫助是很大的,可以讓打點(diǎn)更有針對性,同時(shí)也會(huì )節省很多時(shí)間。
  比較常見(jiàn)的在線(xiàn)指紋查詢(xún)網(wǎng)站有 godeye 和云悉等,工具有 observer_ward 和 Ehole 等。
  
  6、空間搜索引擎
  擅用空間搜索引擎,有時(shí)可以在最初剛拿確定目標的時(shí)候就發(fā)現目標弱點(diǎn)。
  目前比較常見(jiàn)的空間搜索引擎有 Fofa、Shodan、360 夸克、奇安信全球鷹、知道創(chuàng )宇 ZoomEye 等等。
  常見(jiàn)的工具有 Fofa_Viewer、FofaX、Kunyu,其中 Fofa_Viewer 為圖形化界面,使用友好。
  下載地址:
  
  FofaX 為命令行界面,FofaX 可以結合其他工具聯(lián)動(dòng)使用,除了這兩個(gè)工具調用 Fofa 接口外,上面提到的 Ehole 也可以調用 Fofa 查詢(xún)。
  Kunyu 調用的是 ZoomEye 的接口,工具下載地址:
  
  7、api 接口
  獲取 api 接口常用的工具有 jsinfo、findsomething、jsfinder、BurpJSLinkFinder 等等。
  如果找到了一些未授權接口,也許就可以搞到一些高價(jià)值信息,比如大量敏感信息泄露之類(lèi)的。
  另外在翻 js 文件的時(shí)候,可以關(guān)注下有沒(méi)有以 runtime 命名的 js 文件,因為在這種 js 文件中會(huì )包含其他 js 文件的名稱(chēng)(包括當前頁(yè)面沒(méi)有加載的 js 文件),這樣利用 runtime js 文件就發(fā)現了更多的 js 文件,使得找到 api 接口的概率又大了些。
  
  8、目錄獲取
  目錄掃描比較常用的工具有 dirsearch、ffuf
  ffuf 更側重于 FFUZ,不過(guò)不管是目錄掃描還是 FFUZ ,掃描的結果都在于字典,Github 上 4k 多個(gè) star 的字典:
  9、郵箱地址獲取
  郵箱地址比較常用的方法有直接通過(guò)搜索引擎找網(wǎng)上公開(kāi)的郵箱信息,這種往往會(huì )指向目標的網(wǎng)站中,比如目標某個(gè)網(wǎng)頁(yè)的附件中包含有郵箱等信息。
  之外還可以使用 Github 搜索目標公司開(kāi)發(fā)者在代碼中注釋的郵箱信息,其實(shí)不太明白為什么開(kāi)發(fā)者都喜歡把自己的郵箱注釋到代碼里。
  也可以通過(guò)領(lǐng)英找到目標公司的員工姓名,通過(guò)「拼音+@公司域名」的方法去構造員工郵箱。
  也有一些網(wǎng)站可以查詢(xún)郵箱,這種比較方便,比如以下網(wǎng)站:
  另外如果收集到了目標的 outlook 站點(diǎn),也可以嘗試去爆破郵箱用戶(hù)名。
  10、網(wǎng)盤(pán)信息
  網(wǎng)盤(pán)信息里有時(shí)也會(huì )發(fā)現不少好東西,這類(lèi)網(wǎng)站也很多,可以在愛(ài)達雜貨鋪導航站里找到很多網(wǎng)盤(pán)搜索類(lèi)站點(diǎn)。
  11、其他信息
  其他的信息比如 app、小程序、供應商、外包合作商、公眾號等,或多或少都可以從上面的組織股權架構類(lèi)網(wǎng)站中查詢(xún)到,或者使用 ENScan 也可以。
  其中比較值得注意是的供應商和外包合作商,如果拿下供應商也許可以直接進(jìn)入目標內網(wǎng),如果拿下外包合作商則可以借助這一層關(guān)系進(jìn)行社工或者嘗試進(jìn)入目標內網(wǎng)等操作。
  后記
  紅隊中的信息收集當然遠不止上面說(shuō)到的,其他比較常用的還有資產(chǎn)監控平臺、社工庫、自動(dòng)化信息收集工具(比如 ShuiZe)以及各種內部紅隊平臺等等,這里篇幅有限就不再展開(kāi)了。 查看全部

  連載|淺談紅隊中的外網(wǎng)信息收集(一)
  前言
  最近在對以往所學(xué)習的有關(guān)紅隊的知識點(diǎn)進(jìn)行梳理總結,這里主要參考了 ATT&amp;CK 矩陣模型,不過(guò)對其進(jìn)行了簡(jiǎn)化,同時(shí)加入了一些國內特有的情況放了進(jìn)去。
  大體上會(huì )按照外網(wǎng)信息收集、打點(diǎn)、權限維持、提權、內網(wǎng)信息收集、橫向移動(dòng)、痕跡清理這樣的順序展開(kāi)。
  
  因為是梳理總結性的文章,所以文章的側重點(diǎn)在于「面」而不在于具體的某個(gè)「點(diǎn)」,因此文中的具體技術(shù)細節不會(huì )展開(kāi)去談,不然內容會(huì )很多。
  想了解具體細節的讀者可以去看看我的個(gè)人公眾號 TeamsSix 里的歷史文章,里面會(huì )有針對一些點(diǎn)展開(kāi)描述的文章。
  受限于個(gè)人水平,文中難免會(huì )出現錯誤或者描述不當的地方,還望在評論處指出,望諒解。
  確定目標
  當開(kāi)始做信息收集之前,肯定是要先確定目標的,在紅隊項目或者 HW 項目中,一般目標都是一個(gè)公司的名稱(chēng),然后通過(guò)這個(gè)公司的名稱(chēng)獲取各種信息,接著(zhù)開(kāi)展外網(wǎng)打點(diǎn)、內網(wǎng)滲透等等工作。
  在我們得知目標公司名稱(chēng)后,就可以開(kāi)展信息收集的工作了。
  外網(wǎng)信息收集
  我這里梳理了大概以下這些信息收集的方式,當然肯定是不全的,歡迎大家在評論區一起補充討論:
  
  1、組織股權結構
  拿到公司名稱(chēng)后,先不用急著(zhù)查備案、找域名,而是先看看這家公司的股權構成,因為一般一家公司的子公司也是可以作為目標去打的,不過(guò)有時(shí)是要求 50% 持股或者 100% 持股,這個(gè)就要看具體實(shí)際情況了。
  比較常見(jiàn)的查詢(xún)公司組織股權結構的網(wǎng)站有天眼查、企查查、愛(ài)企查、小藍本、釘釘企典等等。
  如果目標持股公司不多,可以直接看股權穿透圖,比較直觀(guān);如果持股公司比較多,股權穿透圖看著(zhù)就比較費力了。
  
  除了股權穿透之外,還可以看它的對外投資信息
  
  這兩個(gè)地方都可以查到有目標持股的公司。
  如果目標比較少一個(gè)一個(gè)子公司的去看還好,但如果目標很多,那這效率就很低了,好在現在也有了現成的工具。
  ENScanGo
  ENScanGo 是現有開(kāi)源項目 ENScan 的升級版本,工具地址:
  
  這是一款由狼組安全團隊的 Keac 師傅寫(xiě)的專(zhuān)門(mén)用來(lái)解決企業(yè)信息收集難的問(wèn)題的工具,可以一鍵收集目標及其控股公司的 ICP 備案、APP、小程序、微信公眾號等信息然后聚合導出。
  例如我這里搜集「北京百度網(wǎng)訊科技有限公司」以及他持股了 50% 的公司信息。
  enscan -n 北京百度網(wǎng)訊科技有限公司 -invest-num 50
  收集后的結果如下:
  
  這樣一來(lái),直接省去了收集 ICP 備案的步驟,一鍵獲得了目標公司及其子公司的公司名稱(chēng)、app、微信公眾號、ICP 備案等信息。
  2、主域名查詢(xún)
  主域名查詢(xún)可以分為備案域名查詢(xún)和未備案域名查詢(xún)。
  備案域名查詢(xún)
  除了上面從企業(yè)信息查詢(xún)網(wǎng)站中獲取到備案信息外,最全也是最準確的方法就是去國家的備案信息查詢(xún)網(wǎng)站里查詢(xún)了,地址為:。
  除了官方的渠道外,還有一些第三方的備案域名查詢(xún)站點(diǎn),比如站長(cháng)之家等等。
  未備案域名查詢(xún)
  有些企業(yè)會(huì )把自己的其他業(yè)務(wù)站點(diǎn)放在網(wǎng)站尾部,里面也許會(huì )包含未備案的站點(diǎn)。
  
  
  3、子域獲取
  比較常見(jiàn)的工具就是 OneForAll,除此之外還有 amass、subfinder、xray、ksubdomain 等等。
  如果提前知道目標,還可以提前收集一波子域,然后項目快開(kāi)始的時(shí)候,再收集一波子域,將兩次收集的結果做下對比,優(yōu)先打新增子域。
  4、端口掃描
  一般比較常見(jiàn)的可能就是 nmap 和 masscan 了,這里分享一個(gè) nmap 快速掃描全部端口的命令。
  nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -p 1-65535 -iL ip.txt -oX output.xml
  不過(guò)除了這些方法外,fscan 其實(shí)也可以拿來(lái)做外網(wǎng)的端口掃描,而且速度更快。
  比如用 fscan 只掃描端口,不做漏洞掃描
  fscan -hf hosts.txt --nopoc -t 100
  fscan 默認線(xiàn)程是 600,因為是外網(wǎng)掃描 600 的線(xiàn)程就比較大了,所以這里設置了 100,如果感覺(jué)掃描結果不理想,線(xiàn)程還可以設置的再小點(diǎn)。
  端口掃描可以結合空間搜索引擎的結果,如果通過(guò)空間搜索引擎發(fā)現目標存在很多的高位端口,那么在進(jìn)行端口掃描的時(shí)候就要記得也把這些高位端口加上。
  5、指紋識別
  指紋識別是我個(gè)人覺(jué)著(zhù)非常重要的一點(diǎn),因為指紋識別的結果對打點(diǎn)的幫助是很大的,可以讓打點(diǎn)更有針對性,同時(shí)也會(huì )節省很多時(shí)間。
  比較常見(jiàn)的在線(xiàn)指紋查詢(xún)網(wǎng)站有 godeye 和云悉等,工具有 observer_ward 和 Ehole 等。
  
  6、空間搜索引擎
  擅用空間搜索引擎,有時(shí)可以在最初剛拿確定目標的時(shí)候就發(fā)現目標弱點(diǎn)。
  目前比較常見(jiàn)的空間搜索引擎有 Fofa、Shodan、360 夸克、奇安信全球鷹、知道創(chuàng )宇 ZoomEye 等等。
  常見(jiàn)的工具有 Fofa_Viewer、FofaX、Kunyu,其中 Fofa_Viewer 為圖形化界面,使用友好。
  下載地址:
  
  FofaX 為命令行界面,FofaX 可以結合其他工具聯(lián)動(dòng)使用,除了這兩個(gè)工具調用 Fofa 接口外,上面提到的 Ehole 也可以調用 Fofa 查詢(xún)。
  Kunyu 調用的是 ZoomEye 的接口,工具下載地址:
  
  7、api 接口
  獲取 api 接口常用的工具有 jsinfo、findsomething、jsfinder、BurpJSLinkFinder 等等。
  如果找到了一些未授權接口,也許就可以搞到一些高價(jià)值信息,比如大量敏感信息泄露之類(lèi)的。
  另外在翻 js 文件的時(shí)候,可以關(guān)注下有沒(méi)有以 runtime 命名的 js 文件,因為在這種 js 文件中會(huì )包含其他 js 文件的名稱(chēng)(包括當前頁(yè)面沒(méi)有加載的 js 文件),這樣利用 runtime js 文件就發(fā)現了更多的 js 文件,使得找到 api 接口的概率又大了些。
  
  8、目錄獲取
  目錄掃描比較常用的工具有 dirsearch、ffuf
  ffuf 更側重于 FFUZ,不過(guò)不管是目錄掃描還是 FFUZ ,掃描的結果都在于字典,Github 上 4k 多個(gè) star 的字典:
  9、郵箱地址獲取
  郵箱地址比較常用的方法有直接通過(guò)搜索引擎找網(wǎng)上公開(kāi)的郵箱信息,這種往往會(huì )指向目標的網(wǎng)站中,比如目標某個(gè)網(wǎng)頁(yè)的附件中包含有郵箱等信息。
  之外還可以使用 Github 搜索目標公司開(kāi)發(fā)者在代碼中注釋的郵箱信息,其實(shí)不太明白為什么開(kāi)發(fā)者都喜歡把自己的郵箱注釋到代碼里。
  也可以通過(guò)領(lǐng)英找到目標公司的員工姓名,通過(guò)「拼音+@公司域名」的方法去構造員工郵箱。
  也有一些網(wǎng)站可以查詢(xún)郵箱,這種比較方便,比如以下網(wǎng)站:
  另外如果收集到了目標的 outlook 站點(diǎn),也可以嘗試去爆破郵箱用戶(hù)名。
  10、網(wǎng)盤(pán)信息
  網(wǎng)盤(pán)信息里有時(shí)也會(huì )發(fā)現不少好東西,這類(lèi)網(wǎng)站也很多,可以在愛(ài)達雜貨鋪導航站里找到很多網(wǎng)盤(pán)搜索類(lèi)站點(diǎn)。
  11、其他信息
  其他的信息比如 app、小程序、供應商、外包合作商、公眾號等,或多或少都可以從上面的組織股權架構類(lèi)網(wǎng)站中查詢(xún)到,或者使用 ENScan 也可以。
  其中比較值得注意是的供應商和外包合作商,如果拿下供應商也許可以直接進(jìn)入目標內網(wǎng),如果拿下外包合作商則可以借助這一層關(guān)系進(jìn)行社工或者嘗試進(jìn)入目標內網(wǎng)等操作。
  后記
  紅隊中的信息收集當然遠不止上面說(shuō)到的,其他比較常用的還有資產(chǎn)監控平臺、社工庫、自動(dòng)化信息收集工具(比如 ShuiZe)以及各種內部紅隊平臺等等,這里篇幅有限就不再展開(kāi)了。

官方客服QQ群

微信人工客服

QQ人工客服


線(xiàn)

亚洲国产精品无码久久大片,亚洲AV无码乱码麻豆精品国产,亚洲品质自拍网站,少妇伦子伦精品无码STYLES,国产精久久久久久久