一个简单的MariaDB认证插件demo

代码地址如下:
http://www.demodashi.com/demo/13076.html

一、前言

众所周知(其实可能很多人不知道)MariaDB支持插件认证。在MariaDB中新建用户,常见的语句是:

CREATE USER ‘username‘@‘host‘ IDENTIFIED BY ‘password‘;

这样创建的用户,登录时的认证方式是密码。其实创建用户的语句还可以是:

CREATE USER ‘username‘@‘host‘ IDENTIFIED VIA ‘pluginname‘ USING ‘authstring‘;

这样创建的用户,登录时的认证方式由插件决定。

本文展示了编写一个简单的MariaDB认证插件的全过程。实现的认证机制是用户输入正确的姓名学号即可登录。显然这一认证机制毫无安全性可言,本文重点在于展示插件编写过程。

本文内容基于MariaDB-10.1.8,操作系统是Ubuntu12.04。假设已经安装好了数据库。

二、基本原理

一个认证插件分为两部分,服务器侧和客户端侧,两者配合,才能完成整个认证过程。最常见的认证情景是服务器侧提问,客户端侧回答。

MariaDB提供了一个通用的客户端侧“dialog”,该客户端侧的功能是接收服务器侧的问题,将问题显示在终端上,并在终端上读取待登录用户的回答,之后将回答发送给服务器侧。它支持不限个数的问答,还支持普通问题和密码问题两种问题,普通问题在待登录用户输入回答时是有回显的,密码问题在待登录用户输入回答时是没有回显的。由于最后一个问题需要特殊处理,所以实际上有四种类型的问题。问题字符串的第一个字节是问题类型,宏定义如下:

/* mysql/auth_dialog_client.h */
#define ORDINARY_QUESTION       "\2"
#define LAST_QUESTION           "\3"
#define PASSWORD_QUESTION       "\4"
#define LAST_PASSWORD           "\5"

由于我们想要编写一个简单的认证插件,所以简单起见,客户端侧就使用“dialog”,完全满足要求。这样,我们便只用编写服务器侧部分。

服务器侧部分要做的事情便是与客户端侧的“dialog”通讯,读取输入的姓名学号进行验证。具体实现见下节。

三、编写代码

1、套路部分

认证插件的套路如下:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <mysql/plugin_auth.h>
#include <mysql/auth_dialog_client.h>

static int school_number_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
{
    /* 该函数是实际上进行认证的地方,
    认证通过返回CR_OK,
    认证失败返回CR_ERROR; */
}

static struct st_mysql_auth my_auth_plugin=
{
    MYSQL_AUTHENTICATION_INTERFACE_VERSION, // 插件的接口版本号
    "dialog", // 客户端侧处理函数,我们直接使用了“dialog”,也可以自定义
    school_number_auth // 服务器侧处理函数
};

mysql_declare_plugin(dialog)
{
    MYSQL_AUTHENTICATION_PLUGIN, // 插件类型
    &my_auth_plugin, // 插件结构体指针
    "school_number", // 插件名
    "Werner", // 作者
    "A simple MariaDB auth plugin", // 描述
    PLUGIN_LICENSE_GPL, // 许可证书
    NULL,
    NULL,
    0x0100,
    NULL,
    NULL,
    NULL,
    0,
}
mysql_declare_plugin_end;

mysql_declare_plugin声明了一个插件,其中写明了插件名、插件类型、作者、描述和许可证书等信息,

最重要的是插件结构体指针“&my_auth_plugin”。

插件结构体指针“&my_auth_plugin”指向插件结构体“my_auth_plugin”,该结构体中写明了客户端侧处理函数和服务器侧处理函数。在我们编写的插件中,客户端侧处理函数直接写字符串"dialog",表示使用MariaDB提供的通用客户端侧“dialog”,服务器侧处理函数school_number_auth是实际上进行认证的地方,认证通过返回CR_OK,认证失败返回CR_ERROR。CR_OK和CR_ERROR宏定义如下:

/* mysql/plugin_auth_common.h */
#define CR_ERROR 0
#define CR_OK -1

我们只需要完善函数school_number_auth即可。

2、认证部分

在这一小节中,我们将完善函数school_number_auth。

首先看该函数的两个参数“MYSQL_PLUGIN_VIO *vio”和“MYSQL_SERVER_AUTH_INFO *info”。

“MYSQL_PLUGIN_VIO”中的“VIO”的含义是虚拟输入输出,它的定义如下所示:

/* mysql/plugin_auth.h.pp */
typedef struct st_plugin_vio
{
    int (*read_packet)(struct st_plugin_vio *vio,
    unsigned char **buf);
    int (*write_packet)(struct st_plugin_vio *vio,
    const unsigned char *packet,
    int packet_len);
    void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info);
} MYSQL_PLUGIN_VIO;

可以看到它是一个结构体,成员都是函数指针。

