SSL/TLS协议详解 - https为什么比http更安全

news2025/1/17 3:54:41

概述

  • 首先纠正一个错误,可能很多初学者都以为HTTPS跟HTTP一样,都属于应用层协议。但其实HTTPS并不是一个单独的协议。HTTPS是安全版本的HTTP,简单理解 HTTPS = HTTP + SSL/TLS,即HTTPS就是使用SSL/TLS协议对HTTP报文进行了加密处理。
    在这里插入图片描述
  • 我们发送HTTP请求时,不管直接从网页,还是使用抓包工具,可以很方便的拿到报文。但是发送HTTPS请求时,报文是经过加密处理的。因此说HTTPS比HTTP更加安全。
  • 接下来就介绍下SSL/TLS协议的握手过程,看看如何通过SSL/TLS协议对应用数据进行加密。

SSL/TLS简介

  • SSL (Secure Sockets Layer)安全套接层,是由Netscape公司于1990年开发,用于保障Word Wide Web(WWW)通讯的安全。主要任务是提供私密性,信息完整性和身份认证。1994年改版为 SSLv2,1995年改版为SSLv3
  • TLS(Transport Layer Security)安全传输层协议,用于在两个通信应用程序之间提供保密性和数据完整性。该标准协议是由IETF 于 1999年颁布,整体来说 TLS 非常类似 SSLv3,只是对 SSLv3 做了些增加和修改。

TLS握手过程

  • TLS握手涉及到加密算法和数字证书的知识。不了解的可参考我的另外两篇文章。
    • 加密算法简介
    • 数字证书简介
  • 握手图示
client server 生成客户端随机数 Client Hello 客户端随机数,客户端TLS版本,密码套件列表 生成服务端随机数 Server Hello 服务端随机数,确认TLS版本号,确认密码套件 Server Certificate 发送服务端证书 Server Hello Done 生成预备主密钥PreMaster 使用服务端证书中的公钥加密 预主密钥得到Encrypted PreMaster Client Key Exchange 发送Encrypted PreMaster 使用私钥解密Encrypted PreMaster 得到预备主密钥PreMaster 使用客户端随机数,服务端随机数 预备主密钥 计算出主密钥 使用客户端随机数,服务端随机数 预备主密钥 计算出主密钥 Change Cipher Spec 密钥协商完成 Finished Change Cipher Spec 密钥协商完成 Finished Application Data 应用数据加密通信 Application Data 应用数据加密通信 client server
  • 握手详解
    • [1] Client Hello
      • 客户端发送TLS版本,支持的加密套件列表给服务端,并生成客户端随机数发送给服务端。
      • Version : 表示客户端支持的SSL/TLS协议版本
      • Random : 客户端随机数
      • Session ID : 和会话恢复有关
      • Cipher Suites : 客户端支持的密码套件列表
      • Compression Methods : 客户端支持的压缩方法
      • Extension : 扩展项
    • [2] Server Hello
      • 服务端确认TLS版本,选择使用的加密套件,生成服务端随机数发送给客户端。
      • Version : 服务端根据客户端传递的版本号,选择一个双方都支持的版本
      • Random : 服务端随机数
      • Session ID : 和会话恢复有关
      • Cipher Suite : 根据客户端传递过来的密码套件列表,选择一个双方都支持的密码套件
      • Compression Method : 压缩算法
      • Extension : 扩展项
    • [3] Server Certificate
      • 该消息是可选的。根据协商出来的密码套件,服务端选择是否发送证书消息。
    • [4] Server Key Exchange
      • 该消息是有条件才发送的。如果证书包含的信息不足以进行密钥交换,那么必须发送该消息。
    • [5] Server Hello Done
      • 表示服务端发送了足够的消息,接下来等待和客户端协商出预备主密钥。
    • [6] Client Key Exchange
      • 接收到服务端的Server Hello Done消息后,客户端发送此消息。该消息的主要作用就是协商出预备主密钥。这里只介绍使用RSA密码套件的流程。
      • 客户端生成一个48位的预备主密钥,然后用服务端证书中的公钥加密并发送给服务端。最终发送的消息就是Encrypted PreMaster。
    • [7] 计算主密钥和密钥块
      • 客户端根据预备主密钥,客户端随机数,服务端随机数计算出主密钥。服务端使用服务端证书中的私钥解密客户端发送过来的Encrypted PreMaster,得到预备主密钥,根据相同的方法计算出主密钥。主密钥的长度固定是48字节。
      • master_secret = PRF(pre_master_secret, “master secret”, ClientHello.random + ServerHello.random)
      • 计算出主密钥后,还需要根据主密钥计算出密钥块。密钥块主要有六个
      • Client MAC Key
      • Server MAC Key
      • Client Key
      • Server Key
      • Client IV
      • Server IV
      • MAC Key主要用于数据的完整性校验,Key用于加密数据。IV作为加密算法的初始化向量。
    • [8] Change Cipher Spec
      • 通知对方,可以用协商好的密钥进行通信了。
    • [9] Finished
      • 确认所有握手消息没有被篡改。
    • [10] Application Data
      • 使用密钥块加密通信

