月度归档:2015年03月

MySQL客户端工具

命令行
MySQL提供了一系列管理工具,比如mysql mysqladmin等

GUI
1) MySQL Workbench
地址:http://dev.mysql.com/downloads/workbench/
workbench
官方出品。社区版免费。对于GUI工具,如果你不想付费,Workbench社区版是我们不二的选择。

2) Navicat(付费)
地址:http://www.navicat.com
navicat
Navicat不仅仅支持MySQL,针对MySQL提供了Navicat for MySQL,界面非常友好,操作简便。
对于GUI工具,Navicat for MySQL是付费工具中不二的选择。

这个工具管理数据库系统不是它的强项,它强大的地方在于操作操作数据。我最初使用它是因为它非常便利的数据导入导出功能。比如我可以在查询分析器中运行一个查询,然后把这个查询的结果导出(可以是Excel表等多种格式),然后把这个结果导入到另个库。

3)SQLyog(付费)
地址:http://www.webyog.com/en/index.php
相比Navicat,视乎要轻量一些。

PHP WEB
1)phpMyAdmin
地址:http://www.phpmyadmin.net
phpmyadmin
phpMyAdmin是开源软件。如果你不希望开放数据库远程链接,希望使用一个WEB工具管理MySQL,那么phpMyAdmin就是我们不二的选择。

2)MySQLDumper
地址:http://www.mysqldumper.de/en
看其名字,就知道它是一个专用工具。

MySQL提供的命令行工具,对于一个DBA是需要熟悉掌握的。但是对已应用程序开发人员,使用命令行工具,显然比较低效,而选择一个GUI工具是必须的,官方的Workbench社区版虽然免费,但是使用上感觉笨重一些,视乎它是针对DBA的GUI工具。Navicat应该是一个针对开发人员做得最友好的工具。SQLyog比Navicat轻量,但是从易用性上看,个人感觉它比Navicat还是差很多。如果你觉得Workbench笨重,Navicat和SQLyog又要付费,那么可选的可能就只有phpMyAdmin了。

注:这里只是列举了本人实际使用过的工具。刚开始接触MySQL时,使用过Workbench(以前不叫这个名字),后来学习了命令行工具。开发中常常使用phpMyAdmin,后来试用了Navicat,感觉它非常不错,于是一直在试用。SQLyog看别人使用,于是自己也折腾了下,感觉不如Navicat,于是就不再试用了。MySQLDumper没有用过,这里基本是凑数了。

jQuery插件 – jstree

官方网址:www.jstree.com

下载的压缩包的dist文件夹中,themes是皮肤文件夹,jstree.js是未压缩的jstree插件,jstree.min.js是min压缩后的jstree插件,100多K的规模已经说明它是一个很重的插件了。

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<!-- jQuery库,大于等于1.9.0 -->
<script src="jquery-1.10.2.min.js"></script>
<!-- 引入jstree -->
<script src="jstree.min.js"></script>
<!-- 引入jstree的皮肤样式 -->
<link rel="stylesheet" href="themes/default/style.min.css" />

<script>
$(function () { $('#jstree').jstree(); });
</script>
</head>
<body>
	<div id="jstree">
		<ul>
			<li>
				服装
				<ul>
					<li>
						女装
						<ul>
							<li>外套</li>
							<li>衬衫</li>
						</ul>
					</li>
					<li>
						男装
						<ul>
							<li>毛衣</li>
							<li>衬衫</li>
						</ul>
					</li>
					<li>其它服装</li>
				</ul>
			</li>
			<li>
				汽车
				<ul>
					<li>女装</li>
					<li>男装</li>
					<li>其它服装</li>
				</ul>
			</li>
		</ul>
	</div>
</body>
</html>

jstree-demo

这个插件跟常见的UI插件操作方式一致,调用jQuery的jstree方法,这个方法可以传递一个配置对象 或 一个字符串命令给它。如果为空,默认的配置继承$.jstree.defaults。

核心配置———————————
1) $.jstree.defaults.data

// Ajax方式获取子节点数据,返回的数据用node承载
// 它的组织结构参考下例
	$("#tree").jstree({
		'core':{
			'data':{
				'url':'/get/children',
				'data':function(node){
					return {'id':node.id}
				}
			}
		}
	});
	
	//直接给出树结构数据(以下是两个根目录,第二个有子节点)
	$('#jstree').jstree({
		'core' : {
	        'data' : [
	            'Simple root node',
	            {
	                'id' : 'node_2',
	                'text' : 'Root node with options',
	                'state' : { 'opened' : true, 'selected' : true },
	                'children' : [ { 'text' : 'Child 1' }, 'Child 2']
	            }
	        ]
	    }
	}); 

	$('#jstree').jstree({
		'core' : {
	        'data' : function (obj, callback) {
	            callback.call(this, ['Root 1', 'Root 2']);
	        }
	    }
	});

2) $.jstree.defaults.core.strings