顾名思义,函数*read_packet是虚拟的读,从vio中读取以“\0”结尾的字符串,返回读取到的字符串长度。这个读操作是阻塞读。

*write_packet是虚拟的写,向vio中写入一个字符串,需要指定写入长度。同样,写操作是阻塞写。

“MYSQL_SERVER_AUTH_INFO”的定义如下:

/* mysql/plugin_auth.h.pp */
typedef struct st_mysql_server_auth_info
{
    char *user_name; // 客户端发送的用户名
    unsigned int user_name_length; // 客户端发送的用户名长度
    const char *auth_string; // 在mysql.user表中记录的相应账户的authentication_string
    unsigned long auth_string_length; // authentication_string长度
    char authenticated_as[512 +1]; // 代理用户名,传入时为user_name,可设置
    char external_user[512 +1]; // 系统变量external_user显示的值,待设置
    int password_used; // 是否使用密码,待设置
    const char *host_or_ip; // 主机或IP
    unsigned int host_or_ip_length; // 主机或IP的长度
} MYSQL_SERVER_AUTH_INFO;

由上述定义可知在“MYSQL_SERVER_AUTH_INFO”中可以取到“user_name”和“auth_string”这样的关键字符串。

“password_used”的含义是“是否使用密码”,当认证出错时,报错信息的后面有“Password used: Yes/No”,显示“Yes”还是“No”就由“password_used”决定。默认为“No”,若想保存信息中显示“Yes”,可在school_number_auth函数中设置“password_used”,代码片段如下:

info->password_used= PASSWORD_USED_YES;

明白传入参数的含义后很容易就可以写出school_number_auth函数,其内容如下:

static int school_number_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
{
    int pkt_len;
    unsigned char *pkt;

    if (vio->write_packet(vio, (const unsigned char *) ORDINARY_QUESTION "Please enter your name: ", 26))
        return CR_ERROR;

    if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
        return CR_ERROR;

    if (strcmp((const char *) pkt, info->user_name))
        return CR_ERROR;

    if (vio->write_packet(vio, (const unsigned char *) LAST_QUESTION "Please enter your school number: ", 35))
        return CR_ERROR;

    if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
        return CR_ERROR;

    if (strcmp((const char *) pkt, info->auth_string))
        return CR_ERROR;

    return CR_OK;
}

至此,我们就完成了认证插件的代码编写,将其保存到文件my_auth_plugin.c中,然后进入到下一节。

四、编译安装

1、编译

插件的代码写好后按如下命令编译:

gcc $(mysql_config --cflags) -shared -fPIC -DMYSQL_DYNAMIC_PLUGIN -o my_auth_plugin.so my_auth_plugin.c

参数“-DMYSQL_DYNAMIC_PLUGIN”是必不可少的,否则编译的时候不会报错,但在MariaDB中执行“INSTALL PLUGIN”时会报如下错误:

ERROR 1127 (HY000): Can‘t find symbol ‘_mysql_plugin_interface_version_‘ in library

另外一种常见的错误是找不到头文件:

#include <mysql/plugin_auth.h>
#include <mysql/auth_dialog_client.h>

解决方法是安装相关开发包引入需要的头文件,命令是:

sudo rpm -ivh MariaDB-devel-5.2.9-102.el5.x86_64.rpm

sudo apt-get install libmariadbclient-dev

其实不执行上述命令,将MariaDB安装路径下的inculde目录加入到gcc的头文件搜索路径中也可以解决头文件缺失问题。

编译成功后得到my_auth_plugin.so。

2、复制

编译得到.so文件后需要将.so文件复制到MariaDB的插件目录中。进入MariaDB,用如下语句查询插件目录:

MariaDB [(none)]> SHOW VARIABLES LIKE ‘plugin_dir‘;
+---------------+------------------------------+
| Variable_name | Value                        |
+---------------+------------------------------+
| plugin_dir    | /usr/local/mysql/lib/plugin/ |
+---------------+------------------------------+
1 row in set (0.00 sec)

将my_auth_plugin.so复制到MariaDB的插件目录中:

sudo cp my_auth_plugin.so /usr/local/mysql/lib/plugin/

复制完成后最好修改my_auth_plugin.so的所有者为运行MariaDB的用户,该用户名一般是mysql,命令如下:

sudo chown mysql /usr/local/mysql/lib/plugin/my_auth_plugin.so

3、安装

只是将.so文件复制到MariaDB的插件目录中还不够,还需要在MariaDB中安装插件,语句如下:

MariaDB [(none)]> INSTALL PLUGIN school_number SONAME ‘my_auth_plugin.so‘;
Query OK, 0 rows affected (0.00 sec)

“school_number”是插件名,定义在mysql_declare_plugin中,my_auth_plugin.so是.so文件名,不要混淆。

有安装就有卸载,如何卸载呢?语句如下:

MariaDB [(none)]> UNINSTALL PLUGIN school_number;
Query OK, 0 rows affected (0.00 sec)

先不要执行卸载语句,或是卸载后重新安装,后面还要用到这个插件。

五、使用插件

先创建一个使用该插件认证登录的用户,语句如下:

MariaDB [(none)]> CREATE USER ‘werner‘@‘localhost‘ IDENTIFIED VIA ‘school_number‘ USING ‘M201434212‘;
Query OK, 0 rows affected (0.00 sec)

退出MariaDB后以werner用户登录,可以看到确实使用了插件认证方式,具体过程如下图所示。

六、项目文件目录截图

一个简单的MariaDB认证插件demo

代码地址如下:
http://www.demodashi.com/demo/13076.html

注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

原文地址:https://www.cnblogs.com/demodashi/p/9437770.html

时间: 2024-08-06 16:04:45

一个简单的MariaDB认证插件demo的相关文章

一个简单的页面弹窗插件 jquery.pageMsgFrame.js

页面弹窗是网站中常用的交互效果,它可以强提示网站的某些信息给用户,或者作用于某些信息的修改等等功能. 这几天在做一个项目的时候,就顺捎把这个插件写一下,栽棵树,自己乘凉吧. 原创博文,转载请注明出处:http://www.cnblogs.com/dereksunok/p/3724764.html html代码: 1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5

如何使用AEditor制作一个简单的H5交互页demo

转载自:http://www.alloyteam.com/2015/06/h5-jiao-hu-ye-bian-ji-qi-aeditor-jie-shao/ 本教程演示如何使用AEditor制作一个简单的H5交互页demo: 交互页demo地址: 点击打开H5交互页demo AEditor访问地址: http://aeditor.alloyteam.com Step1:设置页面背景颜色 首先我们设置页面的背景颜色,右击舞台点击“设置背景”: 然后在背景颜色中填上色值rgb(38, 61, 10

Spring Aop 实现一个简单的memcached小插件

memcached我就不多做介绍了,也就是一个分布式缓存系统!是一个典型的NOSQL. 下面我将用spring aop来实现一个简单的插件,来实现annotation方式,简单方便的取得缓存 一.首先我们得定义一个annotation. <strong>package org.xiezhaodong.spring.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType

一个简单的 IDA f5插件问题分析

有人提出问题,以下汇编f5结果缺失代码: .text:00000C18 Java_com_a_b_c .text:00000C18 PUSH {R3,LR} .text:00000C1A CMP R2, #2 .text:00000C1C BEQ loc_C38 .text:00000C1E CMP R2, #3 .text:00000C20 BEQ loc_C2C .text:00000C22 CMP R2, #1 .text:00000C24 BNE loc_C32 .text:00000C

自己写的一个简单的jQuery提示插件

代码: /** * 2014年11月13日 * 提示插件 */ (function ($) { $.fn.tips = function (text) { var divtipsstyle = "position: absolute; left: 0; top: 0; background-color: #dceaf2; padding: 3px; border: solid 1px #6dbde4; visibility: hidden; line-height:20px; "; $

一个简单的socket通信小demo

写了一个socket的程序,可以和本地的服务器进行通信,要先和服务器建立链接,然后发送登录信息,验证成功,就可以和服务器通信了 1 页面截图 2 点击链接服务器,可以链接服务器,服务器的ip地址为:127.0.0.1 端口为 12345: 3 点击链接服务器之后,打印信息如下 4 点击登录,会向服务器发送登录信息: iam:zhangsan, 然后会客户端会显示登录成功 5 比如输入chongqingyoudian  6 在服务器控制台就能看到客户端发送的信息 客户端代码如下 1 // 2 //

[shiro学习笔记]第二节 shiro与web融合实现一个简单的授权认证

本文地址:http://blog.csdn.net/sushengmiyan/article/details/39933993 shiro官网:http://shiro.apache.org/ shiro中文手册:http://wenku.baidu.com/link?url=ZnnwOHFP20LTyX5ILKpd_P94hICe9Ga154KLj_3cCDXpJWhw5Evxt7sfr0B5QSZYXOKqG_FtHeD-RwQvI5ozyTBrMAalhH8nfxNzyoOW21K 本文作

一个简单的倒计时js插件

接收的参数end是必须传的,格式是/分隔的日期字符串,start是可选的,不传就是从现在开始倒计时,callback也是可选的,到倒计时接收时执行自定义的函数. countdown({ 'end':'2015/9/1 17:12:00', 'callback':function () { document.getElementById('countdown').innerHTML = 'the end!'; } }); (function (){ function countdown(confi

32.自己写的一个简单的加载插件

js /* loader.js version:1.0 author:lgw */ (function($) { "use strict"; $.fn.loader = function(options) { return this.each(function(e) { var that = $(this) $.fn.loader.defaultOptions = { isLoading:false, type:"ball-beat", callBack: func