背景:

关于MySQL的审核的重要性就不说明了,本文的自动化审核是通过InceptionSQLAdvisor实现的,具体的使用可以看它们各自的说明文档。这里大致介绍下如何部署和使用它们,其实该文章也可以说成是如何在ubuntu下安装Inception和SQLAdivsor。本文的web平台是通过python的tornado来实现的,详细信息可以看下面的介绍说明。

环境:

系统:Ubuntu 14.04.5

安装部署:

1)安装Inception

① 下载inception

  1. git clone https://github.com/mysql-inception/inception.git

② 安装依赖包

  1. cmake
  2. sudo apt-get install cmake
  3.  
  4. libncurses5-dev
  5. sudo apt-get install libncurses5-dev
  6.  
  7. libssl-dev
  8. sudo apt-get install libssl-dev
  9.  
  10. g++
  11. sudo apt-get install g++
  12.  
  13. m4
  14. sudo apt-get install m4
  15.  
  16. bison:版本最好是2.6之前的,最新的可能会有问题
  17. wget http://ftp.gnu.org/gnu/bison/bison-2.5.1.tar.gz
  18. ./configure
  19. make
  20. make install

做个软连接:

  1. ln -s /usr/local/inception-master_2.1.50/sql/Inception /usr/bin/Inception

③ 安装inception

  1. 安装,进入clone的目录执行:
  2. sh inception_build.sh debug
  3. 上面执行完毕之后再执行,可执行文件在debug下面的sql目录中,执行:
  4. mv debug/ /usr/local/inception-master_2.1.50
  5.  
  6. 查看版本:
  7. cd /usr/local/inception-master_2.1.50/sql
  8. ./Inception -V
  9. ./Inception Ver Inception2.1.50 for Linux on x86_64 (Source distribution)

④ 安装percona-toolkit

  1. wget https://www.percona.com/downloads/percona-toolkit/2.2.20/tarball/percona-toolkit-2.2.20.tar.gz
  2.  
  3. tar zxvf percona-toolkit-2.2..tar.gz
  4.  
  5. cd percona-toolkit-2.2./
  6.  
  7. perl Makefile.PL
  8.  
  9. make install

⑤ 测试Inception:

命令行启动:

  1. Inception --port=
  2. -- :: [Note] Welcome to use Inception2.1.50
  3. -- :: [Note] Server hostname (bind-address): '*'; port:
  4. -- :: [Note] IPv6 is available.
  5. -- :: [Note] - '::' resolves to '::';
  6. -- :: [Note] Server socket created on IP: '::'.

参数启动:可以根据需要来调整参数

  1. Inception --defaults-file=/etc/inception/inc.cnf

