<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        Mysql學習用戶認證原理與實現

        來源:懂視網 責編:小采 時間:2020-11-09 09:55:44
        文檔

        Mysql學習用戶認證原理與實現

        Mysql學習用戶認證原理與實現:這是有事沒事時做的關于我們來理解在mysql核心代碼中的用戶驗證原理,有興趣的同學可以參考一下。 驗證用戶名和密碼的過程,我們平時做一個系統的時候,很多時候都會涉及到身份驗證。今天我們就來看下Mysql是如何進 行驗證的。(注意是登錄,不是登陸^_^
        推薦度:
        導讀Mysql學習用戶認證原理與實現:這是有事沒事時做的關于我們來理解在mysql核心代碼中的用戶驗證原理,有興趣的同學可以參考一下。 驗證用戶名和密碼的過程,我們平時做一個系統的時候,很多時候都會涉及到身份驗證。今天我們就來看下Mysql是如何進 行驗證的。(注意是登錄,不是登陸^_^

        這是有事沒事時做的關于我們來理解在mysql核心代碼中的用戶驗證原理,有興趣的同學可以參考一下。

        驗證用戶名和密碼的過程,我們平時做一個系統的時候,很多時候都會涉及到身份驗證。今天我們就來看下Mysql是如何進

        行驗證的。(注意是登錄,不是登陸^_^)

        一、用戶認證原理

        我們在應用程序中實現驗證的方式基本上都是創建一張用戶表,里面至少包含username和password兩個字段,

        password基本上都是加密后進行存儲的。作為,對用戶的限制較多,不是像我說的僅僅只有username和password

        這么簡單了。首先粗略的講下訪問控制。

        信息系統中,訪問控制分為自主訪問控制(DAC)和強制訪問控制(MAC)。具體到DBMS,自主訪問控制就是我們所熟悉

        的GRANT,REVOKE,大多數數據庫都支持自助的訪問控制。強制訪問控制就是ORACLE中的LABEL,只有很少的一些系統支持MAC。

        嚴格來說,登錄并不屬于訪問控制機制,而應該屬于用戶身份識別和認證。在Mysql中,將登錄和DAC的相關接口都實現在了

        sql_acl.cc中(其實說登錄是用戶擁有的一種權限也未嘗不可,正如ORACLE中的CREATE SESSION,不過登錄并不僅僅是一種權

        限,還包含很多其他的屬性),從文件名大家可以看出來,ACL即ACCESS CONTROL LIST,訪問控制列表,這是實現訪問控制的

        基本方法。下圖是Mysql的整個訪問控制的流程。

        Mysql中用戶管理模塊的信息存儲在系統表.User中,這個表不僅僅存放了授權用戶的基本信息,還存放一些權限

        信息。我們首先大概看一下這個表的結構。

        代碼如下
        +-----------------------+-----------------------------------+------+-----+---------+-------+
        
        | Field | Type | Null | Key | Default | Extra |
        
        +-----------------------+-----------------------------------+------+-----+---------+-------+
        
        | Host | char(60) | NO | PRI | | |
        
        | User | char(16) | NO | PRI | | |
        
        | Password | char(41) | NO | | | |
        
        | Select_priv | enum('N','Y') | NO | | N | |
        
        | Insert_priv | enum('N','Y') | NO | | N | |
        
        | Update_priv | enum('N','Y') | NO | | N | |
        
        | Delete_priv | enum('N','Y') | NO | | N | |
        
        | Create_priv | enum('N','Y') | NO | | N | |
        
        | Drop_priv | enum('N','Y') | NO | | N | |
        
        | Reload_priv | enum('N','Y') | NO | | N | |
        
        | Shutdown_priv | enum('N','Y') | NO | | N | |
        
        | Process_priv | enum('N','Y') | NO | | N | |
        
        | File_priv | enum('N','Y') | NO | | N | |
        
        | Grant_priv | enum('N','Y') | NO | | N | |
        
        | References_priv | enum('N','Y') | NO | | N | |
        
        | Index_priv | enum('N','Y') | NO | | N | |
        
        | Alter_priv | enum('N','Y') | NO | | N | |
        
        | Show_db_priv | enum('N','Y') | NO | | N | |
        
        | Super_priv | enum('N','Y') | NO | | N | |
        
        | Create_tmp_table_priv | enum('N','Y') | NO | | N | |
        
        | Lock_tables_priv | enum('N','Y') | NO | | N | |
        
        | Execute_priv | enum('N','Y') | NO | | N | |
        
        | Repl_slave_priv | enum('N','Y') | NO | | N | |
        
        | Repl_client_priv | enum('N','Y') | NO | | N | |
        
        | Create_view_priv | enum('N','Y') | NO | | N | |
        
        | Show_view_priv | enum('N','Y') | NO | | N | |
        
        | Create_routine_priv | enum('N','Y') | NO | | N | |
        
        | Alter_routine_priv | enum('N','Y') | NO | | N | |
        
        | Create_user_priv | enum('N','Y') | NO | | N | |
        
        | Event_priv | enum('N','Y') | NO | | N | |
        
        | Trigger_priv | enum('N','Y') | NO | | N | |
        
        | ssl_type | enum('','ANY','X509','SPECIFIED') | NO | | | |
        
        | ssl_cipher | blob | NO | | NULL | |
        
        | x509_issuer | blob | NO | | NULL | |
        
        | x509_subject | blob | NO | | NULL | |
        
        | max_questions | int(11) unsigned | NO | | 0 | |
        
        | max_updates | int(11) unsigned | NO | | 0 | |
        
        | max_connections | int(11) unsigned | NO | | 0 | |
        
        | max_user_connections | int(11) unsigned | NO | | 0 | |
        
        +-----------------------+-----------------------------------+------+-----+---------+-------+
        
        39 rows in set (0.01 sec)

        這個表包含了39個字段,對于我們登錄來說,應該主要是使用前三個字段,即Host,User,Password。

        代碼如下
        mysql> Host,User,Password from user;
        
        +-----------+------+----------+
        
        | Host | User | Password |
        
        +-----------+------+----------+
        
        | localhost | root | |
        
        | 127.0.0.1 | root | |
        
        | localhost | | |
        
        +-----------+------+----------+
        
        3 rows in set (0.00 sec)

        這里比我們預想的只需要用戶名和密碼的方式有所出入,多了一個Host字段,這個字段起到什么作用呢?!原來Mysql的登錄認證不僅需要驗證用戶名和密碼,還需要驗證連接的主機地址,這樣也是為了提高安全性吧。那如果我想一個用戶在任何地址都可以進行登錄豈不是要設置很多地址?Mysql提供了通配符,可以設置Host字段為*,這就代表可以匹配任何Host。具體看下這三行的意思,這三行的密碼均為空。針對root用戶,不需要輸入密碼,客戶端的地址為本機。第三行的用戶名為空,Host為localhost,說明本地的任何用戶均可以進行登錄,即使是個不存在的用戶也可以登錄成功,但是僅限于登錄,沒有其他相關的權限,無法進行實際操作。

        二、跟蹤

        在Connection Manager中提到了login_connection函數用于檢查用戶名和密碼等相關信息,其源碼如下(重點的函數代碼

        會著色):

        代碼如下
        static bool login_connection(THD *thd)
        {
         NET *net= &thd->net;
         int error;
         DBUG_ENTER("login_connection");
         DBUG_PRINT("info", ("login_connection called by thread %lu",
         thd->thread_id));
        
         /* Use "connect_timeout" value during connection phase */
         my_net_set_read_timeout(net, connect_timeout);
         my_net_set_write_timeout(net, connect_timeout);
        error= check_connection(thd); //此處是驗證的具體函數
        
         net_end_statement(thd);
        
         if (error)
         { // Wrong permissions
        #ifdef __NT__
         if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
         my_sleep(1000); /* must wait after eof() */
        #endif
         statistic_increment(aborted_connects,&LOCK_status);
         DBUG_RETURN(1);
         }
         /* Connect completed, set read/write timeouts back to default */
         my_net_set_read_timeout(net, thd->variables.net_read_timeout);
         my_net_set_write_timeout(net, thd->variables.net_write_timeout);
         DBUG_RETURN(0);
        }

        此函數主要是功能是調用函數check_connection進行用戶認證,由于函數check_connection過長,對其進行簡化,如下所示:

        static int check_connection(THD *thd)
        
        {
         uint connect_errors= 0;
         NET *net= &thd->net;
         ulong pkt_len= 0;
         char *end;
        
         DBUG_PRINT("info",
         ("New connection received on %s", vio_description(net->vio)));
        #ifdef SIGNAL_WITH_VIO_CLOSE
         thd->set_active_vio(net->vio);
        #endif
        
         if (!thd->main_security_ctx.host) // If TCP/IP connection
         {
         char ip[30];
        
         if (vio_peer_addr(net->vio, ip, &thd->peer_port))
         {
         my_error(ER_BAD_HOST_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
         return 1;
         }
         if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME))))
         return 1; /* The error is set by my_strdup(). */
         thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
         vio_in_addr(net->vio,&thd->remote.sin_addr);
         if (!(specialflag & SPECIAL_NO_RESOLVE))
         {
         vio_in_addr(net->vio,&thd->remote.sin_addr);
         thd->main_security_ctx.host=
         ip_to_hostname(&thd->remote.sin_addr, &connect_errors);
         /* Cut very long hostnames to avoid possible overflows */
         if (thd->main_security_ctx.host)
         {
         if (thd->main_security_ctx.host != my_localhost)
         thd->main_security_ctx.host[min(strlen(thd->main_security_ctx.host),
         HOSTNAME_LENGTH)]= 0;
         thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
         }
         if (connect_errors > max_connect_errors)
         {
         my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip);
         return 1;
         }
         }
         ...
        
        if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))//此處驗證主機名或IP是否存在
        
         {
         my_error(ER_HOST_NOT_PRIVILEGED, MYF(0),
         thd->main_security_ctx.host_or_ip);
         return 1;
         }
         }
         else /* Hostname given means that the connection was on a socket */
         {
         ...
         }
         vio_keepalive(net->vio, TRUE);
        
         ...
        
         char *user= end;
         char *passwd= strend(user)+1;
         uint user_len= passwd - user - 1;
         char *db= passwd;
         char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
         char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
         uint dummy_errors;
        
         uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
         (uchar)(*passwd++) : strlen(passwd);
         db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
         db + passwd_len + 1 : 0;
         uint db_len= db ? strlen(db) : 0;
        
         if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
         {
         inc_host_errors(&thd->remote.sin_addr);
         my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
         return 1;
         }
        
        ...
         /* If username starts and ends in "'", chop them off */
         if (user_len > 1 && user[0] == ''' && user[user_len - 1] == ''')
         {
         user[user_len-1]= 0;
         user++;
         user_len-= 2;
         }
        
         if (thd->main_security_ctx.user)
         x_free(thd->main_security_ctx.user);
         if (!(thd->main_security_ctx.user= my_strdup(user, MYF(MY_WME))))
         return 1; /* The error is set by my_strdup(). */
         return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);//驗證用戶名和密碼
        
        }

        上面的源碼主要做了如下幾件事情:

        1. 獲取客戶端的IP和主機名
        2. acl_check_host函數驗證USER表中是否存在相應的IP或HOST,如果不存在直接報錯
        3. 獲取用戶名和密碼
        4. check_user函數驗證用戶名和密碼(不輸入用戶名默認為ODBC),如果系統表中不存在匹配的報錯返回
        5. 獲取用戶的權限列表,驗證用戶的相關屬性是否合法,如連接數是否超過上限,連接是否超時,操作是否超過限制等信息,如果不合法,則報錯返回。

        由于在一個認證的過程中涉及到的東西比較多,各個方面吧,我不能一一跟蹤,只能大概了解其中的實現流程,撿重點進行

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        Mysql學習用戶認證原理與實現

        Mysql學習用戶認證原理與實現:這是有事沒事時做的關于我們來理解在mysql核心代碼中的用戶驗證原理,有興趣的同學可以參考一下。 驗證用戶名和密碼的過程,我們平時做一個系統的時候,很多時候都會涉及到身份驗證。今天我們就來看下Mysql是如何進 行驗證的。(注意是登錄,不是登陸^_^
        推薦度:
        標簽: 用戶 驗證 過程
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        Top
        主站蜘蛛池模板: 国产成人高清精品免费观看| 亚洲aⅴ无码专区在线观看春色 | 永久免费毛片手机版在线看| 久久久久久久亚洲Av无码 | 亚洲www77777| 成人免费视频一区二区三区| 亚洲视频一区二区三区四区| 亚洲乱码中文字幕综合| 中国内地毛片免费高清| 久久精品国产亚洲综合色| 亚洲综合一区国产精品| 色吊丝永久在线观看最新免费| 亚洲精品无码av中文字幕| 国产91在线免费| 久久国产精品免费| 亚洲电影一区二区三区| 91成年人免费视频| 亚洲国产成人AV在线播放| 亚洲精品高清在线| 日本卡1卡2卡三卡免费| 亚洲人成免费电影| 四虎影院永久免费观看| 不卡视频免费在线观看| 久久亚洲日韩看片无码| 午夜小视频免费观看| 国产va免费精品| 亚洲精品伊人久久久久 | 久久亚洲精品成人| 99久久这里只精品国产免费| 无码一区二区三区亚洲人妻| 亚洲一区AV无码少妇电影☆| 亚洲国产精品免费在线观看| 亚洲人成色777777精品| 亚洲色欲色欲www在线丝| 国产高清免费视频| 四虎影视久久久免费观看| 亚洲v高清理论电影| 国产精品免费电影| 四虎成人精品永久免费AV| 亚洲高清一区二区三区电影| 国产亚洲综合久久系列|