Mysql學習——使用者認證原理與實現

來源:互聯網
上載者:User

驗證使用者名稱和密碼的過程,我們平時做一個系統的時候,很多時候都會涉及到身分識別驗證。今天我們就來看下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中使用者管理模組的資訊儲存在系統資料表 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> select 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,說明本地的任何使用者均可以進行登入,即使是個不存在的使用者也可以登入成功,但是僅限於登入,沒有其他相關的許可權,無法進行實際操作。
  
 代碼如下 複製代碼
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);}
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. 擷取使用者的許可權列表,驗證使用者的相關屬性是否合法,如串連數是否超過上限,串連是否逾時,操作是否超過限制等資訊,如果不合法,則報錯返回。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.