久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

Mysql用戶認(rèn)證的原理是什么

共計(jì) 6762 個(gè)字符,預(yù)計(jì)需要花費(fèi) 17 分鐘才能閱讀完成。

Mysql 用戶認(rèn)證的原理是什么,相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

一、用戶認(rèn)證原理

  我們?cè)趹?yīng)用程序中實(shí)現(xiàn)驗(yàn)證的方式基本上都是創(chuàng)建一張用戶表,里面至少包含 username 和 password 兩個(gè)字段,

password 基本上都是加密后進(jìn)行存儲(chǔ)的。作為數(shù)據(jù)庫(kù),對(duì)用戶的限制較多,不是像我說的僅僅只有 username 和 password

這么簡(jiǎn)單了。首先粗略的講下訪問控制。

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

的 GRANT,REVOKE,大多數(shù)數(shù)據(jù)庫(kù)都支持自助的訪問控制。強(qiáng)制訪問控制就是 ORACLE 中的 LABEL,只有很少的一些系統(tǒng)支持 MAC。

嚴(yán)格來說,登錄并不屬于訪問控制機(jī)制,而應(yīng)該屬于用戶身份識(shí)別和認(rèn)證。在 Mysql 中,將登錄和 DAC 的相關(guān)接口都實(shí)現(xiàn)在了

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

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

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

  Mysql 中用戶管理模塊的信息存儲(chǔ)在系統(tǒng)表 mysql.User 中,這個(gè)表不僅僅存放了授權(quán)用戶的基本信息,還存放一些權(quán)限

信息。我們首先大概看一下這個(gè)表的結(jié)構(gòu)。

+———————–+———————————–+——+—–+———+——-+

| 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)

  這個(gè)表包含了 39 個(gè)字段,對(duì)于我們登錄來說,應(yīng)該主要是使用前三個(gè)字段,即 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)

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

二、跟蹤

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

會(huì)著色):

static bool login_connection(THD *thd)

{

  NET *net= thd-

  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); // 此處是驗(yàn)證的具體函數(shù)

  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);

}

此函數(shù)主要是功能是調(diào)用函數(shù) check_connection 進(jìn)行用戶認(rèn)證, 由于函數(shù) check_connection 過長(zhǎng),對(duì)其進(jìn)行簡(jiǎn)化,如下所示:

 static int check_connection(THD *thd)

{

  uint connect_errors= 0;

  NET *net= thd-

  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))// 此處驗(yàn)證主機(jī)名或 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);// 驗(yàn)證用戶名和密碼

}

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

獲取客戶端的 IP 和主機(jī)名

acl_check_host 函數(shù)驗(yàn)證 USER 表中是否存在相應(yīng)的 IP 或 HOST,如果不存在直接報(bào)錯(cuò)

獲取用戶名和密碼

check_user 函數(shù)驗(yàn)證用戶名和密碼(不輸入用戶名默認(rèn)為 ODBC),如果系統(tǒng)表中不存在匹配的報(bào)錯(cuò)返回

獲取用戶的權(quán)限列表,驗(yàn)證用戶的相關(guān)屬性是否合法,如連接數(shù)是否超過上限,連接是否超時(shí),操作是否超過限制等信息,如果不合法,則報(bào)錯(cuò)返回。

  由于在一個(gè)認(rèn)證的過程中涉及到的東西比較多,各個(gè)方面吧,我不能一一跟蹤,只能大概了解其中的實(shí)現(xiàn)流程,撿重點(diǎn)進(jìn)行

跟蹤,有興趣的童鞋自己具體跟蹤吧

題外話:

  Mysql 中權(quán)限系統(tǒng)表都是在系統(tǒng)啟動(dòng)時(shí),載入內(nèi)存的(當(dāng)然 User 表也是這樣),一般情況下,不需要進(jìn)行頻繁的授權(quán)和回收

操作,這中情況下,權(quán)限表基本保持不變,將其在系統(tǒng)啟動(dòng)的時(shí)候載入內(nèi)存的好處自然是快速的進(jìn)行權(quán)限判斷,減少磁盤的 I /O,

你懂的 ^_^。有好處自然有壞處,就是在頻繁進(jìn)行授權(quán)和回收相關(guān)操作時(shí),權(quán)限表需要重新載入內(nèi)存,Mysql 為了避免這種情況,

在手冊(cè)中已經(jīng)說的很清楚了,授權(quán)和回收只會(huì)反應(yīng)到磁盤中,內(nèi)存的數(shù)據(jù)字典信息是不會(huì)改變的,如果想立即生效,需要調(diào)用

FLUSH PRIVILEGES 系統(tǒng)函數(shù),這個(gè)系統(tǒng)函數(shù)的工作應(yīng)該就是對(duì)權(quán)限系統(tǒng)表的 RELOAD。

看完上述內(nèi)容,你們掌握 Mysql 用戶認(rèn)證的原理是什么的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注丸趣 TV 行業(yè)資訊頻道,感謝各位的閱讀!

正文完
 
丸趣
版權(quán)聲明:本站原創(chuàng)文章,由 丸趣 2023-08-03發(fā)表,共計(jì)6762字。
轉(zhuǎn)載說明:除特殊說明外本站除技術(shù)相關(guān)以外文章皆由網(wǎng)絡(luò)搜集發(fā)布,轉(zhuǎn)載請(qǐng)注明出處。
評(píng)論(沒有評(píng)論)
主站蜘蛛池模板: 乡城县| 洪湖市| 临城县| 永和县| 巴塘县| 玛曲县| 绥中县| 木兰县| 嵩明县| 蒲江县| 丹凤县| 英吉沙县| 南通市| 澳门| 上栗县| 东乡| 西乡县| 汉寿县| 梅河口市| 涿鹿县| 巢湖市| 四平市| 宿迁市| 武川县| 民县| 邓州市| 乐都县| 安丘市| 永州市| 六安市| 广汉市| 星座| 疏勒县| 石狮市| 梓潼县| 普定县| 南京市| 和平县| 襄城县| 克东县| 榆中县|