各个参数的意义

  1. [inception]
  2. general_log=
  3. general_log_file=/var/log/inception.log
  4. port=
  5. socket=/tmp/inc.socket
  6. character-set-client-handshake=
  7. character-set-server=utf8
  8.  
  9. #备份相关
  10. #需要开启binlog
  11. inception_remote_system_password=cVQ9FLoiTbED2R3ycvnJ
  12. inception_remote_system_user=audit_user
  13. inception_remote_backup_port=
  14. inception_remote_backup_host=192.168.200.49
  15.  
  16. #在DML语句中没有WHERE条件时,是不是要报错
  17. inception_check_dml_where=
  18. #在DML语句中使用了LIMIT时,是不是要报错
  19. inception_check_dml_limit=
  20. #在DML语句中使用了Order By时,是不是要报错
  21. inception_check_dml_orderby
  22. #Select*时是不是要报错
  23. inception_enable_select_star=
  24. #order by rand时是不是报错
  25. inception_enable_orderby_rand=
  26. #创建或者新增列时如果列为NULL,是不是报错
  27. inception_enable_nullable=
  28. #是不是支持外键
  29. inception_enable_foreign_key=
  30. #一个索引中,列的最大个数,超过这个数目则报错(-)
  31. inception_max_key_parts=
  32. #在一个修改语句中,预计影响的最大行数,超过这个数就报错(-max)
  33. inception_max_update_rows=
  34. #一个表中,最大的索引数目,超过这个数则报错(-)
  35. inception_max_keys=
  36. #建表指定的存储引擎不为Innodb,不报错
  37. inception_enable_not_innodb=
  38. #表示在建表或者建库时支持的字符集,如果需要多个,则用逗号分隔,影响的范围是建表、设置会话字符集、修改表字符集属性等
  39. inception_support_charset=utf8mb4
  40. #建表时,表没有注释时报错
  41. inception_check_table_comment=
  42. #建表时,列没有注释时报错
  43. inception_check_column_comment=
  44. #建表时,如果没有主键,则报错
  45. inception_check_primary_key=
  46. #是不是支持分区表
  47. inception_enable_partition_table=
  48. #是不是支持enum,set,bit数据类型
  49. inception_enable_enum_set_bit=
  50. #是不是要检查索引名字前缀为"idx_",检查唯一索引前缀是不是"uniq_"
  51. inception_check_index_prefix=
  52. #自增列是不是要为无符号型
  53. inception_enable_autoincrement_unsigned=
  54. #当char类型的长度大于这个值时,就提示将其转换为VARCHAR(-max)
  55. inception_max_char_length=
  56. #当建表时自增列的值指定的不为1,则报错
  57. inception_check_autoincrement_init_value=
  58. #当建表时自增列的类型不为int或者bigint时报错
  59. inception_check_autoincrement_datatype=
  60. #建表时,如果没有为timestamp类型指定默认值,则报错
  61. inception_check_timestamp_default=
  62. #允许列自己设置字符集
  63. inception_enable_column_charset=
  64. #建表时,如果指定的自增列的名字不为ID,则报错,说明是有意义的,给提示
  65. inception_check_autoincrement_name=
  66. #在多个改同一个表的语句出现时,报错,提示合成一个
  67. inception_merge_alter_table=
  68. #检查在建表、修改列、新增列时,新的列属性是不是要有默认值
  69. inception_check_column_default_value=
  70. #检查是不是支持BLOB字段,包括建表、修改列、新增列操作
  71. inception_enable_blob_type=
  72. #检查在SQL语句中,是不是有标识符被写成MySQL的关键字,默认值为报警。
  73. inception_enable_identifer_keyword=
  74. #这个参数的作用是为了匹配Python客户端每次自动设置auto_commit=0的,如果取消则会报错,针对Inception本身没有实际意义
  75. #auto_commit=
  76. #这个参数实际上就是MySQL数据库原来的参数,因为Incpetion没有权限验证过程,那么为了实现更安全的访问,可以给Inception服务器的这个参数设置某台机器(Inception上层的应用程序)不地址,这样
  77. #其它非法程序是不可访问的,那么再加上Inception执行的选项中的用户名密码,对MySQL就更加安全
  78. bind_address=127.0.0.1
  79. #inception_user
  80. #inception_password
  81. #inception_enable_sql_statistic
  82. #inception_read_only
  83. #打开与关闭Inception对SQL语句中各种名字的检查,如果设置为ON,则如果发现名字中存在除数字字母下划线之外的字符时,报Identifier "invalidname" is invalid, valid options: [a-z,A-Z,-,_].
  84. inception_check_identifier=
  85.  
  86. #inception_osc_min_table_size=
  87. #inception_osc_bin_dir=/data/temp
  88. #inception_osc_chunk_time=0.1

Inception需要用MySQL客户端连接,需要安装mysql-client,本文测试用Percona MySQL 5.6。因为后面需要用到MySQL,就直接安装客户端和服务端:

  1. wget https://repo.percona.com/apt/percona-release_0.1-4.$(lsb_release -sc)_all.deb
  2.  
  3. dpkg -i percona-release_0.-.$(lsb_release -sc)_all.deb
  4.  
  5. sudo apt-get update
  6.  
  7. sudo apt-get install percona-server-server-5.6

连接Inception(6669),用inception get variables;测试是否安装成功。

  1. mysql -uroot -h127.0.0. -P6669
  2. Welcome to the MySQL monitor. Commands end with ; or \g.
  3. Your MySQL connection id is
  4. Server version: Inception2.1.50
  5.  
  6. Copyright (c) - Percona LLC and/or its affiliates
  7. Copyright (c) , , Oracle and/or its affiliates. All rights reserved.
  8.  
  9. Oracle is a registered trademark of Oracle Corporation and/or its
  10. affiliates. Other names may be trademarks of their respective
  11. owners.
  12.  
  13. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
  14.  
  15. mysql> inception get variables;
  16. ...

