整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          如何在Tomcat中做TLS客戶端認(rèn)證

          如何在Tomcat中做TLS客戶端認(rèn)證

          見的https網(wǎng)站做的是服務(wù)端認(rèn)證(server authentication),瀏覽器通過證書判斷你所訪問的https://baidu.com是否真的是百度,而不是其他人偽造的網(wǎng)站。同時還對流量加密,防止別人竊聽你的流量。

          tls還可以做客戶端認(rèn)證(client authentication),即服務(wù)端判斷客戶端是否為其所信任的客戶端。由此可見,客戶端認(rèn)證用于那些需要受控訪問服務(wù)端。

          在數(shù)據(jù)中心中,有些服務(wù)是非常敏感的,那么我們要做到:

          1. 客戶端和我的流量是加密的,防止別人監(jiān)聽
          2. 客戶端能夠確認(rèn)所訪問的服務(wù)端的確是我們提供的服務(wù)端,而不是別人偽造的服務(wù)端
          3. 只有我信任的客戶端可以訪問我,防止惡意請求

          所以很明顯,前兩個問題可以通過服務(wù)端認(rèn)證解決,最后一個問題可以通過客戶端認(rèn)證解決。順便一提,如果要使用客戶端認(rèn)證就必須使用服務(wù)端認(rèn)證。

          先來講講概念然后舉個tomcat的例子講講怎么做。

          概念

          服務(wù)端認(rèn)證

          不論是做Server authentication還是Client authentication都需要證書。證書的來源有兩種:

          • 由權(quán)威CA簽發(fā),一般都是去購買。也可以使用let's encrypt申請免費(fèi)證書。
          • 自己簽發(fā)

          在一切可能的情況下都應(yīng)該使用權(quán)威CA簽發(fā)的證書,為什么這么建議?因?yàn)檫@里牽涉到一個信任問題,瀏覽器、編程語言SDK和某些工具都維護(hù)了一個信任CA證書清單,只要是由這些CA簽發(fā)的證書那就信任,否則就不信任。而這個鏈條是可以多級的,這里就不展開了。你只需要知道由信任CA簽發(fā)的所有證書都是可信的。比如JDK自帶的信任CA證書可以通過下面命令看到:

          keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts
          verisignclass2g2ca [jdk], 2016-8-25, trustedCertEntry,
          證書指紋 (SHA1): B3:EA:C4:47:76:C9:C8:1C:EA:F2:9D:95:B6:CC:A0:08:1B:67:EC:9D
          digicertassuredidg3 [jdk], 2016-8-25, trustedCertEntry,
          證書指紋 (SHA1): F5:17:A2:4F:9A:48:C6:C9:F8:A2:00:26:9F:DC:0F:48:2C:AB:30:89
          verisignuniversalrootca [jdk], 2016-8-25, trustedCertEntry,
          ...
          

          讓你輸密碼的時候輸入changeit。

          如果這個證書不是由信任CA簽發(fā)的(比如自己簽發(fā))會發(fā)生什么?瀏覽器、編程語言SDK、你所使用的工具會報告以下錯誤:

          curl:

          curl: (60) SSL certificate problem: self signed certificate in certificate chain
          

          Java:

          Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
           at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
           at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1964)
           at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:328)
           at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:322)
           at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1614)
          ...
          

          瀏覽器:

          這個錯誤實(shí)際上就是在告訴你這個證書不可信任,可能是一個偽造站點(diǎn),讓你小心點(diǎn)兒。如果這個證書由權(quán)威CA簽發(fā),那么就沒有這個問題了。但是權(quán)威CA簽發(fā)的證書要求申請人擁有域名,如果你這個服務(wù)是內(nèi)部使用的沒有域名,那就只能自己簽發(fā)了。那么如何解決上面的問題呢?你得把自己簽發(fā)的證書加入到信任CA證書清單里。

          下圖是權(quán)威CA簽發(fā)證書的示例:

          可以看到客戶端有一個truststore,這個就是存放信任CA證書的地方,服務(wù)端有一個keystore,存放的自己的證書及對應(yīng)的私鑰。

          下圖是自簽發(fā)證書的示例:

          在上面可以看到我們自己成為了一個Root CA,把它放到客戶端的truststore里。

          客戶端認(rèn)證

          前面講過客戶端認(rèn)證是服務(wù)端來驗(yàn)證客戶端是否可信的機(jī)制,其實(shí)做法和服務(wù)端認(rèn)證類似只不過方向相反。客戶端認(rèn)證大多數(shù)情況下只能是自簽發(fā)的(因?yàn)闆]有域名),雖然不是不可以從權(quán)威CA簽發(fā)但是存在一些問題。下面解釋為什么,假設(shè)權(quán)威CA是let's encrypt,然后服務(wù)端信任它簽發(fā)的所有證書。但是let's encrypt是阿貓阿狗都可以申請的,現(xiàn)在有一個黑客申請了這個證書,然后請求你的服務(wù)端,服務(wù)端就認(rèn)可了。

          上面這個問題可以用這個方法解決:比如你用let's encrypt申請了A證書,黑客用let's encrypt申請了B證書,你的服務(wù)端的truststore只信任A證書,那么黑客用B證書訪問你的時候就會被拒絕。但是這就帶來另一個問題,比如你在開發(fā)的時候客戶端證書有這么幾套:生產(chǎn)用、調(diào)試用、開發(fā)用,那么每次客戶端簽發(fā)一個證書都要更新到你的服務(wù)器的truststore里,這也太麻煩了。

          所以結(jié)合安全性和便利性,我們把自己變成Root CA,然后服務(wù)端信任它,這樣一來服務(wù)端就可以在開發(fā)的時候把Client Root CA內(nèi)置進(jìn)去,大大減輕了維護(hù)truststore的工作量,看下圖:

          用Tomcat舉個例子

          下面舉一個Tomcat做客戶端認(rèn)證的例子,因?yàn)槭菧y試用,所以服務(wù)端認(rèn)證也是用的自簽發(fā)證書。

          我們用了cfssl這個工具來生成證書。

          服務(wù)端

          先弄一套目錄:

          # 放自簽發(fā)的服務(wù)端CA根證書
          server-secrets/ca
          # 放自簽發(fā)的服務(wù)端的證書
          server-secrets/cert
          # 放服務(wù)端的keystore和truststore
          server-secrets/jks
          

          生成自簽名CA證書

          新建文件:server-secrets/ca/server-root-ca-csr.json

          內(nèi)容如下:

          {
           "key": {
           "algo": "rsa",
           "size": 2048
           },
           "names": [
           {
           "O": "Company",
           "OU": "Datacenter",
           "L": "Shanghai",
           "ST": "Shanghai",
           "C": "CN"
           }
           ],
           "CN": "server-root-ca"
          }
          

          運(yùn)行下面命令生成Server ROOT CA證書:

          cfssl gencert --initca=true ./server-root-ca-csr.json | cfssljson --bare server-root-ca
          

          會得到下面幾個文件:

          server-secrets/ca/
          ├── server-root-ca-key.pem
          ├── server-root-ca.csr
          └── server-root-ca.pem
          

          用下面命令驗(yàn)證證書:

          openssl x509 -in ./server-root-ca.pem -text -noout
          Certificate:
           Data:
           Version: 3 (0x2)
           Serial Number:
           0c:8a:1a:ca:da:fa:4c:17:6c:1f:42:40:4c:f1:90:f4:fd:1d:fe:58
           Signature Algorithm: sha256WithRSAEncryption
           Issuer: C=CN, ST=Shanghai, L=Shanghai, O=Company, OU=Datacenter, CN=server-root-ca
           Validity
           Not Before: Mar 27 05:14:00 2019 GMT
           Not After : Mar 25 05:14:00 2024 GMT
           Subject: C=CN, ST=Shanghai, L=Shanghai, O=Company, OU=Datacenter, CN=server-root-ca
          

          可以看到簽發(fā)人和被簽發(fā)人是同一個。

          生成自簽發(fā)證書

          新建文件 server-secrets/cert/server-gencert.json,內(nèi)容如下:

          {
           "signing": {
           "default": {
           "usages": [
           "signing",
           "key encipherment",
           "server auth"
           ],
           "expiry": "87600h"
           }
           }
          }
          

          可以看到我們會生成用來做server auth的證書。

          新建文件 server-secrets/cert/demo-csr.json,內(nèi)容如下:

          {
           "key": {
           "algo": "rsa",
           "size": 2048
           },
           "names": [
           {
           "O": "Company",
           "OU": "Datacenter",
           "L": "Shanghai",
           "ST": "Shanghai",
           "C": "CN"
           }
           ],
           "CN": "server-demo",
           "hosts": [
           "127.0.0.1",
           "localhost"
           ]
          }
          

          看上面的hosts,你可以根據(jù)自己的需要填寫域名或IP,這里因?yàn)槭潜镜匮菔舅允?27.0.0.1和localhost。

          運(yùn)行下面命令生成證書

          cfssl gencert \
           --ca ../ca/server-root-ca.pem \
           --ca-key ../ca/server-root-ca-key.pem \
           --config ./server-gencert.json \
           ./demo-csr.json | cfssljson --bare ./demo
          

          得到文件:

          server-secrets/cert/
          ├── demo-key.pem
          ├── demo.csr
          └── demo.pem
          

          驗(yàn)證結(jié)果:

          openssl x509 -in ./demo.pem -text -noout
          Certificate:
           Data:
           Version: 3 (0x2)
           Serial Number:
           1d:d0:51:97:6c:ce:ea:29:2a:f4:3b:3c:48:a3:69:b0:ef:f3:26:7b
           Signature Algorithm: sha256WithRSAEncryption
           Issuer: C=CN, ST=Shanghai, L=Shanghai, O=Company, OU=Datacenter, CN=server-root-ca
           Validity
           Not Before: Mar 27 05:17:00 2019 GMT
           Not After : Mar 24 05:17:00 2029 GMT
           Subject: C=CN, ST=Shanghai, L=Shanghai, O=Company, OU=Datacenter, CN=server-demo
          

          可以看到簽發(fā)者是server-root-ca,Subject是server-demo。

          將證書導(dǎo)入keystore

          到 server-secrets/jks,執(zhí)行下面命令生成pkcs12格式的keystore(JDK識別這個格式)

          openssl pkcs12 -export \
           -in ../cert/demo.pem \
           -inkey ../cert/demo-key.pem \
           -out server-demo.keystore \
           -name server-demo \
           -CAfile ../ca/server-root-ca.pem \
           -caname root -chain
          

          過程中會讓你輸入密碼,你就輸入:server-demo-ks。

          得到文件:

          server-secrets/jks/
          └── server-demo.keystore
          

          用JDK提供的keytool看看里面的內(nèi)容:

          keytool -list -keystore server-demo.keystore
          server-demo, 2019-3-27, PrivateKeyEntry,
          證書指紋 (SHA1): B2:E5:46:63:BB:00:E7:82:48:A4:2F:EC:01:41:CE:B4:4B:CE:68:7A
          

          讓你輸入密碼的時候就輸入:server-demo-ks。

          客戶端

          先弄一套目錄:

          # 放自簽發(fā)的客戶端CA根證書
          client-secrets/ca
          # 放自簽發(fā)的客戶端的證書
          client-secrets/cert
          # 放客戶端的keystore和truststore
          client-secrets/jks
          

          生成自簽名CA證書

          新建文件 client-secrets/ca/client-root-ca-csr.json:

          {
           "key": {
           "algo": "rsa",
           "size": 2048
           },
           "names": [
           {
           "O": "Company",
           "OU": "Datacenter",
           "L": "Shanghai",
           "ST": "Shanghai",
           "C": "CN"
           }
           ],
           "CN": "client-root-ca"
          }
          

          運(yùn)行下面命令生成Client ROOT CA證書:

          cfssl gencert --initca=true ./client-root-ca-csr.json | cfssljson --bare client-root-ca
          

          會得到下面幾個文件:

          client-secrets/ca/
          ├── client-root-ca-key.pem
          ├── client-root-ca.csr
          └── client-root-ca.pem
          

          用下面命令驗(yàn)證證書:

          openssl x509 -in ./client-root-ca.pem -text -noout
          Certificate:
           Data:
           Version: 3 (0x2)
           Serial Number:
           7e:fc:f3:53:07:1a:17:ae:24:34:d5:1d:00:02:d6:e4:24:09:92:12
           Signature Algorithm: sha256WithRSAEncryption
           Issuer: C=CN, ST=Shanghai, L=Shanghai, O=Company, OU=Datacenter, CN=client-root-ca
           Validity
           Not Before: Mar 27 05:20:00 2019 GMT
           Not After : Mar 25 05:20:00 2024 GMT
           Subject: C=CN, ST=Shanghai, L=Shanghai, O=Company, OU=Datacenter, CN=client-root-ca
          

          可以看到簽發(fā)人和被簽發(fā)人是同一個。

          生成自簽發(fā)證書

          新建文件 client-secrets/cert/client-gencert.json,內(nèi)容如下:

          {
           "signing": {
           "default": {
           "usages": [
           "signing",
           "key encipherment",
           "client auth"
           ],
           "expiry": "87600h"
           }
           }
          }
          

          可以看到我們會生成用來做client auth的證書。

          新建文件 client-secrets/cert/demo-csr.json,內(nèi)容如下:

          {
           "key": {
           "algo": "rsa",
           "size": 2048
           },
           "names": [
           {
           "O": "Company",
           "OU": "Datacenter",
           "L": "Shanghai",
           "ST": "Shanghai",
           "C": "CN"
           }
           ],
           "CN": "client-demo"
          }
          

          這里沒有hosts,這是因?yàn)槲覀儾恍枰眠@個證書來做服務(wù)端認(rèn)證。

          運(yùn)行下面命令生成證書

          cfssl gencert \
           --ca ../ca/client-root-ca.pem \
           --ca-key ../ca/client-root-ca-key.pem \
           --config ./client-gencert.json \
           ./demo-csr.json | cfssljson --bare ./demo
          

          得到文件:

          client-secrets/cert/
          ├── demo-key.pem
          ├── demo.csr
          └── demo.pem
          

          驗(yàn)證結(jié)果:

          openssl x509 -in ./demo.pem -text -noout
          Certificate:
           Data:
           Version: 3 (0x2)
           Serial Number:
           6e:50:e2:2c:02:bb:ef:fd:03:d9:2c:0a:8f:ba:90:65:fb:c4:b5:75
           Signature Algorithm: sha256WithRSAEncryption
           Issuer: C=CN, ST=Shanghai, L=Shanghai, O=Company, OU=Datacenter, CN=client-root-ca
           Validity
           Not Before: Mar 27 05:21:00 2019 GMT
           Not After : Mar 24 05:21:00 2029 GMT
           Subject: C=CN, ST=Shanghai, L=Shanghai, O=Company, OU=Datacenter, CN=client-demo
          

          可以看到簽發(fā)者是client-root-ca,Subject是client-demo。

          將證書導(dǎo)入keystore

          到 client-secrets/jks,執(zhí)行下面命令生成pkcs12格式的keystore(JDK識別這個格式)

          openssl pkcs12 -export \
           -in ../cert/demo.pem \
           -inkey ../cert/demo-key.pem \
           -out client-demo.keystore \
           -name client-demo \
           -CAfile ../ca/client-root-ca.pem \
           -caname root -chain
          

          過程中會讓你輸入密碼,你就輸入:client-demo-ks。

          得到文件:

          client-secrets/jks/
          └── client-demo.keystore
          

          用JDK提供的keytool看看里面的內(nèi)容:

          keytool -list -keystore client-demo.keystore
          client-demo, 2019-3-27, PrivateKeyEntry,
          證書指紋 (SHA1): 83:AE:0E:5E:0C:CE:86:C9:D1:84:D7:6F:87:F3:76:1F:B4:3E:46:31
          

          讓你輸入密碼的時候就輸入:client-demo-ks。

          兩端互信

          好了,到此為止server和client的證書都已經(jīng)生成了,接下來只需要將各自的root-ca添加到彼此都truststore中。

          把server-root-ca導(dǎo)入到client的truststore中

          cd client-secrets/jks
          keytool -importcert \
           -alias server-root-ca \
           -storetype pkcs12 \
           -keystore client.truststore \
           -storepass client-ts \
           -file ../../server-secrets/ca/server-root-ca.pem -noprompt
          

          注意上面的-storepass參數(shù),這個是trustore的密碼:client-ts。

          得到文件:

          client-secrets/jks/
          └── client.truststore
          

          用JDK提供的keytool看看里面的內(nèi)容:

          keytool -list -keystore client.truststore
          server-root-ca, 2019-3-27, trustedCertEntry,
          證書指紋 (SHA1): 75:E3:78:97:85:B2:29:38:25:3C:FD:EC:68:97:9B:78:A0:5F:BB:9D
          

          讓你輸入密碼的時候就輸入:client-ts。

          把client-root-ca導(dǎo)入到server的truststore中

          cd server-secrets/jks
          keytool -importcert \
           -alias client-root-ca \
           -storetype pkcs12 \
           -keystore server.truststore \
           -storepass server-ts \
           -file ../../client-secrets/ca/client-root-ca.pem -noprompt
          

          注意上面的-storepass參數(shù),這個是trustore的密碼:server-ts。

          得到文件:

          server-secrets/jks/
          └── server.truststore
          

          用JDK提供的keytool看看里面的內(nèi)容:

          keytool -list -keystore server.truststore
          client-root-ca, 2019-3-27, trustedCertEntry,
          證書指紋 (SHA1): 1E:95:2C:12:AA:7E:6D:E7:74:F1:83:C2:B8:73:6F:EE:57:FB:CA:46
          

          讓你輸入密碼的時候就輸入:server-ts。

          配置Tomcat

          好了,我們現(xiàn)在client和server都有了自己證書放在了自己的keystore中,而且把彼此的root-ca證書放到了自己的truststore里。現(xiàn)在我們弄一個tomcat作為server,然后為他配置SSL。

          修改tomcat/conf/server.xml,添加如下Connector:

          <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="150" SSLEnabled="true">
           <SSLHostConfig
           certificateVerification="required"
           truststoreFile="/path/to/server-secrets/jks/server.truststore"
           truststorePassword="server-ts" 
           truststoreType="PKCS12">
           <Certificate
           certificateKeyAlias="server-demo"
           certificateKeystoreFile="/path/to/server-secrets/demo-jks/server-demo.keystore"
           certificateKeystoreType="PKCS12"
           certificateKeystorePassword="server-demo-ks"
           type="RSA" />
           </SSLHostConfig>
          </Connector>
          

          可以看到我們開啟了客戶端認(rèn)證certificateVerification="required",也開啟了服務(wù)端認(rèn)證<Certificate>。記得修改上面的keystore和truststore的路徑。

          修改tomcat/conf/web.xml,添加如下元素:

          <security-constraint>
           <web-resource-collection>
           <web-resource-name>Automatic Forward to HTTPS/SSL</web-resource-name>
           <url-pattern>/*</url-pattern>
           </web-resource-collection>
           <user-data-constraint>
           <transport-guarantee>CONFIDENTIAL</transport-guarantee>
           </user-data-constraint>
          </security-constraint>
          

          這個作用是當(dāng)訪問8080端口時,都跳轉(zhuǎn)到8443端口,強(qiáng)制走HTTPS。

          啟動tomcat:

          tomcat/bin/catalina.sh run
          

          用curl測試

          好了,我們現(xiàn)在用curl來測試訪問一下:

          curl https://localhost:8443/
          curl: (60) SSL certificate problem: self signed certificate in certificate chain
          ...
          

          看到curl說服務(wù)端用的是一個自簽發(fā)的證書,不可信,也就是說服務(wù)端認(rèn)證失敗。添加--insecure試試:

          curl --insecure https://localhost:8443/
          curl: (35) error:1401E412:SSL routines:CONNECT_CR_FINISHED:sslv3 alert bad certificate
          

          這里就說明客戶端認(rèn)證失敗。

          所以如果要正確訪問得像下面這樣,指定server-root-ca證書,以及客戶端自己簽發(fā)的證書及private key:

          curl --cacert server-secrets/ca/server-root-ca.pem \
           --key client-secrets/cert/demo-key.pem \
           --cert client-secrets/cert/demo.pem \
           https://localhost:8443/
          <!DOCTYPE html>
          <html lang="en">
          ...
          

          Httpclient測試

          我們現(xiàn)在用Httpclient來訪問看看。pom.xml中添加依賴:

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

          Java代碼,記得把文件路徑改掉:

          import org.apache.http.HttpEntity;
          import org.apache.http.HttpException;
          import org.apache.http.client.methods.CloseableHttpResponse;
          import org.apache.http.client.methods.HttpGet;
          import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
          import org.apache.http.impl.client.CloseableHttpClient;
          import org.apache.http.impl.client.HttpClients;
          import org.apache.http.ssl.SSLContexts;
          import org.apache.http.util.EntityUtils;
          import javax.net.ssl.SSLContext;
          import java.io.File;
          import java.io.IOException;
          public class Client {
           public static void main(String[] args) throws Exception {
           SSLContext sslcontext=SSLContexts.custom()
           .loadTrustMaterial(
           new File("/path/to/client-secrets/demo-jks/client.truststore"),
           "client-ts".toCharArray()
           )
           .loadKeyMaterial(
           new File("/path/to/client-secrets/demo-jks/client-demo.keystore"),
           "client-demo-ks".toCharArray(),
           "client-demo-ks".toCharArray())
           .build();
           SSLConnectionSocketFactory sslsf=new SSLConnectionSocketFactory(
           sslcontext,
           SSLConnectionSocketFactory.getDefaultHostnameVerifier());
           CloseableHttpClient httpclient=HttpClients.custom()
           .setSSLSocketFactory(sslsf)
           .build();
           HttpGet httpGet=new HttpGet("https://localhost:8443");
           CloseableHttpResponse response=httpclient.execute(httpGet);
           try {
           System.out.println(response.getStatusLine());
           HttpEntity entity=response.getEntity();
           System.out.println(EntityUtils.toString(entity));
           } finally {
           response.close();
           }
           }
          }
          

          安全性考慮

          • 所有private key都很重要!如果它被泄漏了,就要回收它所對應(yīng)都證書。如果CA的private key泄漏了,那么用它簽發(fā)的所有證書都要被回收。
          • keystore和truststore的密碼設(shè)置的要復(fù)雜一些。

          關(guān)于反向代理

          因?yàn)榉?wù)端認(rèn)證所需要的證書直接配置在Tomcat上的,因此在做反向代理的時候不能使用SSL Termination模式,而是得使用SSL Passthrough模式。

          其他語言、SDK、工具

          上面講的方法不是只適用于Tomcat和Httpclient的,TLS的服務(wù)端認(rèn)證與客戶端認(rèn)證應(yīng)該在絕大部分的語言、SDK、類庫都有支持,請自行參閱文檔實(shí)踐。文中的keystore和truststore是Java特有的,不過不必迷惑,因?yàn)樗鼈儍H僅起到一個存放證書和private key的保險箱,有些語言或工具則是直接使用證書和private key,比如前面提到的curl。

          歡迎工作一到五年的Java工程師朋友們加入Java程序員開發(fā): 721575865

          群內(nèi)提供免費(fèi)的Java架構(gòu)學(xué)習(xí)資料(里面有高可用、高并發(fā)、高性能及分布式、Jvm性能調(diào)優(yōu)、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點(diǎn)的架構(gòu)資料)合理利用自己每一分每一秒的時間來學(xué)習(xí)提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!

          計用戶在線人數(shù)

          在統(tǒng)計用戶在人數(shù)的時候,我們用到了監(jiān)聽器,監(jiān)聽器大致分為以下三種:

          1. ServletRequestListener:用于監(jiān)聽請求的監(jiān)聽接口
          2. HttpSessionListener:用于監(jiān)聽會話的監(jiān)聽接口
          3. ServletContextListener:用于監(jiān)聽?wèi)?yīng)用的回話接口

          錯誤的統(tǒng)計辦法

          監(jiān)聽Request域

          這種統(tǒng)計辦法是錯誤的認(rèn)為每次刷新頁面后進(jìn)行進(jìn)行一次的count++運(yùn)算

          import javax.servlet.*;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.HttpSessionAttributeListener;
          import javax.servlet.http.HttpSessionEvent;
          import javax.servlet.http.HttpSessionListener;
          import javax.servlet.http.HttpSessionBindingEvent;
          
          @WebListener()
          public class MyRequestListener implements ServletRequestListener{
              private ServletContext sc;
              private Integer count;
              @Override
              //請求被初始化 Request
              public void requestInitialized(ServletRequestEvent sre) {
                  //獲取全局域
                  sc=sre.getServletContext();
                  //將count從全局域中獲取出來
                  count=(Integer) sc.getAttribute("count");
                  System.out.println(count);
                  count++;
                  System.out.println(count);
                  sc.setAttribute("count",count);
              }
          }
          
          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.HttpSessionAttributeListener;
          import javax.servlet.http.HttpSessionEvent;
          import javax.servlet.http.HttpSessionListener;
          import javax.servlet.http.HttpSessionBindingEvent;
          
          @WebListener()
          public class MyServletContextListener implements ServletContextListener{
              private ServletContext sc;
              @Override
              //Application被初始化的時候創(chuàng)建
              public void contextInitialized(ServletContextEvent sce) {
                  Integer count=0;
                  //獲取全局域
                  sc=sce.getServletContext();
                  //將count放入到全局域中
                  sc.setAttribute("count",count);
              }
          }
          
          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <html>
            <head>
              <title>$Title$</title>
            </head>
            <body>
            <center><h1>You are the ${applicationScope.count} customer to visit. </h1></center>
            </body>
          </html>
          

          這種錯誤地做法導(dǎo)致的是每刷新一次頁面 就會導(dǎo)致count進(jìn)行累加操作,最終產(chǎn)生錯誤的在線人數(shù),所以此時想到不應(yīng)該監(jiān)聽Request域,而應(yīng)該監(jiān)聽Session域。

          監(jiān)聽Session域

          在第二次監(jiān)聽Session域之后,發(fā)現(xiàn)每次刷新頁面后不改變count但是在啟動不同的瀏覽器后count++會實(shí)現(xiàn),但是,這樣做并不是我們要統(tǒng)計的在線人數(shù),所以此種做法錯誤。由于代碼只是將原來寫在Request監(jiān)聽器中的代碼轉(zhuǎn)移到Session監(jiān)聽器中,所以其他沒變的代碼將不重復(fù)。

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.HttpSessionAttributeListener;
          import javax.servlet.http.HttpSessionEvent;
          import javax.servlet.http.HttpSessionListener;
          import javax.servlet.http.HttpSessionBindingEvent;
          
          @WebListener()
          public class MySessionListener implements HttpSessionListener{
          
              private ServletContext sc;
              private Integer count;
          
              @Override
              //當(dāng)對話產(chǎn)生時激活此方法
              public void sessionCreated(HttpSessionEvent se) {
                  sc=se.getSession().getServletContext();
                  count=(Integer) sc.getAttribute("count");
                  count++;
                  sc.setAttribute("count",count);
              }
          }
          

          這時我們發(fā)現(xiàn)對于在線人數(shù)的統(tǒng)計,不是網(wǎng)頁訪問的次數(shù),也不是瀏覽器打開的個數(shù),對需求的理解的錯誤理解。所以正確的做法是統(tǒng)計其IP的數(shù)量,這樣的話,不管你在一臺電腦上開啟多少客戶端,都會只有一個。

          正確的統(tǒng)計方法

          統(tǒng)計其IP的數(shù)量,將IP的數(shù)量作為當(dāng)前的在線人數(shù),那么如何統(tǒng)計IP的數(shù)量呢?這樣將會導(dǎo)出以下問題:

          • 如何獲取用戶的IP?
          • IP將如何存儲?
          • 如何判斷IP之前已經(jīng)存在?

          現(xiàn)在來解決這些問題:

          • 只能從請求中獲取
          • 通過2、3問題,我們想到了集合(List),因?yàn)榧喜粌H可以存儲任何字符串,還可以通過遍歷來判斷之前是否有重復(fù)的IP出現(xiàn)。

          到了這里又冒出來一個問題集合(List)放到哪個域里呢?

          ServletContext域

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.HttpSessionAttributeListener;
          import javax.servlet.http.HttpSessionEvent;
          import javax.servlet.http.HttpSessionListener;
          import javax.servlet.http.HttpSessionBindingEvent;
          import java.util.ArrayList;
          import java.util.List;
          
          @WebListener()
          public class MyServletContextListener implements ServletContextListener{
              private ServletContext sc;
          
              @Override
              //Application被初始化的時候創(chuàng)建
              public void contextInitialized(ServletContextEvent sce) {
                  //創(chuàng)建一個鏈表來存儲IP
                  List<String> ips=new ArrayList<>();
                  sc=sce.getServletContext();
                  //將創(chuàng)建好的鏈表對象,放到Application域中
                  sc.setAttribute("ips",ips);
              }
          }
          

          由于IP只能在Request域中獲取,所以遍歷判斷在Request域中進(jìn)行。

          import javax.servlet.*;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.List;
          
          @WebListener()
          public class MyRequestListener implements ServletRequestListener{
          
              private HttpServletRequest sr;
              private String clientIp;
              private ServletContext sc;
              private List<String> ips;
              private HttpSession session;
          
              @Override
              //請求被初始化 Request
              public void requestInitialized(ServletRequestEvent sre) {
                  //從請求域中獲取IP
                  sr=(HttpServletRequest) sre.getServletRequest();
                  clientIp=sr.getRemoteAddr();
                  session=sr.getSession();
                  session.setAttribute("clientIp",clientIp);
          
                  //測試
                  // System.out.println("clientIp="+ clientIp);
                  //獲取Application域中的List
                  sc=sre.getServletContext();
                  ips=(List<String>) sc.getAttribute("ips");
                  //遍歷ips
                  for (String ip :
                          ips) {
                      if (clientIp.equals(ip))
                          return;
                  }
                  ips.add(clientIp);
                  sc.setAttribute("ips",ips);
              }
          }

          因?yàn)橐y(tǒng)計在線人數(shù),所以要設(shè)置退出按鈕,點(diǎn)擊退出按鈕之后,因?yàn)橐獜腖ist域中移除,所以使用Session域監(jiān)聽器來判斷session回話的關(guān)閉

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.List;
          
          @WebListener()
          public class MySessionListener implements HttpSessionListener{
          
              private ServletContext sc;
              private List<String> ips;
              private HttpSession session;
              private Object clientIp;
          
              @Override
              public void sessionDestroyed(HttpSessionEvent se) {
                  sc=se.getSession().getServletContext();
                  ips=(List<String>) sc.getAttribute("ips");
                  session=se.getSession();
                  clientIp=session.getAttribute("clientIp");
                  //刪除ip,如何獲取IP,但是不可以從session獲取到IP
                  //因?yàn)镾ession獲取不到Request
                  //一個Session包含多個Request
                  //一個Request只對應(yīng)一個Session 所以獲取不到,這時只能先從Request域中獲取到的ips,放置到Session域
                  //然后從Session 域中讀取
                  ips.remove(clientIp);
                  // session一失效就馬上將此IP從鏈表中移除是錯誤的
                  //應(yīng)該看此IP是否有另外的回話存在,如果有的話不能刪除
              }
          }
          

          此處代碼是頁面點(diǎn)擊關(guān)閉后,激活的退出方法

          import javax.servlet.ServletException;
          import javax.servlet.annotation.WebServlet;
          import javax.servlet.http.HttpServlet;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import javax.servlet.http.HttpSession;
          import java.io.IOException;
          
          @WebServlet(name="LogoutServlet",urlPatterns="/logoutServlet")
          public class LogoutServlet extends HttpServlet {
          
              private HttpSession session;
          
              protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                  //從域中獲取一個session,設(shè)置為false 如果域中存在一個session,則直接獲取,如果不存在,則返回一個空的session
                  session=request.getSession(false);
                  if (session !=null){
                      //使session失效
                      session.invalidate();
                      //失效后,需要進(jìn)行的操作,List鏈表中需要減去,用到了Session域監(jiān)聽器
                  }
              }
          
              protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                  doPost(request,response);
              }
          }
          

          在jsp頁面進(jìn)行讀取的時候,因?yàn)閕ps是以List鏈表的形式存在的,所以要想判斷當(dāng)前在線人數(shù),所以必須要判斷鏈表的長度,所以是applicationScope.ips.size()

          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <html>
            <head>
              <title>$Title$</title>
            </head>
            <body>
            <center><h1>You are the ${applicationScope.ips.size()} customer to visit. </h1><br>
              <h3><a href="${pageContext.request.contextPath}/logoutServlet">
                安全退出
              </a></h3>
            </center>
            </body>
          </html>
          

          好了?,這時候,程序?qū)懲炅耍绾闻袛嗄兀?/span>

          此時,我們的程序是部署在本地的Tomcat上的,對于本臺電腦,只有一個IP,如何實(shí)現(xiàn)多個IP呢?其實(shí)啊我們的電腦可以有三個IP,在訪問服務(wù)器的時候,服務(wù)器的IP多寫幾個,相當(dāng)于本機(jī)的IP多出來幾個。是哪三個IP呢?

          1、默認(rèn)clientIp : 0:0:0:0:0:0:0:1

          2、127.0.0.1

          這時大家可能會問127.0.0.1和localhost有什么區(qū)別呢,其實(shí)在這里要區(qū)分三個概念:

          localhost、127.0.0.1 和 本機(jī)IP之間的區(qū)別:

          • localhost等于127.0.0.1,不過localhost是域名,127.0.0.1是IP地址。
          • localhost和127.0.0.1不需要聯(lián)網(wǎng),都是本機(jī)訪問。
          • 本機(jī)IP需要聯(lián)網(wǎng),本機(jī)IP是本機(jī)或外部訪問, 本機(jī) IP 就是本機(jī)對外放開訪問的IP地址,這個網(wǎng)址就 是與物理網(wǎng)卡綁定的IP地址。

          3、IPv4地址:192.168.1.110

          這樣就很完美的實(shí)現(xiàn)了本地三個IP的測試。

          寫到這里,似乎已經(jīng)可以簡單的測試當(dāng)前在線人數(shù),也許仔細(xì)的人會發(fā)現(xiàn)在Session域被銷毀的方法中的注釋中發(fā)現(xiàn)一些貓膩。大家可以仔細(xì)想想,如果客戶端用不同的瀏覽器,相同的IP去訪問呢?點(diǎn)擊退出后,會不會出現(xiàn)錯誤情況呢?答案是會的。演示結(jié)果如下圖

          最完美的代碼

          所以在點(diǎn)擊退出登錄的按鈕之后,不可以直接將IP移除,要判斷有沒有另外的回話存在,如果有另外的回話存在,此IP是不可以刪掉的,問題由此變的復(fù)雜了,因?yàn)檫€要統(tǒng)計此IP所發(fā)出的會話有多少。

          整體思路:

          在全局域中,將不是直接將iP存放在List的鏈表中,而是以一個Map的形式存在,Map的鍵為String類型,Key為List類型,List中存放的是當(dāng)前IP所激發(fā)的會話對象,這樣就可以統(tǒng)計,一個IP觸發(fā)的sessions有多少個。

          通過調(diào)用Map的get方法,將當(dāng)前IP最為參數(shù),將可以獲取到他所激發(fā)的會話集合。但是,此集合可能為空,因?yàn)橛锌赡墚?dāng)前IP一次也沒有訪問此頁面,所以在List為空的時候好要創(chuàng)建一個ArrayList來存放sessions,然后將變化后的List重新寫回到Map,再將變化后的Map寫回到全局域中 。這樣創(chuàng)建過程基本完成。

          然后考慮銷毀過程,IP還需方法放到Session域中,當(dāng)session被銷毀的時候,應(yīng)該把當(dāng)前Session從List 中刪除,但是Map中此sessions對應(yīng)的IP可是不能直接刪,要判斷List中的sessions的個數(shù)(Entry對象),個數(shù)為1的時候才可以刪除,不然就不可以刪除。

          所以,要將當(dāng)前IP通過Request域存放到當(dāng)前Session域中,

          然后,要考慮的問題是,每次刷新頁面后sessions的個數(shù)會增加,這是錯誤的,原因是什么?

          答案是,因?yàn)樵诖娣舠essions的時候,創(chuàng)建數(shù)組直接進(jìn)行的添加,這樣的話,每次一刷新頁面,就會導(dǎo)致sessions的添加,所以在此之前應(yīng)該判斷,sessions中是否有此session,有的話直接跳出。

          這樣添加就沒問題了

          Servlet域中添加Map

          在Map中,需要使用鍵值對的方式,Key為IP,Value為List,那么List中存放什么呢?存放的是此IP發(fā)出的所有回話的HttpSession的對象,所以List的泛型是HttpSession。

          請求,在請求中,因?yàn)閷?dāng)前Session 對象存放到List中, List在Map中,Map在全局域中,所以首先得從全局域獲取到Map,然后,從Map中獲取由當(dāng)前IP所發(fā)出的所有Session的組成的List,判斷當(dāng)前的List是否為NULL,若為NULL,則創(chuàng)建List,否則,將當(dāng)前SessioncurrentSession放入List中。

          import javax.servlet.*;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.ArrayList;
          import java.util.List;
          import java.util.Map;
          
          @WebListener()
          public class MyRequestListener implements ServletRequestListener{
          
              private HttpServletRequest sr;
              private String clientIp;
              private ServletContext sc;
              private List<String> ips;
              private HttpSession currentSession;
              private Map<String,List<HttpSession>> map;
              private List<HttpSession> sessions;
          
          
              @Override
              //請求被初始化 Request
              public void requestInitialized(ServletRequestEvent sre) {
                  //從請求域中獲取IP
                  sr=(HttpServletRequest) sre.getServletRequest();
                  clientIp=sr.getRemoteAddr();
                  currentSession=sr.getSession();
                  //將當(dāng)前Session 對象存放到List中, List在Map中,Map在全局域中,
                  sc=sre.getServletContext();
                  map=(Map<String, List<HttpSession>>) sc.getAttribute("map");
                  //從Map中獲取由當(dāng)前IP所發(fā)出的所有Session的組成的List
                  sessions=map.get(clientIp);
                  //判斷當(dāng)前的List是否為NULL,若為NULL,則創(chuàng)建List,否則,將當(dāng)前Session放入List
                  if (sessions==null){
                      sessions=new ArrayList<>();
                  }
          //        遍歷List的session 對象,若有則不添加,若沒有則添加
                  for (HttpSession session :
                          sessions) {
                      if (session==currentSession)
                          return;
                  }
                  sessions.add(currentSession);
          
          
                  //將變化過的List重新寫回到Map
                  map.put(clientIp,sessions);
                  //再將變化的Map寫回到全局域中
                  sc.setAttribute("map",map);
          
                  //將當(dāng)前IP放入到當(dāng)前Session
                  currentSession.setAttribute("clientIp",clientIp);
              }
          
          }
          

          ServletContext

          這里將不使用ips了,所以將其刪除

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.ArrayList;
          import java.util.HashMap;
          import java.util.List;
          import java.util.Map;
          
          @WebListener()
          public class MyServletContextListener implements ServletContextListener{
              private ServletContext sc;
              @Override
              //Application被初始化的時候創(chuàng)建
              public void contextInitialized(ServletContextEvent sce) {
                  //創(chuàng)建一個Map,key為IP,value為該IP上所發(fā)出的會話的對象
                  Map<String,List<HttpSession>> map=new HashMap<>();
                  sc=sce.getServletContext();
                  //將map放到全局域中
                  sc.setAttribute("map",map);
              }
          }
          

          Session監(jiān)聽器

          接下來剖析Session的刪除工作,獲取當(dāng)前Session對象,這里有之前傳遞過來的IP,在進(jìn)行刪除操作的時候,要注意此處,刪除的是List中的sessions,刪除之后,還要判斷其IP的是否要刪除,如果List中沒有該元素,則說明當(dāng)前IP所發(fā)出的會話全部關(guān)閉,就可以從map中將當(dāng)前IP對應(yīng)的Entry對象刪除,否則,當(dāng)前IP所發(fā)出的會話任存在,那么使用put方法將變化過的List寫回到map。

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.List;
          import java.util.Map;
          
          @WebListener()
          public class MySessionListener implements HttpSessionListener{
          
              private ServletContext sc;
              private List<String> ips;
              private HttpSession currentSession;
              private String clientIp;
              private Map<String,List<HttpSession>> map;
              private List<HttpSession> sessions;
          
              @Override
              public void sessionDestroyed(HttpSessionEvent se) {
                  sc=se.getSession().getServletContext();
          
                  currentSession=se.getSession();
                  clientIp=(String) currentSession.getAttribute("clientIp");
                  map=(Map<String, List<HttpSession>>) sc.getAttribute("map");
                  //從Map中獲取List
                  sessions=map.get(clientIp);
                  //從List中刪除當(dāng)前Session對象
                  sessions.remove(currentSession);
                  //如果List中沒有該元素,則說明當(dāng)前IP所發(fā)出的會話全部關(guān)閉,就可以從map中
                  //將當(dāng)前IP對應(yīng)的Entry對象刪除
                  //若List中仍有元素,當(dāng)前IP所發(fā)出的會話任存在,那么將變化過的List寫回到map
                   if (sessions.size()==0){
                       map.remove(clientIp);
                   }else {
                       map.put(clientIp,sessions);
                   }
                   sc.setAttribute("map",map);
              }
          }
          

          因?yàn)樘幚淼耐顺龅捻撁?/span>/logoutServlet不需要做任何不同的處理,所以這里將不再重復(fù)。

          因?yàn)樵趈sp用到了JSP標(biāo)準(zhǔn)庫,所以到導(dǎo)兩個包。

          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
          <html>
            <head>
              <title>$Title$</title>
            </head>
            <body>
            <center><h1>You are the ${applicationScope.map.size()} customer to visit. </h1><br>
              <h3><a href="${pageContext.request.contextPath}/logoutServlet">
                安全退出
              </a><br></h3>
              <h2>
                <c:forEach items="${map}" var="entry">
                  ${entry.key }=${entry.value.size()}<br>
                </c:forEach>
              </h2>
            </center>
            </body>
          </html>
          

          最后 測試成功,這就是一個完美的統(tǒng)計當(dāng)前用戶的在線人數(shù)。

          來源:https://mp.weixin.qq.com/s/gM-lyh_9FeFKRRAJlPY83g

          佬勿噴,我就是一個腳本小子

          通過谷歌爬取要進(jìn)行sql注入的網(wǎng)站

          Google hack語法

          inurl:/search_results.php search=inurl:’Product.asp?BigClassName
          inurl:Article_Print.asp?
          inurl:NewsInfo.asp?id=inurl:EnCompHonorBig.asp?id=inurl:NewsInfo.asp?id=inurl:ManageLogin.asp
          inurl:Offer.php?idf=inurl:Opinions.php?id=inurl:Page.php?id=inurl:Pop.php?id=inurl:Post.php?id=inurl:Prod_info.php?id=inurl:Product-item.php?id=inurl:Product.php?id=inurl:Product_ranges_view.php?ID=inurl:Productdetail.php?id=inurl:Productinfo.php?id=inurl:Produit.php?id=inurl:Profile_view.php?id=inurl:Publications.php?id=inurl:Stray-Questions-View.php?num=inurl:aboutbook.php?id=inurl:ages.php?id=inurl:announce.php?id=inurl:art.php?idm=inurl:article.php?ID=inurl:asp?id=inurl:avd_start.php?avd=inurl:band_info.php?id=inurl:buy.php?category=inurl:category.php?id=inurl:channel_id=inurl:chappies.php?id=inurl:clanek.php4?id=inurl:clubpage.php?id=inurl:collectionitem.php?id=inurl:communique_detail.php?id=inurl:curriculum.php?id=inurl:declaration_more.php?decl_id=inurl:detail.php?ID=inurl:download.php?id=inurl:downloads_info.php?id=inurl:event.php?id=inurl:faq2.php?id=inurl:fellows.php?id=inurl:fiche_spectacle.php?id=inurl:forum_bds.php?num=inurl:galeri_info.php?l=inurl:gallery.php?id=inurl:game.php?id=inurl:games.php?id=inurl:historialeer.php?num=inurl:hosting_info.php?id=inurl:humor.php?id=

          谷歌爬取腳本

          import requests
          from lxml import etree
          import time
          def create_requests(page,data):
              url="https://www.google.com/search?q="+data+"&lr=lang_zh-CN&start={}".format(page)+"&ie=utf-8"
              header={
                  "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
              }
              response=requests.get(url=url,headers=header)#proxies=proxy
              response.encoding="utf-8"
              context=response.text
              return context
          def parse_data(context):
              parse=etree.HTML(context)
              data=parse.xpath('//*/<span>@href')#CTL{n} </span>   f=open(r"url.txt","a")
              for url in data:
                  if "/search?" in url:
                      continue
                  if "google.com" in url:
                      continue
                  if ".jpg" in url:
                      continue
                  if "jpeg" in url:
                      continue
                  if "png" in url:
                      continue
                  if "#" in url:
                      continue
                  if "%" in url:
                      continue
                  if "url=https://" in url:
                      continue
                  if "url=http://" in url:
                      continue
                  if ".pdf" in url:
                      continue
                  if ".htm" in url:
                      continue
                  if ".htmls" in url:
                      continue
                  if ".html" in url:
                      continue
                  if ".gov.cn" in url:
                      continue
                      break
                  f.write(url+"\n")
                  print(url+"成功寫入")
              f.close()
          if __name__=='__main__':
              data=input("請輸入Google語法:")
              print("----------------------開始抓取----------------------")
              for page in range(0,int(input("請輸入結(jié)束頁面的倍數(shù):")),10):
                  context=create_requests(page,data)
                  parse_data(context)
              print("----------------------抓取完畢----------------------")
          

          需要使用魔法,如果沒有魔法還有一種方法,使用谷歌鏡像站,通過fofa搜索

          fofa語法:

          title="Google" && region="HK"
          title=="Google" && server=="cloudflare"
          

          進(jìn)入谷歌鏡像站,輸入谷歌hacking語法搜索


          上面的腳本把谷歌網(wǎng)址換成鏡像站網(wǎng)址一樣使用,然后保存以下腳本

          import os
          import shutil
          
          def get_exists(path):
              sum=0
              for root, dirs, files in os.walk(path, topdown=False):  # 使用topdown=False以便于刪除目錄
                  log_path=os.path.join(root, "log")
                  if os.path.isfile(log_path) and os.path.getsize(log_path)==0:
                      shutil.rmtree(root)
                      dirs[:]=[]  # 清空dirs列表,因?yàn)橐呀?jīng)刪除了當(dāng)前目錄
                  else:
                      sum +=1
                      print(root + "\t注入成功")
              return sum
          
          if __name__=='__main__':
              path=r"result"
              print("--------------------開啟執(zhí)行--------------------")
              sum=get_exists(path)
              print(f"--------------------{sum}注入點(diǎn)--------------------")
          

          調(diào)用上面的腳本去看一下sqlmap的日志文件,如果sqlmap成功發(fā)現(xiàn)注入點(diǎn)會有日志文件,日志文件不是空的,沒有注入點(diǎn),日志文件就為空


          下面這個腳本就是全自動的,自動運(yùn)行sqlmap和查看注入點(diǎn),腳本運(yùn)行完sqlmap就會自動調(diào)用下一個命令,最好和sqlmap在同一個文件夾


          主站蜘蛛池模板: 久久国产精品一区| 久久青草精品一区二区三区| 国精产品一区一区三区MBA下载| 亚洲成AV人片一区二区密柚| 国产激情一区二区三区 | 亚洲人成网站18禁止一区| 国产精品亚洲不卡一区二区三区 | 综合久久久久久中文字幕亚洲国产国产综合一区首 | 日韩精品无码Av一区二区| 精品一区二区三区3d动漫| 一区二区三区视频在线播放| 一区二区三区视频网站| 国产免费一区二区视频| 麻豆视频一区二区三区| 大屁股熟女一区二区三区| 在线精品一区二区三区电影| 精品综合一区二区三区| 九九久久99综合一区二区| 日韩中文字幕精品免费一区| 国产无吗一区二区三区在线欢 | 成人区精品一区二区不卡亚洲| 精品一区二区三区波多野结衣| 精品一区精品二区制服| 亚洲国产成人精品久久久国产成人一区二区三区综 | 国产成人精品无码一区二区老年人| 变态调教一区二区三区| 精品人妻AV一区二区三区| 日韩视频在线观看一区二区| 久久久精品人妻一区二区三区四| 国产成人久久一区二区不卡三区| 日本人真淫视频一区二区三区| 精品一区二区三区在线视频| 麻豆精品人妻一区二区三区蜜桃 | 久久久久久人妻一区二区三区 | 91精品福利一区二区三区野战| 高清一区二区三区视频| 国产精品高清一区二区三区不卡| 国产精品亚洲综合一区| 午夜在线视频一区二区三区| 中文字幕亚洲综合精品一区| 亚洲毛片αv无线播放一区|