Maison base de données tutoriel mysql Mysql源码学习――用户认证原理与实现_MySQL

Mysql源码学习――用户认证原理与实现_MySQL

Jun 01, 2016 pm 01:44 PM
用户名 认证

bitsCN.com

 

前几节跟踪了Connection Manager和Thread Manager,在连接的过程中,还有一个身份认证的过程,就是大家所熟悉的

 

验证用户名和密码的过程,我们平时做一个系统的时候,很多时候都会涉及到身份验证。今天我们就来看下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

 

 

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

 

  /* 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);//验证用户名和密码

 

}

 

  

 

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

 

获取客户端的IP和主机名

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

获取用户名和密码

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

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

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

 

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

 

题外话:

 

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

 

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

 

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

 

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

 

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

 

    下篇进入实质性的介绍,通过跟踪一个建表语句,来学习Mysql是如何存储表的元数据的,即frm格式文件的剖析。

<script></script>

 

 

    PS.最近工作比较清闲,却迷失了方向,一会想看OS的实现,一会想看逆向,一会又想看计算机组成原理,哎,转专业的学生伤

 

不起啊,计算机很神奇,我很迷茫…

 

摘自 心中无码

bitsCN.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

Video Face Swap

Video Face Swap

Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Comment résoudre le problème selon lequel Windows 11 vous demande de saisir le nom d'utilisateur et le mot de passe de l'administrateur pour continuer ? Comment résoudre le problème selon lequel Windows 11 vous demande de saisir le nom d'utilisateur et le mot de passe de l'administrateur pour continuer ? Apr 11, 2024 am 09:10 AM

Lorsque vous utilisez le système Win11, vous rencontrerez parfois une invite vous demandant de saisir le nom d'utilisateur et le mot de passe de l'administrateur. Cet article explique comment gérer cette situation. Méthode 1 : 1. Cliquez sur [Logo Windows], puis appuyez sur [Maj+Redémarrer] pour passer en mode sans échec ou entrez en mode sans échec de cette façon : cliquez sur le menu Démarrer et sélectionnez Paramètres ; Sélectionnez « Mise à jour et sécurité » ; sélectionnez « Redémarrer maintenant » dans « Récupération » après avoir redémarré et saisi les options, sélectionnez - Dépannage - Options avancées - Paramètres de démarrage -&mdash.

Comment changer votre nom sur Instagram il y a 14 jours Comment changer votre nom sur Instagram il y a 14 jours Apr 16, 2023 pm 02:40 PM

Au début des médias sociaux, vous pouviez changer votre nom de profil plusieurs fois, mais maintenant, changer de nom sur n'importe quelle application de médias sociaux s'accompagne de son propre ensemble de restrictions. Si vous souhaitez modifier votre nom d'affichage ou votre nom d'utilisateur sur Instagram, la publication ci-dessous vous expliquera à quelle fréquence vous pouvez les modifier, comment le faire et ce que vous pouvez faire lorsque vous ne pouvez pas changer votre nom sur la plateforme. Comment changer le nom d’affichage et le nom d’utilisateur sur Instagram ? Instagram propose deux emplacements pour votre nom : votre nom d'affichage et votre nom d'utilisateur, et heureusement, vous pouvez facilement modifier les deux dans l'application mobile. Le nom d'affichage est celui que vous entreriez normalement vrai

Correction : problèmes avec les paramètres Oobekeyboard Ooberegion Oobelocal oobe sous Windows 11/10 Correction : problèmes avec les paramètres Oobekeyboard Ooberegion Oobelocal oobe sous Windows 11/10 Apr 17, 2023 am 09:01 AM

OOBE ou expérience prête à l'emploi est un processus conçu pour que les utilisateurs les guident à travers les différentes étapes des étapes post-installation. Cela inclut les pages de droits et d’accords, les pages de connexion, les options de connexion WiFi ou réseau, etc. Si vous rencontrez des problèmes OOBEKeyboard, OOBELOCAL ou OOBEREGION, vous ne pourrez pas passer aux étapes d'installation finales. Ne t'inquiète pas. Il existe quelques correctifs simples que vous pouvez utiliser pour résoudre ce problème. Solutions de contournement - Avant de faire quoi que ce soit d'autre, essayez ces solutions normales - 1. Lorsque vous recevez une invite d'erreur, continuez et cliquez sur l'invite « Réessayer ». Continuez à essayer au moins 7 à 8 fois supplémentaires. 2. Vérifiez la connectivité réseau. Si vous utilisez une connexion Ethernet ou Wi-Fi

1.1.1.1 Comment se connecter au système d'authentification en ligne 1.1.1.1 Comment se connecter au système d'authentification en ligne Apr 20, 2023 am 10:44 AM

1.1.1.1 Méthode de connexion au système d'authentification Internet : 1. Recherchez le signal sans fil du réseau du campus et connectez-vous ; 2. Ouvrez le navigateur et sélectionnez « Self-Service » sur l'interface d'authentification contextuelle ; mot de passe initial pour vous connecter ; 4. Complétez les informations personnelles et définissez un mot de passe fort.

Comment obtenir votre identifiant Steam en quelques étapes ? Comment obtenir votre identifiant Steam en quelques étapes ? May 08, 2023 pm 11:43 PM

De nos jours, de nombreux utilisateurs Windows qui aiment les jeux ont accédé au client Steam et peuvent rechercher, télécharger et jouer à n'importe quel bon jeu. Cependant, de nombreux profils d'utilisateurs peuvent porter exactement le même nom, ce qui rend difficile la recherche d'un profil ou même la liaison d'un profil Steam à d'autres comptes tiers ou la participation à des forums Steam pour partager du contenu. Le profil se voit attribuer un identifiant unique à 17 chiffres, qui reste le même et ne peut être modifié à aucun moment par l'utilisateur, contrairement au nom d'utilisateur ou à l'URL personnalisée. Quoi qu'il en soit, certains utilisateurs ne connaissent pas leur Steamid, et il est important de le savoir. Si vous ne savez pas comment retrouver le Steamid de votre compte, pas de panique. Dans cet article

Comment utiliser ThinkPHP6 pour l'authentification JWT ? Comment utiliser ThinkPHP6 pour l'authentification JWT ? Jun 12, 2023 pm 12:18 PM

JWT (JSONWebToken) est un mécanisme léger d'authentification et d'autorisation qui utilise des objets JSON comme jetons de sécurité pour transmettre en toute sécurité les informations d'identité des utilisateurs entre plusieurs systèmes. ThinkPHP6 est un framework MVC efficace et flexible basé sur le langage PHP. Il fournit de nombreux outils et fonctions utiles, notamment le mécanisme d'authentification JWT. Dans cet article, nous présenterons comment utiliser ThinkPHP6 pour l'authentification JWT afin de garantir la sécurité et la fiabilité des applications Web.

Comment remplir le nom d'utilisateur du chemin de fer 12306 Comment remplir le nom d'utilisateur du chemin de fer 12306 Feb 23, 2024 pm 04:07 PM

Comment remplir le nom d'utilisateur de Railway 12306 ? Vous pouvez remplir le nom d'utilisateur dans l'application Railway 12306, mais la plupart des amis ne savent pas comment remplir le nom d'utilisateur de Railway 12306. Vient ensuite le didacticiel graphique sur la façon de remplir dans le nom d'utilisateur du chemin de fer 12306 apporté par l'éditeur , les utilisateurs intéressés viennent y jeter un œil ! Didacticiel d'utilisation du Railway 12306 Comment remplir le nom d'utilisateur du Railway 12306 1. Ouvrez d'abord l'application Railway 12306 et cliquez sur [S'inscrire] en bas de la page principale. 2. Ensuite, sur la page de fonction d'enregistrement, entrez le nom d'utilisateur, le mot de passe et la confirmation ; mot de passe, etc.; 3. Enfin, entrez Une fois terminé, vous pouvez remplir l'enregistrement de l'utilisateur.

Quel est le nom d'utilisateur Wi-Fi ? Quel est le nom d'utilisateur Wi-Fi ? Mar 21, 2023 am 11:32 AM

Le nom d'utilisateur wifi fait référence au nom d'utilisateur administratif du routeur sans fil. Ce nom d'utilisateur, l'adresse IP du routeur et le mot de passe administratif par défaut sont généralement imprimés au bas du routeur sans fil et peuvent également être trouvés dans le manuel du routeur sans fil. ; l'utilisateur administratif par défaut de la plupart des routeurs. Le nom est à la fois admin et le mot de passe de gestion est également admin.

See all articles