⑥ 接口:python接口

  1. #!/usr/bin/python
  2. #-*- coding: utf-8 -*-
  3.  
  4. import MySQLdb
  5. sql='/*--user=root;--password=123456;--host=127.0.0.1;--port=3306;--execute=1;*/\
  6. inception_magic_start;\
  7. use test;\
  8. CREATE TABLE xxx(id int unsigned not null auto_increment comment "aaaa",username varchar(10) not null default 0 comment "xxx",primary key(id))engine = innodb default charset utf8mb4 comment "dddd";\
  9. inception_magic_commit;'
  10. #insert into adaptive_office(username) values("ASD"),("ZXC"),("EST");
  11. try:
  12. conn=MySQLdb.connect(host='127.0.0.1',user='root',passwd='',db='',port=6669)
  13. cursor=conn.cursor()
  14. cursor.execute(sql)
  15. results = cursor.fetchall()
  16. column_name_max_size=max(len(i[0]) for i in cursor.description)
  17. row_num=0
  18. for result in results:
  19. row_num=row_num+1
  20. print '*'.ljust(27,'*'),row_num,'.row', '*'.ljust(27,'*')
  21. row = map(lambda x, y: (x,y), (i[0] for i in cursor.description), result)
  22. for each_column in row:
  23. if each_column[0] != 'errormessage':
  24. print each_column[0].rjust(column_name_max_size),":",each_column[1]
  25. else:
  26. print each_column[0].rjust(column_name_max_size),':',each_column[1].replace('\n','\n'.ljust(column_name_max_size+4))
  27. cursor.close()
  28. conn.close()
  29. except MySQLdb.Error,e:
  30. print "Mysql Error %d: %s" % (e.args[0], e.args[1])

效果:

  1. python inc.py
  2. *************************** 1 .row ***************************
  3. ID : 1
  4. stage : CHECKED
  5. errlevel : 0
  6. stagestatus : Audit completed
  7. errormessage : None
  8. SQL : use test
  9. Affected_rows : 0
  10. sequence : '0_0_0'
  11. backup_dbname : None
  12. execute_time : 0
  13. sqlsha1 :
  14. *************************** 2 .row ***************************
  15. ID : 2
  16. stage : CHECKED
  17. errlevel : 1
  18. stagestatus : Audit completed
  19. errormessage : Set unsigned attribute on auto increment column in table 'xx'.
  20. SQL : CREATE TABLE xx(id int not null auto_increment comment "aaaa",username varchar(10) not null default 0 comment "xxx",primary key(id))engine = innodb default charset utf8mb4 comment "dddd"
  21. Affected_rows : 0
  22. sequence : '0_0_1'
  23. backup_dbname : 127_0_0_1_3306_test
  24. execute_time : 0
  25. sqlsha1 :

到此,Inception安装已完成。更多的使用说明,可以从文档说明里查找,文档是最好的手册指南。

2)安装SQLAdvisor,架构和原理说明可以看美团点评SQL优化工具SQLAdvisor开源

① 下载

  1. git clone https://github.com/Meituan-Dianping/SQLAdvisor.git

② 安装依赖包

  1. apt-get install cmake
  2. apt-get install libffi-dev
  3. apt-get install libaio-dev
  4. apt-get install glib2.-dev
  5. apt-get install glib2.
  6. apt-get install percona-server-client-5.6
  7. apt-get install percona-server-server-5.6
  8. apt-get install percona-server-common-5.6
  9. apt-get install libmysqlclient-dev
  10. apt-get install build-essential
  11. apt-get install g++
  12. apt-get install bison