openssl实现tls通信

  • openssl提供了SSL/TLS协议库,下面通过代码实现下。
  • linux下执行这两个命令安装openssl。
    • sudo apt-get install openssl
    • sudo apt-get install libssl-dev

证书生成

  • 编码实现前,需要用到三个证书。CA证书,服务端证书,客户端证书。可以直接使用openssl命令行工具来生成。

  • CA证书

    • 生成RSA私钥
    • openssl genrsa -out ca.key 1024
    • 生成CA证书
    • openssl req -new -x509 -key ca.key -out ca.crt -days 365
  • 服务端证书

    • 生成服务端密钥
    • openssl genrsa -out server.key 1024
    • 生成证书请求文件
    • openssl req -new -key server.key -out server.csr
    • 使用CA证书签发服务端证书
    • openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt -days 365
  • 客户端证书

    • 生成客户端密钥
    • openssl genrsa -out client.key 1024
    • 生成证书请求文件
    • openssl req -new -key client.key -out client.csr
    • 使用CA证书签发客户端证书
    • openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt -days 365

代码

  • 服务端代码
    •   #include <stdio.h>
        #include <stdlib.h>
        #include <memory.h>
        #include <errno.h>
        #include <sys/types.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>
        #include <unistd.h>
        #include <openssl/rsa.h>     
        #include <openssl/crypto.h>
        #include <openssl/x509.h>
        #include <openssl/pem.h>
        #include <openssl/ssl.h>
        #include <openssl/err.h>
        #include <openssl/rand.h>
      
        #define CERTF   "server.crt" /*服务端的证书(需经CA签名)*/
        #define KEYF   "server.key"  /*服务端的私钥(建议加密存储)*/
        #define CACERT "ca.crt" /*CA 的证书*/
        #define PORT   10088   /*准备绑定的端口*/
      
        int main (){
            SSL_load_error_strings();            /*为打印调试信息作准备*/
            OpenSSL_add_ssl_algorithms();        /*初始化*/
      
            SSL_CTX* ctx = SSL_CTX_new(TLSv1_server_method());
            if(ctx == NULL) {
                printf("SSL_CTX_new failed.\n");
                return -1;
            }
      
            SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);   /*验证与否*/
            SSL_CTX_load_verify_locations(ctx,CACERT,NULL); /*若验证,则放置CA证书*/
      
            if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            if (!SSL_CTX_check_private_key(ctx)) {
                printf("Private key does not match the certificate public key\n");
                return -1;
            }
      
            SSL_CTX_set_cipher_list(ctx,"RC4-MD5"); 
      
            /*开始正常的TCP socket过程.*/
            printf("Begin TCP socket...\n");
      
            int listenSock = socket(AF_INET, SOCK_STREAM, 0);  
            if(listenSock == -1){
                perror("socket");
                return -1;
            }
      
            struct sockaddr_in sa_serv;
            memset (&sa_serv, 0, sizeof(sa_serv));
            sa_serv.sin_family = AF_INET;
            sa_serv.sin_addr.s_addr = INADDR_ANY;
            sa_serv.sin_port = htons(PORT);         
      
            if(bind(listenSock, (struct sockaddr*) &sa_serv, sizeof (sa_serv)) == -1){
                perror("bind");
                return -1;
            }
      
            /*接受TCP链接*/
            if(listen (listenSock, 5) == -1){
                perror("listen");
                return -1;
            }                   
      
            struct sockaddr_in sa_cli;
            socklen_t client_len = sizeof(sa_cli);
            int connfd = accept (listenSock, (struct sockaddr*) &sa_cli, &client_len);
            if(connfd == -1){
                perror("accept");
                close (listenSock);
                return -1;
            }
      
            printf ("[%s:%d] connected...\n", inet_ntoa(sa_cli.sin_addr), sa_cli.sin_port);
      
            /*TCP连接已建立,进行服务端的SSL过程. */
            printf("Begin server side SSL\n");
      
            SSL* ssl = SSL_new (ctx);
            if(ssl == NULL){
                printf("SSL_new failed.\n");
                return -1;
            }
      
            SSL_set_fd (ssl, connfd);
            
            int sslSock = SSL_accept (ssl);
            if(sslSock == -1){
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            printf("SSL_accept finished\n");
      
            /*打印所有加密算法的信息(可选)*/
            printf ("SSL connection using %s\n", SSL_get_cipher(ssl));
      
            /*得到客户端的证书并打印些信息(可选) */
            X509* client_cert = SSL_get_peer_certificate (ssl);
            if (client_cert != NULL) {
                printf ("Client certificate:\n");
      
                char* subStr = X509_NAME_oneline(X509_get_subject_name (client_cert), 0, 0);
                if(subStr == NULL){
                printf("X509_NAME_oneline subject failed.\n");
                return -1;
                }
      
                printf ("subject: %s\n", subStr);
                //Free (subStr);
      
                char* issStr = X509_NAME_oneline(X509_get_issuer_name  (client_cert), 0, 0);
                if(issStr == NULL){
                printf("X509_NAME_oneline subject failed.\n");
                return -1;
                }
      
                printf ("issuer: %s\n", issStr);
                //Free (issStr);
      
                X509_free (client_cert);/*如不再需要,需将证书释放 */
            }else{
                printf ("Client does not have certificate\n");
            }
                
            char buf[4096] = {0};  
            /* 数据交换开始,用SSL_write,SSL_read代替write,read */
            int readSize = SSL_read(ssl, buf, sizeof(buf) - 1);  
            if(readSize == -1){
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            printf ("SSL_read buf[%d] = {%s}\n", readSize, buf);
      
            if(SSL_write (ssl, "Welcome to Connect to Server!", strlen("Welcome to Connect to Server!")) == -1){
                ERR_print_errors_fp(stderr);
                return -1;
            } 
      
            shutdown (connfd, 2);
            SSL_free (ssl);
            SSL_CTX_free (ctx);
            return 0;
        }
      
  • 客户端代码
    •   #include <stdio.h>
        #include <stdlib.h>
        #include <memory.h>
        #include <errno.h>
        #include <sys/types.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>
        #include <unistd.h>
        #include <openssl/rsa.h>     
        #include <openssl/crypto.h>
        #include <openssl/x509.h>
        #include <openssl/pem.h>
        #include <openssl/ssl.h>
        #include <openssl/err.h>
        #include <openssl/rand.h>
      
        /*所有需要的参数信息都在此处以#define的形式提供*/
        #define CERTF  "client.crt"  /*客户端的证书(需经CA签名)*/
        #define KEYF  "client.key"   /*客户端的私钥(建议加密存储)*/
        #define CACERT "ca.crt"      /*CA 的证书*/
        #define PORT   10088          /*服务端的端口*/
        // #define SERVER_ADDR "182.92.205.179"  /*服务段的IP地址*/
        #define SERVER_ADDR "127.0.0.1"  /*服务段的IP地址*/
      
        int main () {
            /*初始化*/
            OpenSSL_add_ssl_algorithms(); 
      
            //载入所有SSL错误消息
            SSL_load_error_strings();     
      
            /*采用什么协议(SSLv2/SSLv3/TLSv1)在此指定*/
            /*申请SSL会话环境*/
            SSL_CTX *ctx = SSL_CTX_new(TLSv1_client_method());
            if(ctx == NULL){
                printf("SSL_CTX_new failed!\n");
                return -1;
            }                       
            
            /*验证与否,是否要验证对方*/
            SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
      
            /*若验证对方,则放置CA证书*/
            SSL_CTX_load_verify_locations(ctx,CACERT,NULL); 
      
            /*加载自己的证书*/
            if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
                ERR_print_errors_fp(stderr);
                return -1;
            }
            
            /*加载自己的私钥,以用于签名*/
            if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            /*调用了以上两个函数后,检验一下自己的证书与私钥是否配对*/
            if (!SSL_CTX_check_private_key(ctx)) {
                printf("Private key does not match the certificate public key\n");
                return -1;
            } 
      
            /*以下是正常的TCP socket建立过程*/
            printf("Begin tcp socket...\n");
      
            int sock = socket(AF_INET, SOCK_STREAM, 0);
            if(sock == -1){
                perror("socket");
                return -1;
            }       
      
            struct sockaddr_in sa;
            memset (&sa, 0, sizeof(sa));
            sa.sin_family = AF_INET;
            sa.sin_addr.s_addr = inet_addr(SERVER_ADDR);   /* Server IP */
            sa.sin_port = htons(PORT);          /* Server Port number */
      
            if(connect(sock, (struct sockaddr*) &sa, sizeof(sa)) == -1){
                perror("connect");
                return -1;
            } 
      
            /* TCP 链接已建立.开始 SSL 握手过程 */
            printf("Begin SSL negotiation \n");
      
            /*申请一个SSL套接字*/
            SSL* ssl = SSL_new (ctx);
            if(ssl == NULL){
                printf("SSL_new failed.\n");
                return -1;
            }
      
            /*绑定读写套接字*/
            SSL_set_fd(ssl, sock);
      
            if(SSL_connect(ssl) == -1){
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            /*打印所有加密算法的信息(可选)*/
            printf ("SSL connection using %s\n", SSL_get_cipher(ssl));
      
            /*得到服务端的证书并打印些信息(可选) */
            X509* server_cert = SSL_get_peer_certificate (ssl); 
            if(server_cert == NULL){
                printf("SSL_get_peer_certificate failed.\n");
                return -1;
            }     
            
            printf ("Server certificate:\n");
      
            char* subStr = X509_NAME_oneline(X509_get_subject_name (server_cert),0,0);
            if(subStr == NULL){
                printf("X509_NAME_oneline subject failed.\n");
                return -1;
            }
      
            printf("subject: %s\n", subStr);
            // Free(subStr);
      
            char* issStr = X509_NAME_oneline (X509_get_issuer_name(server_cert),0,0);
            if(issStr == NULL){
                printf("X509_NAME_oneline issuer failed.\n");
                return -1;
            }
      
            printf ("issuer: %s\n", issStr);
            // Free (issStr);
      
            X509_free(server_cert);  /*如不再需要,需将证书释放 */
      
            /* 数据交换开始,用SSL_write,SSL_read代替write,read */
            printf("Begin SSL data exchange\n");
      
            if(SSL_write(ssl, "Hello, I am client!", strlen("Hello, I am client!")) == -1){
                ERR_print_errors_fp(stderr);
                return -1;
            } 
      
            char buf[4096] = {0};
            int readSize = SSL_read(ssl, buf, sizeof(buf) - 1); 
            if(readSize == -1){
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            printf ("SSL_read buf[%d] = {%s}\n", readSize, buf);
      
            SSL_shutdown (ssl);  /* send SSL/TLS close_notify */
      
            shutdown (sock, 2);
            SSL_free (ssl);
            SSL_CTX_free (ctx);
      
            return 0;
        }
      
  • Makefile
    •   all: server client
        server: server.cpp
            g++ -o server server.cpp -lssl -lcrypto
        client: client.cpp
            g++ -o client client.cpp -lssl -lcrypto
      
        clean:
            rm server client
      

使用wireShark抓包分析

  • 接下来就使用wireShark抓包工具来分析下SSL/TLS协议的握手过程
    在这里插入图片描述

  • 通过上图可以看出,在SSL/TLS握手前后,还是要进行TCP三次握手和四次挥手。这个不再介绍TCP的握手过程,不清楚的可查看我这篇文章 : 抓包分析TCP协议

  • 接下来详细看下具体的SSL/TLS握手内容

  • Client Hello

    • TCP三次握手完成,客户端发送一个Client Hello消息,开始进行SSL/TLS握手
    • 客户端发送支持的TLS版本,支持的密码套件列表和一个客户端随机数。
      在这里插入图片描述
  • Server Hello

    • 服务端确认TLS版本,并从客户端发送来的加密套件列表中选择一个加密套件,再发送一个服务端随机数
      在这里插入图片描述
  • Server Certificate

    • 服务端发送自己的证书
      在这里插入图片描述
  • Server Hello Done

    • 服务端消息发送完毕,等待与客户端协商主密钥
      在这里插入图片描述
  • Client Key Exchange

    • 客户端将加密后的预备主密钥发送给服务端
      在这里插入图片描述
  • Change Cipher Spec

    • 通知对方,可以使用协商好的主密钥进行加密通信
      在这里插入图片描述
  • Finished

    • 即Encrypted Handshake Message消息。确认握手消息没有被篡改。
  • Application Data

    • 应用数据进行加密发送
      在这里插入图片描述

参考资料

  • 《HTTPS权威指南》
  • 《深入浅出HTTPS从原理到实战》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/162066.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一不小心进入“决赛圈”,没“阳过”的怎么办?

前两天看到一个央视新闻&#xff0c;说河南新冠病毒感染率接近90%&#xff0c;流行毒株是奥密克戎BA.5.2&#xff0c;数字让我挺吃惊的&#xff1a;真有这么多人感染了&#xff1f; 如果是这样&#xff0c;我就属于那幸运的10%了&#xff0c;没有经历过刀片嗓&#xff0c;水泥鼻…

机器学习--模型调参、超参数优化、网络架构搜索

目录 一、模型调参 手动调超参数 多次调参的管理 机器调参与人调参的成本比较 自动调参&#xff08;AutoML&#xff09; 总结 二、超参数优化 在搜索空间中选择超参数 HPO算法有哪些 Black-Box Multi-Fidelity 总结 三、网络架构搜索 Neural Architecture Sear…

【Java AWT 图形界面编程】LayoutManager 布局管理器 ② ( FlowLayout 流式布局 )

文章目录一、FlowLayout 流式布局二、FlowLayout 流式布局 API三、FlowLayout 流式布局代码示例1、FlowLayout 流式布局左对齐代码示例及执行效果2、FlowLayout 流式布局居中对齐代码示例及执行效果2、FlowLayout 流式布局右对齐代码示例及执行效果一、FlowLayout 流式布局 Flo…

上半年要完成的博客60

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…

RT-Thread系列--对象容器

一、目的RT-Thread号称小而美&#xff0c;那具体美在哪里呢&#xff1f;下面我们就来说说首先&#xff0c;代码简练逻辑清晰明了&#xff1b;其次是框架设计。其创新性的对象容器系统是我认为最优美的设计亮点&#xff0c;本篇的目的就是给大家介绍一下对象容器的设计以及这样设…

sklearn聚类之谱聚类SpectralClustering

文章目录基本原理sklearn中谱聚类的构造实战基本原理 谱聚类是一种基于图论的聚类方法&#xff0c;所谓图&#xff0c;就是将空间中的所有点连接起来&#xff0c;只要这些连接中出现了一个圈&#xff0c;就可以称之为图。如果把这些连线加上一个权重&#xff0c;就叫做加权图。…

小程序开发经验分享(6)开发流程大全

一、准备的资料 我们在开发微信小程序前,需要准备下相关资料。这个资料主要是后面进行ICP备案,微信小程序认证以及申请支付接口时需要用到。 资料如下: 营业执照【个体经营户或公司都行】银行开户许可证【个体经营户可以用法人个人银行卡】,企业网银【最好开通】。相关类…

车载以太网 - DoIP实体状态信息AliveCheck - 08

我们知道车载以太网的实体在车内一般常见的就是网关和节点,而网关和节点的状态信息对我们的以太网通信尤为重要,我们只有知道节点或者网关所处的状态,才能更好的进行下一步处理或者诊断,今天我们就来详细的了解下DoIP实体的状态信息这部分在ISO 13400中的定义。 DoIP实体状…

多图解析manacher算法原理

什么是manacher算法 用于快速计算一个字符串的最长回文子串 什么是最长回文子串&#xff1f; 例如&#xff1a;abc12321中&#xff0c;最长回文子串为12321&#xff0c;即子字符串中最长&#xff0c;且是回文的那个 怎么用暴力做法找出最长回文子串呢&#xff1f; 长度为奇…

线程等待,线程休眠,线程状态

线程等待:因为线程与线程之间&#xff0c;调度顺序是完全不确定&#xff0c;它取决于操作系统本身调度器的一个实现&#xff0c;但是有时候我们希望这个顺序是可控的&#xff0c;此时的线程等待&#xff0c;就是一种方法&#xff0c;用来控制线程结束的先后顺序&#xff1b; 1)…

神经网络基础部件-激活函数详解

本文分析了激活函数对于神经网络的必要性&#xff0c;同时讲解了几种常见的激活函数的原理&#xff0c;并给出相关公式、代码和示例图。 一&#xff0c;激活函数概述 1.1&#xff0c;前言 人工神经元(Artificial Neuron)&#xff0c;简称神经元(Neuron)&#xff0c;是构成神经…

[linux] 冯诺依曼体系及操作系统的概念

文章目录1. 冯诺依曼体系结构1. 为什么要有内存&#xff1f;1. 若内存不存在2. 若内存存在结论12.在硬件层面&#xff0c;单机和跨单机之间数据流是如何流向的&#xff1f;结论22. 操作系统(Operator System)1. 概念2.如何理解操作系统对硬件管理&#xff1f;结论13.管理者和被…

Linux安装 MySQL

1、MySQL安装方式 Linux MySQL安装有很多方式&#xff1a;yum安装、apt-get安装、rpm安装、二进制安装、源码编译安装。 比较通用的做法就是&#xff0c;二进制安装、源码编译安装&#xff0c;但是源码编译安装太麻烦&#xff0c;所以一般都是选择二进制安装。本文就是采用二…

百度百科创建词条参考资料问题汇总

百度百科词条编辑规则是相当复杂的&#xff0c;不是单纯写一写百度词条内容那么简单&#xff0c;还需要准备对应的参考资料来佐证你内容的真实性&#xff0c;很多小伙伴就因为这个参考资料犯了难&#xff0c;每次词条审核不通过的原因也大部分是因为参考资料的问题。 参考资料…

爬虫攻守道 - 2023最新 - Python Selenium 实现 - 数据去伪存真,正则表达式谁与争锋 - 爬取某天气网站历史数据

前言 前面写过3篇文章&#xff0c;分别介绍了反爬措施&#xff0c;JS逆向ajax获取数据&#xff0c;以及正则表达式匹配开头、结尾、中间的用法。第3篇算是本文 Python Selenium 爬虫实现方案的子集&#xff0c;大家可以参照阅读。 另外本意是“攻守”&#xff0c;不知道为何输…

【搞懂AUTOSAR网络管理测试】AUTOSAR网络管理规范需求解读

文章目录前言一、名词解释二、NM报文1.NM报文格式2.NM报文数据场内容三、NM状态机1.NM状态转换图2.状态前言 AUTOSAR Automotive Open System Architecture&#xff0c;汽车开放系统架构&#xff0c;由全球汽车制造商、部件供应商及其他电子、半导体和软件系统公司联合建立&am…

【Nginx】Nginx配置实例-负载均衡

1. 首先准备两个同时启动的 Tomcat2. 在 nginx.conf 中进行配置 1. 首先准备两个同时启动的 Tomcat 2. 在 nginx.conf 中进行配置 在 nginx.conf 中进行配置 随着互联网信息的爆炸性增长&#xff0c;负载均衡&#xff08;load balance&#xff09;已经不再是一个很陌生的话题…

中睿天下入选“2023年网络安全服务阳光行动”成员单位

近日&#xff0c;中国网络安全产业联盟&#xff08;CCIA&#xff09;正式公布了“2023年网络安全服务阳光行动”成员单位名单。中睿天下作为以“实战对抗”为特点的能力价值型网络安全厂商&#xff0c;凭借领先的产品方案、专业的服务支持和优秀的行业自律精神成功入选。为规范…

12、Javaweb_登录添加删除修改多选删除分页查询复杂条件查询案例

1. 综合练习 1. 简单功能 1. 列表查询 2. 登录 3. 添加 4. 删除 5. 修改 2. 复杂功能 1. 删除选中 2. 分页查询 * 好处&#xff1a; 1. 减轻服务器内存的开销 …

C/C++数据类型转换详解

目录 C语言数据类型转换 1、自动类型转换 &#xff08;1&#xff09;算术表达式的自动类型转换 &#xff08;2&#xff09;赋值运算中的自动类型转换 2、强制类型转换 C数据类型转换 1、static_cast<> 2、const_cast<> 3、dynamic_cast<> 4、reint…