$('#tree').jstree({
    'core' : {
        'strings' : {
            'Loading ...' : 'Please wait ...'
        }
    }
});

初始化时的提示字符串。
3)$.jstree.defaults.core.check_callback
是否检查回调函数。如果是false, create, rename, delete, move or copy操作都被阻止。最后是指定一个回调函数来进行控制:

$('#tree').jstree({
    'core' : {
        'check_callback' : function (operation, node, node_parent, node_position, more) {
            // operation can be 'create_node', 'rename_node', 'delete_node', 'move_node' or 'copy_node'
            // in case of 'rename_node' node_position is filled with the new node name
            return operation === 'rename_node' ? true : false;
        }
    }
});

4) $.jstree.defaults.core.error
产生错误是调用的回调函数。
5) $.jstree.defaults.core.animation
关闭或展开节点时的动画。
6) $.jstree.defaults.core.multiple
控制是否允许多选。设置true表示可以多选(按下CTRL)
7)$.jstree.defaults.core.expand_selected_onload
展开选中的节点(可以在节点上设置默认选中)
8)$.jstree.defaults.core.worker
默认为true,使用worker来解析请求过来的JSON数据以达到不堵塞当前UI(实际类似打开一个子线程)
9)$.jstree.defaults.core.force_text
是否强制使用纯文本(去除html),默认为false
10) $.jstree.defaults.core.dblclick_toggle

皮肤配置———————————
1 $.jstree.defaults.core.themes.name
皮肤名称,false表示使用default,如果指定了vfeelit,那么应该在themes目录下存在这个目录。
2 $.jstree.defaults.core.themes.url
皮肤样式文件,设置为false可以手动包含皮肤文件。
3 $.jstree.defaults.core.themes.dir
皮肤目录。如果设置了皮肤url,这里的设置是基准目录。
4 $.jstree.defaults.core.themes.dots
5 $.jstree.defaults.core.themes.stripes
6 $.jstree.defaults.core.themes.variant
7 $.jstree.defaults.core.themes.responsive

插件配置———————————
jstree支持的插件配置以$.jstree.defaults.插件名称来存储。参考:
http://www.jstree.com/api

事件————————————-
支持的事件非常多,参考例子:

	$('#jstree').on("changed.jstree", function (e, data) {
		  console.log(data.selected);
	});

参考API,data.selected返回的是一个节点ID的数组(jstree可以配置为多选)

方法————————————-
jstree支持的方法非常多。基本涵盖了所有我们所能想到的功能。

SQL实例之五

logistics

物流商(logistics)会有多个运输方式(logistics_transport),这是一对多的关系。运输方式(transport)定义了系统使用的运输方式,这些运输方式有些物流商(logistics)并没有对应的运输方式,相反,有些物流商(logistics)提供了对应的运输方式,但系统可能并不使用它,所以在运输方式(transport)、物流商(logistics)和物流商运输方式(logistics_transport)中间必须存在一个对应关系,它记录了某运输方式对应的某个物流商以及物流商的某个运输方式。

这样的关系模型,在关系数据库设计中还是非常常见的。

要获取某物流商对应的系统运输方式 或 获取系统运输方式对应的物流商运输方式,只要进行简单的JOIN操作即可。不过现在系统是针对所有系统运输方式,都那个所有物流商与之匹配,然后选择需要的进行设置,举例:

物流商:Lgstcs_A Lgstcs_B
系统运输方式:Trsprt_01 Trsprt_02

输出:

Trsprt_01		Lgstcs_A		选择Lgstcs_A的对应运输方式
Trsprt_02		Lgstcs_A		选择Lgstcs_A的对应运输方式
Trsprt_01		Lgstcs_B		选择Lgstcs_B的对应运输方式
Trsprt_02		Lgstcs_B		选择Lgstcs_B的对应运输方式

这里的transport和logistics是典型的无条件JOIN关系,SQL如下:

SELECT t.transport_code, t.transport_name, l.logistics_code, l.logistics_name
    FROM transport t JOIN logistics l 
        LEFT JOIN transport_logistics_transport tlt ON (tlt.transport_id = t.id AND tlt.logistics_id = l.id) 

这个查询在transport和logistics没有添加新记录的情况下,返回的记录都是一样的,当设置了对应的物流商运输方式时,会对应一条记录插入到transport_logistics,如果没有设置,对应的记录就要删除。

这里搞这个查询主要是因为transport和logistics都是小集合,比如运输方式只有几种到二三十种左右,物流商应该低于十种,所以把它们全部的可能列出来,反而是很直观的。

安全主题 之 密码保存

网站登录的各种账户一般都应该经过哈希处理之后进行保存。但是如果直接对明文进行哈希后保存还是远远不够的,通常我们需要随机生成一个字符串然后和明文合并在一起再取哈希,把得到的哈希和哈希串合并起来保存,在比对密码时,先分解出随机串取和哈希,然后重复一次产生密码的过程得到哈希码,对比新旧哈希是否一致来判断密码是否正确。