③ 相关修改

  1. 1:建立安装目录
  2. mkdir -p /usr/local/sqlparser
  3.  
  4. 2:增加软连接
  5. cd /usr/lib/x86_64-linux-gnu/
  6. ls -lh /usr/lib/x86_64-linux-gnu/libperconaserverclient*
  7. ln -s libperconaserverclient_r.so.18 libperconaserverclient_r.so
  8.  
  9. 3:修改配置
  10. vi sqladvisor/CMakeLists.txt
  11.  
  12. cmake_minimum_required (VERSION 2.6)
  13. project(sqladvisor)
  14.  
  15. # /usr/local/sqlparser为sqlparser库安装目录,必要时进行修改
  16. include_directories("/usr/local/sqlparser/include")
  17. include_directories("/usr/local/sqlparser/include/regex")
  18. #include_directories("/usr/lib64/glib-2.0/include")
  19. include_directories("/usr/lib/x86_64-linux-gnu/glib-2.0/include")
  20. include_directories("/usr/include/glib-2.0")
  21. link_directories("/usr/local/sqlparser/lib")
  22. link_directories("/usr/lib/x86_64-linux-gnu")
  23.  
  24. set(TEST_SRC main.cc)
  25. add_executable(sqladvisor ${TEST_SRC})
  26.  
  27. #如果是sqlparser debug库,则应连接的库为sqlparser-debug
  28. target_link_libraries(sqladvisor sqlparser-debug)
  29. target_link_libraries(sqladvisor perconaserverclient_r)
  30. target_link_libraries(sqladvisor glib-2.0)

④ 编译依赖项sqlparser

  1. :
  2. cmake -DBUILD_CONFIG=mysql_release -DCMAKE_BUILD_TYPE=debug -DCMAKE_INSTALL_PREFIX=/usr/local/sqlparser ./
  3. :
  4. make
  5. :
  6. make install

⑤ 安装SQLAdvisor源码

  1. :
  2. cd sqladvisor/
  3. :
  4. cmake -DCMAKE_BUILD_TYPE=debug ./
  5. :
  6. make

⑥ 测试:

  1. sqladvisor --help
  2. Usage:
  3. sqladvisor [OPTION...] sqladvisor
  4.  
  5. SQL Advisor Summary
  6.  
  7. Help Options:
  8. -?, --help Show help options
  9.  
  10. Application Options:
  11. -f, --defaults-file sqls file
  12. -u, --username username
  13. -p, --password password
  14. -P, --port port
  15. -h, --host host
  16. -d, --dbname database name
  17. -q, --sqls sqls
  18. -v, --verbose :output logs :output nothing
  1. sqladvisor -h 127.0.0.1 -P -u root -p -d test -q "select * from xxx order by username " -v
  2. -- :: [Note] 1步: SQL解析优化之后得到的SQL:select `*` AS `*` from `test`.`xxx` order by `username`
  3.  
  4. -- :: [Note] 2步:开始解析order by 条件
  5.  
  6. -- :: [Note] 3步:开始验证 字段username是不是主键。表名:xxx
  7.  
  8. -- :: [Note] show index from xxx where Key_name = 'PRIMARY' and Column_name ='username' and Seq_in_index =
  9.  
  10. -- :: [Note] 4步:字段username不是主键。表名:xxx
  11.  
  12. -- :: [Note] 5步:开始添加order by 字段
  13.  
  14. -- :: [Note] 6步:开始验证 字段username是不是主键。表名:xxx
  15.  
  16. -- :: [Note] show index from xxx where Key_name = 'PRIMARY' and Column_name ='username' and Seq_in_index =
  17.  
  18. -- :: [Note] 7步:字段username不是主键。表名:xxx
  19.  
  20. -- :: [Note] 8步:开始验证 字段username是不是主键。表名:xxx
  21.  
  22. -- :: [Note] show index from xxx where Key_name = 'PRIMARY' and Column_name ='username' and Seq_in_index =
  23.  
  24. -- :: [Note] 9步:字段username不是主键。表名:xxx
  25.  
  26. -- :: [Note] 10步:开始验证 字段username是不是主键。表名:xxx
  27.  
  28. -- :: [Note] show index from xxx where Key_name = 'PRIMARY' and Column_name ='username' and Seq_in_index =
  29.  
  30. -- :: [Note] 11步:字段username不是主键。表名:xxx
  31.  
  32. -- :: [Note] 12步:开始验证表中是否已存在相关索引。表名:xxx, 字段名:username, 在索引中的位置:
  33.  
  34. -- :: [Note] show index from xxx where Column_name ='username' and Seq_in_index =
  35.  
  36. -- :: [Note] 13步:开始输出表xxx索引优化建议:
  37.  
  38. -- :: [Note] Create_Index_SQLalter table xxx add index idx_username(username)
  39.  
  40. -- :: [Note] 14步: SQLAdvisor结束!

到此,SQLAdvisor安装已完成。更多的使用说明,可以从文档说明里查找,文档是最好的手册指南。

