我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Mysql用户认证的原理是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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中用户管理模块的信息存储在系统表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,说明本地的任何用户均可以进行登录,即使是个不存在的用户也可以登录成功,但是仅限于登录,没有其他相关的权限,无法进行实际操作。

二、跟踪

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

 

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

#endif

    statistic_increment(aborted_connects,&LOCK_status);

    DBUG_RETURN(1);

  }

 

  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;

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

     

      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

  {

   ...

  }

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

  return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);//验证用户名和密码

}

上面的源码主要做了如下几件事情:

获取客户端的IP和主机名

acl_check_host函数验证USER表中是否存在相应的IP或HOST,如果不存在直接报错

获取用户名和密码

check_user函数验证用户名和密码(不输入用户名默认为ODBC),如果系统表中不存在匹配的报错返回

获取用户的权限列表,验证用户的相关属性是否合法,如连接数是否超过上限,连接是否超时,操作是否超过限制等信息,如果不合法,则报错返回。

    由于在一个认证的过程中涉及到的东西比较多,各个方面吧,我不能一一跟踪,只能大概了解其中的实现流程,捡重点进行

跟踪,有兴趣的童鞋自己具体跟踪吧

题外话:

    Mysql中权限系统表都是在系统启动时,载入内存的(当然User表也是这样),一般情况下,不需要进行频繁的授权和回收

操作,这中情况下,权限表基本保持不变,将其在系统启动的时候载入内存的好处自然是快速的进行权限判断,减少磁盘的I/O,

你懂的^_^。有好处自然有坏处,就是在频繁进行授权和回收相关操作时,权限表需要重新载入内存,Mysql为了避免这种情况,

在手册中已经说的很清楚了,授权和回收只会反应到磁盘中,内存的数据字典信息是不会改变的,如果想立即生效,需要调用

FLUSH PRIVILEGES系统函数,这个系统函数的工作应该就是对权限系统表的RELOAD。

看完上述内容,你们掌握Mysql用户认证的原理是什么的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Mysql用户认证的原理是什么

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

服务器ssl认证的原理是什么

服务器 SSL 认证的原理是通过使用公钥和私钥来加密和解密数据。当用户连接到服务器时,服务器会向客户端发送一个公钥,客户端使用该公钥对数据进行加密,并将加密后的数据发送回服务器。服务器使用其私钥对数据进行解密,然后将响应发送回客户端。这个过
2023-06-11

Laravel unit test : 模拟认证的用户是什么

小编给大家分享一下Laravel unit test : 模拟认证的用户是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Laravel 是什么Laravel
2023-06-14

云服务器新用户认证试用的方法是什么

云服务器新用户可通过认证试用来了解和体验云服务器的性能和功能。注册云平台账号后,即可申请试用。云平台审核通过后,将创建试用云服务器。用户可连接到试用服务器并自由体验各种功能,包括资源配置、操作系统、应用部署、网络管理和监控等。试用期间产生数据将在试用结束后删除。认证试用仅限一人使用,且时长和规则可能因云平台而异。通过试用,新用户可评估云服务器的适用性并做出购买决策。
云服务器新用户认证试用的方法是什么
2024-04-10

云服务器新用户认证试用的方法是什么

云服务器新用户认证试用的方法通常包括以下步骤:1. 注册账号:首先,用户需要在云服务器提供商的官方网站上注册一个新的账号。2. 选择试用服务:在注册完成后,用户可以选择使用云服务器提供商提供的试用服务。这通常包括不同配置和运行时间的试用套餐
2023-09-27

Git认证失败的原因是什么及怎么解决

这篇文章主要介绍“Git认证失败的原因是什么及怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Git认证失败的原因是什么及怎么解决”文章能帮助大家解决问题。一、Git认证失败的原因1.凭证缓存
2023-07-05

mysql的mha高可用原理是什么

MySQL的MHA(Master High Availability)是一种用于MySQL数据库的高可用解决方案,主要用于管理主从复制架构,实现自动故障切换和故障恢复。MHA的高可用原理如下:主节点监控:MHA的管理节点会不断监控主节点的
2023-10-26

ssl认证的流程是什么

ssl认证的流程是:1、按照要求把证书配置到网站服务器中;2、客户端会直接向服务器发送一个接入请求;3、服务器接受到请求后,会将证书发送给客户端;4、ssl证书收到后,客户端会开始对ssl认证;5、认证通过后,客户端会产生一个密钥,然后把密
2023-02-09

网站ssl证书的原理是什么

SSL证书的原理是通过使用非对称加密算法和数字签名来确保网站与浏览器之间的安全通信。具体原理如下:1. 首先,网站服务器需要生成一个密钥对,包括一个私钥和一个公钥。私钥保密保存在服务器上,而公钥则可以向任何人公开。2. 当用户通过浏览器访问
2023-08-24

Linq中用户定义函数的原理是什么

Linq中用户定义函数的原理是什么,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Linq用户定义函数我们可以在LINQ to SQL中使用Linq用户定义函数。只要把Lin
2023-06-17

编程热搜

目录