这个过程有几个关键点需要注意:
1 随机串的生成应该是长度一样的,最好是跟哈谢结果使用一样的字符集合,因为这样的字符串跟哈希串合并后可以让机器无法识别哈希算法
2 随机字符串和密码哈希合并时,实际上可以按照一定规律插入到哈希码中间去(取出时按照一样的规则),这样做,可以让机器彻底无法识别哈希算法,就算符合某种算法规则,计算结果永远是错误,这个可以彻底杜绝暴力破解。
3 哈希算法的选择,实际只要做到以上两点,使用什么哈希算法已经无关重要,举例,随机串长度选择为32字符,按照固定位置插入到哈希字符中间,首先什么算法得到这样的串,机器已经无法知晓,就算知道,成功分离了随机串,那么意味值你至少需要暴力破解一个33(假设密码最小长度为一个字符)个字符的密码,这是一个天文数字,它足够安全了。

以下是实现以上思维的例子:

class Password{
	
    // 产生随机串
    public static function getPasswordSalt(){
        return substr(hash('whirlpool',mt_rand()),0,20);
    }
    
    // 根据Hash返回salt
    public static function getPasswordSaltByHash($hash){
        if(!empty($hash)) {
            return substr($hash,32,20);
        }
        return '';
    } 
    
    // 随机串插入32字符处
    public static function getPasswordHash($salt,$password){
        $hash = hash('whirlpool',$salt.$password);
        return substr($hash,0,32).$salt.substr($hash,32);
    }
     
    // 比对密码
    public static function comparePassword($password,$hash)
    {
        // 取回随机串
        $salt = substr($hash,32,20);
        // 用这个随机串产生哈希,比对是否相等
        return $hash == Password::getPasswordHash($salt,$password);
    }
     
    // 密码对应的哈希,封装函数
    public static function getHash($password)
    {
        return Password::getPasswordHash(Password::getPasswordSalt(),$password);
    }
}

// 测试
$password = "admin";
$salt = Password::getPasswordSalt();

$password_hash = Password::getPasswordHash($salt,$password);

echo $salt."\n";
echo hash('whirlpool',$salt.$password)."\n";
echo $password_hash;
echo "\n--------------------------\n";

if(Password::comparePassword($password,$password_hash)){
	echo "密码一样\n";
}else{
	echo "密码不一样\n";
}

if(Password::comparePassword("adminadmin",$password_hash)){
	echo "密码一样\n";
}else{
	echo "密码不一样\n";
}

输出:
988d3ae1f014be38072a
d95a8d4b33882d8c850305067180279476a82ecc3e308ff6b4d32b7e050c68d31e049be899c2e49d8d614d2d4ccf03ff50cdfb87a691f3522283af339b81ccc1
d95a8d4b33882d8c8503050671802794988d3ae1f014be38072a76a82ecc3e308ff6b4d32b7e050c68d31e049be899c2e49d8d614d2d4ccf03ff50cdfb87a691f3522283af339b81ccc1
————————–
密码一样
密码不一样

机器可能可以识别插入了随机串(salt)的哈希码所使用的算法,但是随机串(salt)插入了哪个位置和长度是无法判断的,因此不可能从这个串暴力出原来密码,再说这个salt长度有20字符,那么密码至少有21位(一般设置9字符密码,那么长度就会达到29位),就算现在很牛逼的“彩虹表”也最多支持查询10多位的密码暴力破解而已,更比说曝光的哈希字符串本身是一个”错误”的。

算法题 – 洗牌

一副牌(54张,用1到54表示每张牌),要求随机打乱并且对应的位置的牌不能和原来的相同,举例:原来第6张牌是3,那么洗牌后的第6张不能是3。

洗牌这个东西肯定是跟随机相关的,但是这里的随机是有条件的。我试着使用随机取索引(数组下标)并保证这个索引和新牌索引不一样的办法来避免值比较来实现这个算法:

<?php
/*
 * 洗牌算法
 */

// 模拟一副随机打乱的牌
$list = range(1,54);
shuffle($list);

// 新牌
$newList = array();

// 可用下标
$canUseIndex = array_pad(array(),54,1);

for($i=0;$i<54;$i++){
	while(true){
		// 从可用下标中取一个随机下标
		$index = array_rand($canUseIndex);
		// 下标必须和原牌下标不同
		if($index !== $i){
			break;
		}
	}
	// 填写新牌
	$newList[] = $list[$index];
	// 删除已经使用过的下标
	unset($canUseIndex[$index]);
}

// 判断是否有重复
foreach($list as $id=>$vv){
	if($newList[$id] != $vv){
		
	}else{
		echo "Index -> $id - $vv \n";
	}
}

//结果输出
print_r($list);
print_r($newList);

永久链接:http://blog.ifeeline.com/1567.html