1)和2)即Inception和SQLAdvisor已经解决了大部分场景的审核要求,通过他们各自的说明手册了解其使用方法就可以直接通过web来操作实现了。

3)Tornado实现web

① 安装


  1. apt-get install python-setuptools

  2. easy_install tornado

  3. apt-get install python-mysqldb
    4:
    easy_install futures

② 初始化MySQL(上面已经装好了mysql server)

  1. CREATE DATABASE `sql_audit`
  2. USE `sql_audit`;
  3.  
  4. #SQLAdvisor使用
  5. DROP TABLE IF EXISTS `slave_dbnames`;
  6. CREATE TABLE `slave_dbnames` (
  7. `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  8. `dbname` varchar(30) NOT NULL,
  9. `createTime` datetime NOT NULL,
  10. PRIMARY KEY (`id`),
  11. UNIQUE KEY `uniq_dbname` (`dbname`)
  12. ) ENGINE=InnoDB AUTO_INCREMENT=136 DEFAULT CHARSET=utf8;
  13.  
  14. #下面2张Inception使用
  15. DROP TABLE IF EXISTS `sysuser`;
  16. CREATE TABLE `sysuser` (
  17. `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  18. `username` varchar(30) NOT NULL COMMENT '用户名',
  19. `password` varchar(100) NOT NULL COMMENT '密码',
  20. `email` varchar(100) NOT NULL COMMENT '邮箱',
  21. `createTime` datetime NOT NULL,
  22. PRIMARY KEY (`id`),
  23. UNIQUE KEY `uk_username` (`username`),
  24. UNIQUE KEY `uk_email` (`email`)
  25. ) ENGINE=InnoDB AUTO_INCREMENT=55 DEFAULT CHARSET=utf8 COMMENT='用户信息表';
  26.  
  27. DROP TABLE IF EXISTS `user_dbnames`;
  28. CREATE TABLE `user_dbnames` (
  29. `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  30. `username` varchar(30) NOT NULL COMMENT '用户名',
  31. `dbname` varchar(30) NOT NULL COMMENT '数据库名',
  32. `createTime` datetime NOT NULL COMMENT '创建时间',
  33. PRIMARY KEY (`id`),
  34. UNIQUE KEY `uk_username_dbname` (`username`,`dbname`)
  35. ) ENGINE=InnoDB AUTO_INCREMENT=368 DEFAULT CHARSET=utf8 COMMENT='用户操作数据库对应表';

③ 测试效果

web部分的代码就不说明了,主要就是传参数使用Inception和SQLAdvisor。我使用的模板来自:http://ace.jeka.by/index.html

运行:

  1. python audit_server.py
  2.  
  3. Inception --port=

最终实现的效果如下:

4)Supervisor来实现后台进程启动

具体的可以看进程管理supervisor的简单说明,这里需要注意的是上面的安装都是通过root的,而Inception和SQLAdivsor以及Tornado的使用都不需要用root进行启动,为了安全,安装完所有之后,专门创建启动上面进程都用户,如:

  1. ps -ef| grep incept
  2. #inception用户启动inception
  3. incepti+ Mar14 ? :: /usr/bin/Inception --defaults-file=/etc/inception/inc.cnf
  4.  
  5. ps -ef| grep tornado | grep -v "color=auto"
  6. #tornado用户启动tornado
  7. tornado Mar22 ? :: /usr/bin/python /home/jyzhou/audit_platform/audit_server.py
  8.  
  9. SQLAdvisor 是一个执行文件,直接使用就可以了。

总结:

通过上面大致就完成了一个web审核管理平台,本文主要是介绍ubuntu下如何安装Inception和SQLAdvisor,Centos可以直接看它们的手册说明。

参考文档:

Inception使用规范及说明文档

SQLAdvisor详细说明

美团点评SQL优化工具SQLAdvisor

Tornado介绍说明

MySQL自动化审核平台部署说明的更多相关文章

  1. 基于Inception搭建MySQL SQL审核平台Yearing

    基于Inception搭建MySQL SQL审核平台Yearing Inception 1. Inceptionj简介 2. Inception安装 2.1 下载和编译 2.2 启动配置 Yearni ...

  2. mysql自动化审核工具Yearning

    mysql自动化审核工具Yearning cd /opt/wget https://github-production-release-asset-2e65be.s3.amazonaws.com/10 ...

  3. MySQL SQL审核平台 inception+archer2.0(亲测)

    docker run -d --privileged -v `pwd`/archer_data:/data -p 9306:3306 --name archer --hostname archer - ...

  4. 部署MySQL自动化运维工具inception+archer

    ***************************************************************************部署MySQL自动化运维工具inception+a ...

  5. Yearning和inception搭建MySQL审核平台

    前言 采用开源Yearning和inception开源软件,搭建用于MYSQL审核及线上MYSQL语句更新的审核平台. 功能说明 Yearning: 基于Vue.js与Django的整套mysql-s ...

  6. SQL审核平台Yearning部署

    SQL审核平台Yearning部署  Yearning优势: Yearning SQL 审计平台 基于Vue.js与Django的整套mysql-sql审核平台解决方案.提供基于Inception的S ...

  7. SQL审核平台-Yearning安装部署实践

    相关文档: https://guide.yearning.io/ yearning简介 http://python.yearning.io/install/  yearning安装 Yearning ...

  8. 魅族资深DBA:利用MHA构建MySQL高可用平台

    龙启东 魅族资深DBA 负责MySQL.Redis.MongoDB以及自动化平台建设 .擅长MySQL高可用方案.SQL性能优化.故障诊断等. 本次分享主要包括以下几方面: 如何利用MHA 改造MHA ...

  9. Yearning 介绍(SQL审核平台)

    介绍 Yearning SQL 审计平台 基于Vue.js与Django的整套mysql-sql审核平台解决方案.提供基于Inception的SQL检测及执行. GitHub:https://gith ...

随机推荐

  1. iOS Plist 文件的 增 删 改

    一:Plist 文件的创建 Plist 文件作为我们IOS开发的一种数据存储文件,还是经常会用到的.在看<X-code江湖录>的时候,看到了这一点.自己就写了一下,把写的东西分享出来!先看 ...

  2. ZeroMQ初探

    概述 ZeroMQ(也称为 ØMQ,0MQ 或 zmq)是一个可嵌入的网络通讯库(对 Socket 进行了封装). 它提供了携带跨越多种传输协议(如:进程内,进程间,TCP 和多播)的原子消息的 so ...

  3. 剖析Asp.Net Web API路由系统---WebHost部署方式

    上一篇我们剖析了Asp.Net路由系统,今天我们再来简单剖析一下Asp.Net Web API以WebHost方式部署时,Asp.Net Web API的路由系统内部是怎样实现的.还是以一个简单实例开 ...

  4. 微信公众号平台接口开发:基础支持,获取微信服务器IP地址

    官方说明 目前看不出来这个接口有哪些具体运用,但是既然有这个接口,那我们就试试能不能用 访问接口 修改WeCharBase.cs,新增以下2个方法 public static string Serve ...

  5. DirectX11中Shader的封装

    引言 ​ 这个寒假学DirectX11的时候用的书是<Introduction to 3D Game Programming with DirectX 11>,里面关于Shader的部分全 ...

  6. 4105: [Thu Summer Camp 2015]平方运算

    首先嘛这道题目只要知道一个东西就很容易了:所有循环的最小公约数<=60,成一条链的长度最大为11,那么我们就可以用一个很裸的方法.对于在链上的数,我们修改直接暴力找出并修改.对于在环上的数,我们 ...

  7. html5 新特性

    1.querySelector 返回文档中匹配指定css选择器的一个元素. 注意:uerySelector() 方法仅仅返回匹配指定选择器的第一个元素 如果你需要返回所有的元素,请使用 querySe ...

  8. system, fileExist函数包装

    #include "stdio.h" #include <string> #include<sys/types.h> #include<fcntl.h ...

  9. keepalived配置文件

    1. 查看进程 ps aux | grep keepalived ,其输出为: [root@lvs-m ~]# ps aux| grep keepalived |grep -v greproot 21 ...

  10. WebSite---前台系统图片验证码心得

    背景: 因为移动端APP和Msite手机注册发送短信验证码没有添加图片验证码功能.公司的短信接口被恶意刷取.所以我们就觉得在移动端添加一个图片验证码功能.分享一下大体实现方式思路.PS demo是自己 ...