青菜不是葵花


  • 首页

  • 归档

  • 分类

  • 标签

  • 关于

CSS中的高度系列

发表于 2017-07-18 | 分类于 前端
各类高度 包含选项
height content
clientHeight content+padding
offsetHeight content+padding+border+scroll
scrollHeight (子元素高度 <= content)? clientHeight:(子元素高度 + padding)

注意:当我们按照如下设置:

1
2
3
4
div {
height: 200px;
overflow-x: scroll; //始终显示滚动条
}

此时height的计算结果为183px,因为横向滚动条的高度为17px,需要减去这17px才是元素的实际高度值。

Web应用初体验——登录登出验证系统

发表于 2017-07-17 | 分类于 后端

本系统所用技术包括:jade模板(加强的html),bootstrap(css样式集),express(Web应用框架),js,nodejs,mysql,token验证等。

GitHub地址:https://github.com/qingywen/DEMO/tree/master/Greeni

基本功能如下:

  1. 初次登录该系统首页可浏览公共页面并进行注册。
  2. 成为注册用户后可登录个人中心,关闭浏览器仍保持一天之内可随时登陆。
  3. 登录后可注销,此后则只能浏览公共页面。
  4. 登录一天后或者注销后,若访问个人中心则自动引导到登录页面。

step1

先写前端界面,包括index.html(公共页面),login.html(登录页面),signup.html(注册页面),my.html(个人中心),这里用到了bootstrap的示例页面signin,carousel和dashboard的页面结构和样式。

也许会有人跟我一样刚开始接触bootstrap并不知从何用起,以下做个简短说明:bootstrap是一个css样式集,用的好可以为你的项目锦上添花,用不好反倒累赘,因此还需好好琢磨。了解bootstrap的菜鸟教程可以浏览菜鸟教程。实际上,bootstrap3的中文官网已经讲解的非常详尽。不过仍建议先阅读菜鸟教程进行热身。

页面js主要功能为表单提交,提交前先进行简单的验证,再序列化表单,接着用ajax提交到服务器,最后关闭表单的默认提交功能。

step2

前端界面搭建好后,我们来新建一个数据库,数据库表中只有一个users表,结构如下:

Field Type Null Key Default Extra
P_Id int(11) No PRI NULL auto_increment
email varchar(255) NO UNI NULL
password varchar(255) NO NULL

现在表数据如下:

P_Id email password
1 1195259521@qq.com 123
9 qingywen@gmail.com 123
12 wenwu@gmai.com 123
14 123@qq.com 123

数据库导出文件为greeni.sql,在本地数据库中将sql文件导入新建数据库greeni即可。

step3

前端、数据库搭建好后,我们即可开始整个应用的系统搭建,通过后台逻辑将前端和数据库连接起来。

这里在一个新文件夹中用express进行项目初始化,当然前提是本地用npm安装了express。接着在package.json中加入db、ejs、jsonwebtoken三个模块,然后用cnpm install进行安装。

这里我们需要将app.js文件中默认的渲染引擎从jade改为ejs。

1
2
3
4
// view engine setup
app.set('views', path.join(__dirname, 'views'));
pp.engine('.html', require('ejs').__express);
app.set('view engine', 'jade');

修改routes文件夹下index.js、my.js的路由定向和事件句柄,此时要用到数据库,因此新建db文件夹,在db目录下新建DBConfig.js和SQL.js,分别用作数据库的连接配置文件和SQL语句模块。

在写登录验证的逻辑时,我们根据用户邮箱用jsonwebtoken随机生成一个令牌,并发送用户,用户在cookie中保存此令牌,在下次请求私有资源时则不必明文发送密码,而是在发送请求时随着cookie自动将令牌发送给服务器,服务器收到令牌验证成功后则返回用户所请求的资源,否则令牌有可能被恶意更改,则拒绝请求。

此时系统搭建完毕,我么可以用npm start来启动项目,注意bin文件夹写的端口号为8000,因此访问地址为:127.0.0.1:8000。

step4

最后我们想做一个小优化,如果每个用户登录到个人中心的页面都一样的话,这个个人中心恐怕是个假个人中心,因此我们需要对不同的用户请求返回一样的页面结构但是不同的用户信息。为求简易,这里我们只对每个用户返回各自的email,并添加到页面上。

因为页面首先需要在服务器上渲染才能返回给客户端,最经济的思路当然是在服务端渲染出不同的页面再返回给客户端。而html并没有变量的功能,因此我们使用jade模板(现更名为pug,但jade模块仍有效)来代替html。jade是一个非常高效的模板引擎,提供了变量、for循环等功能。有一个非常好的jade语法学习网站安利给大家。这里我们需要将之前的html转为jade,手动改编太麻烦,用这个html2jade的网站来转化吧。

得到jade格式的前端文件,我们修改app.js的模板引擎设置,将ejs引擎改回默认的jade引擎:

1
2
3
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

在routes下的my.js中,返回个人中心页面同时,返回用户的email:

1
res.render('my',{email: decoded.email});

修改views文件夹下my.jade文件中的标题,用来接收服务器传送来的email变量:

1
a.navbar-brand(href='#') #{email}

此后大功告成,每个用户登陆后访问个人中心的页面,其页面标题都是自己用来注册的email,因此此个人中心成立。

其他

  1. 表单序列化方法serialize(),阻止表单默认提交方式preventDefault(),以及设置cookie的CookieUtil.set()均来自《JS高级程序设计》。
  2. 登录后通过令牌验证来避免密码泄露用到jwt.sign() jwt.verify(),建议细看jsonwebtoken包下的README.md,讲解很详细。
  3. README.md文件是个好东西,许多网上的经验贴都没有官方使用文档讲的简单易懂。sublime有许多markdown实时阅览插件很方便,此博主有分享。
  4. 源代码是最后版本,因此前文所述中间过程可能不能详尽体现,但可以按照上述方式进行此系统的构建。这篇记录不是使用说明,仅作一个Guide,有问题Google和百度并行解决。若解决不了可联系数据库中P_Id为1的邮箱,我会尽快为你回复。

浅谈令牌原理及漏洞分析

发表于 2017-07-11 | 分类于 后端

相关资源:
令牌讲解
使用方法

基本概念

token

中文译名为令牌,包含三个部分:header、payload、signature。

1
token = encodeBase64(header) + '.' + encodeBase64(payload) + '.' + encodeBase64(signature)

header

此为令牌的头部,可以在其中设置签名算法和token类型,如下所示:

1
header = '{"alg":"HS256","typ":"JWT"}'

payload

该部分包含我们需要设置的信息:

1
payload = '{"loggedInAs":"admin","iat":1422779638}'

signature

signature则是将header和payload结合并用指定算法进行签名(加密)的结果。

1
2
3
key = 'secretkey'
unsignedToken = encodeBase64(header) + '.' + encodeBase64(payload)
signature = HMAC-SHA256(key, unsignedToken)

令牌原理

验证令牌有效性是整个机制的关键:我们的方法是根据header里提供的信息,对payload再次进行签名,并将结果与signature进行比对,如果一致,则令牌有效;若不一致,则说明是经过恶意改动的无效令牌。

关键漏洞

  1. 因为我们的令牌中header和payload部分可直接通过解码获得,因此我们的签名算法等信息可以被任意人员获取,且signature部分的签名算法是通过header里的信息来决定的。因此攻击者可以将令牌头部信息设置为alt: none,因此服务器可能会将未经验证的signature当做已经验证过且有效的token。这样任何人就可以随意访问我们的系统。但是,现在大多数库都对此有一个防御机制:若提供了secret key,则那些设置为alt: none的令牌就无效,故而解决。
  2. 令牌机制的关键漏洞在于服务端的验证算法取决于令牌里的header信息。如果令牌采用RS256算法进行签名,一旦攻击者获得public key,则可以用如下方式来伪造令牌:将令牌的算法设置为HS256,并将secret key设置为RS256算法的签名公钥,这样就可以鱼目混珠啦。如下图所示。但这种方式理论上是这样,实际上的测试结果并不如此,而是返回invalid algorithm的错误。若有大神知道还求解答。不过针对此种错误,jsonwebtoken已经给出了相应的解决方案,服务端在验证时可以指定签名算法的类型,因此可以避免伪造令牌。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var fs = require('fs');
var jwt = require('jsonwebtoken');
var cert_pub = fs.readFileSync(__dirname + '/rsa-public-key.pem');
var tokenPayload = {
foo: "bar"
}
var forgedToken = jwt.sign(tokenPayload, cert_pub, { algorithm: 'HS256' });
jwt.verify(forgedToken, cert_pub, function(err,decoded){
if (err) {
console.error(err);
} else {
console.log(decoded);
}
});

mysql-setup

发表于 2017-06-19 | 分类于 数据库

今天使用mysql -uroot -p在终端连接数据库时,一直报错ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) The current server installed: "C:\Program Files\MySQL\MySQL Server 5.6\mysqld,通过谷歌和百度搜寻了很多方法,大多都是关于如何重置root用户的密码,但由于版本不同,且答案冗杂,到最后也没能解决。因想起大一由于配置不好jdk的环境变量就放弃了Java,决定不能一头撞死,于是搜寻了下卸载MySQL的方法,准备重装。

卸载MySQL

第一步:在控制面板-程序和功能中找到mysql进行卸载
第二步:找到安装目录,若仍有文件残余,则用360进行粉碎
第三步:清楚环境变量里mysql的配置
第四步:点击运行-regedit,打开注册编辑表,删除如下三个值:
HKEY_LOCAL_ACHINE\SYSTEM\ControlSet001\Services\Eventlog\Application\MySQL
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Services\Eventlog\Application\MySQL
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\MySQL
至此,清理完毕

安装MySQL

MySQL5.7安装教程中写的非常完整,每一步都十分必要,因此不再赘述。

js-pro-event

发表于 2017-06-05 | 分类于 前端

window.event

支持:IE,Opera,Chrome,Safari
返回:一个event对象,并被当前对象填充。
区别:IE中的window.event返回的event对象通常都含有固定的成员,其中只有一部分成员被当前对象填充;Opera,Chrome,Safari中,返回的event对象类型取决于当前事件。

event

支持:IE9及以上,Opera,Chrome,Safari
发生:当一个事件产生,一个event对象则会被初始化并作为第一个参数传递给事件处理程序,event对象的类型取决于当前对象。且与window.event引用自同一个对象。
区别:IE9之前event对象当且仅当用attachEvent方法注册事件时才存在,并且是window.event的复制,但并不是同一对象。

js-pro-ecma

发表于 2017-05-17 | 分类于 前端
  • ECMAScript中的一切都区分大小写。
  • ECMAScript的变量是松散类型的,可以用来保存任何类型的数据。
  • ECMAScript的5种简单数据类型(基本数据类型):Undefined、Null、Boolean、Number、String。还有一种复杂数据类型——Object。
  • 操作符typeof null返回object,高程中解释说“null被认为是一个空对象的引用”,但规范里说明到对象值缺失的数据,其数据类型为null。
  • 对未声明和未经初始化的变量执行typeof都会返回undefined。有一点要注意的是变量提升,即使变量已声明,解释器还是会先给变量赋值为undefined,直到上下文准备好,这个过程比较隐秘。
  • 如果定义的变量将来用于保存对象,那么最好将该变量初始化为null。
  • var cat; console.log(cat == null); 会返回true,是因为null == undefined。
  • 不要测试浮点数值的大小!
  • 数值转换的三种函数:Number()、parseInt()、parseFloat()。
  • 转换为字符串:toString()方法,以及String()函数。
  • Object七大法宝:constructor、hasOwnProperty(propertyname)、isPrototypeOf(object)、propertyIsEnumerable(propertyName)、toLacaleString()、toString()、valueOf()
  • 操作符包括:一元操作符、按位操作符、布尔操作符、乘性操作符、关系操作符、相等操作符、赋值操作符、逗号操作符
  • switch在比较的时候使用的是全等操作符。
  • ECMAScript中的函数定义的时候不必指定是否返回值;return语句可以不带有返回值;严格模式下不能出现两个参数同名的情况。
  • ECMAScript没有函数签名,相同的函数名称即使参数个数不同,后定义的会覆盖前面的,也就是没有重载。ECMAScript函数中的参数在内部是用一个数组来表示的,可以通过arguments对象来访问所传参数,且对所传参数个数没有要求,并不需要一定和命名参数个数相等。这一特性可以弥补没有重载的缺憾。
  • 在通过函数的arguments对象修改参数值时,会反映到对应的命名参数中(内存空间相互独立,只是值会同步)。arguments对象的长度由传入参数个数决定,若修改长度之外的参数值,将不会反应到对应的命名参数中。而没有传递值的命名参数将自动被赋予undefined值。严格模式下不允许重写arguments值。
  • ECMAScript中所有的参数传递的都是值,不可能通过引用传递参数。
  • 检测变量是什么基本数据类型:typeof操作符,返回值可能为undefined、string、boolean、number、object、function。检测变量是什么类型的对象:instanceof操作符,如果给定对象是引用类型的实例,则返回true。
  • JavaScript具有自动垃圾收集机制,垃圾收集器会按照固定的时间间隔周期性的执行这一项操作。
  • 基本类型值在内存中占据固定空间的大小,因此被保存在栈内存中;引用类型的值是对象,保存在堆内存中。
  • 标记清除是目前主流的垃圾回收机制。还有一种是引用计数,但当代码中存在循环引用时,引用计数方法会导致问题。
  • 主动解除引用可以消除循环引用现象,对垃圾收集也有好处。因此需要及时解除不再使用的全局对象、全局对象属性以及循环变量的引用。
  • 数组的length属性可读可写。
  • 函数实际上是对象,每个函数都是Function类型的实例。由于函数是对象,函数名实际上是一个指向函数对象的指针,不会与某个函数绑定。
  • 定义函数的三种方式:函数声明,函数表达式,构造函数方式(不推荐)。
  • 函数声明和函数表达式的唯一区别:在代码开始执行前,解析器会进行函数声明提升将函数声明添加到源代码树的顶部。
  • 要访问函数的指针而不是执行函数的话,必须去掉函数名后的括号。
  • 函数有两个特殊的内部属性:arguments,包含传入函数的所有参数,且具有一个callee属性,指向拥有arguments对象的函数;另一个是this,引用的是函数据以执行的环境对象,也就是说谁调用了该函数,this就指向谁。
  • 函数对象的另一个属性:caller,保存着调用当前函数的函数的引用。
  • 引用类型与基本包装类型的主要区别就是对象的声明周期。基本包装类型包括:Number、String、Boolean。
  • 布尔表达式中所有对象都会被转为true。
  • encodeURI只对空格编码,encodeURIComponent()会编码所有的非字母数字字符。
  • 在所有代码执行之前,作用域中就已经存在两个内置对象:Global和Math。
  • 对象的属性分为数据属性和访问器属性。数据属性有四种行为描述:configurable,enumerable,writable,value。访问器属性有四种行为描述:configurable,enumerable,get,set。都可以通过Object.defineProperty(object,propertyName,{})进行定义。读取则通过Obeject.getOwnPropertyDescriptor(object,propertyName)方法。

async和defer的区别

发表于 2017-05-16 | 分类于 前端

当浏览器遇到script的时候,有三种情况:

1
<script src="test.js">
没有defer或async时,浏览器会立即加载和执行该脚本,并停止加载和渲染文档元素。
1
<script src="test.js" async="async">
有async时,浏览器会异步加载和执行该脚本,其异步过程不影响文档元素的加载和渲染。
1
<script src="test.js" defer="defer">
有defer时,浏览器会异步加载该脚本,不影响文档元素的加载和渲染,但执行脚本的过程延迟到页面解析完毕后运行。
以上三个过程如下图。
图片来源(https://segmentfault.com/q/1010000000640869)
1
2
<script src="test1.js" defer="defer">
<script src="test2.js" defer="defer">
此外,当同时有多个延迟脚本时,如上图,此时脚本1和脚本2会并行加载,但执行的顺序却有些复杂。HTML5规范延迟脚本按照它们出现的先后顺序执行,但实际中浏览器的实现机制却各不相同。当我在脚本1中写出一个执行时间远大于脚本2中的函数时,Chrome浏览器按照规范先执行脚本1,再执行脚本2;IE则会先执行脚本2,再执行脚本1;firefox同Chrome。

CSS的几种消失机制

发表于 2017-05-15 | 分类于 前端

display: none

元素消失,不占据页面空间。会导致页面的重排和重绘。无法获取鼠标事件。

visibility: hidden

元素消失,占据页面空间。导致页面的重绘。无法获取鼠标事件。

opacity: 0

元素没有消失,只是看不见。导致页面的重绘。可获取鼠标事件。

position: reletive; left: -10000px;

元素定位在可视区域之外。导致该元素的重排和重绘。无法获取鼠标事件。

height:0 or width:0

若设置overflow:hidden,元素无法获取鼠标事件。若没有,元素宽高仍为0,但其子元素会撑破父元素,因此仍可以通过子元素获取到鼠标事件。

z-index:-1

很少用到这个,不过有同学提到了,因此还是列出来。

此外,这元素都可以用选择器定位到,可见仍是可进行DOM操作的元素。因此虽然无法获取鼠标事件,但可以用js来触发事件响应。例如用$('.item').click()即触发点击事件。

opencv+VS配置问题

发表于 2017-05-15 | 分类于 图像处理

安装了VS2012并下载了opencv249后,按照Lennon的方法配置了系统环境变量、包含目录、库目录以及附加依赖,运行程序则开始出现各种错误,记录一下。

找不到XXX.lib文件

确定附加依赖项配置文件路径及版本号正确。文件名后面的数字及版本号。

无法找到xxx.dll文件

缺乏动态文件库,可以手动下载并添加到C:\Windows\System32目录(或报错目录)下,下载地址为Download missing dll

无法打开或找不到PDB文件

anwser1.PNG此图即可。

“wb.exe”已退出,返回值为 -1 (0xffffffff)

查看main函数有一个输入文件路径填写不正确,因此程序退出一闪而过。将文件路径填写正确后才开始正常运行。具体情况具体分析。

其他要注意的地方

opencv中v10 代表适配 Visual Studio 2011
opencv中v11 代表适配 Visual Studio 2012
opencv中v14 代表适配 Visual Studio 2015
经验一:百度没有谷歌靠谱。
经验二:以后需要仔细查看报错信息而不是直接复制粘贴进行搜索。

document.write与innerHTML

发表于 2017-05-09 | 分类于 前端

看到《DOM编程艺术》第7章,有几点值得记一下。

document.write 与 innerHTML

document.write是一个古老的方法,它在W3C上定义了两种用途:一是在文档中输出HTML,另一种是在调用该方法的的窗口之外的窗口、框架中产生新文档(记得用close()关闭)。看似document.write对产生一个新的文档或窗口这样的用途很有意义,但书上说:

document.write的最大缺点是它违背了“行为与样式分离的原则”。即使把document.write语句挪到外部函数里,也还是需要在标记的<body>部分使用<script>标签才能调用那个函数。

厮以为作者在这里讲的并不清楚,document.write违背了行为与表现分离的原则并不在于函数需要放在外部引用还是内部引用,而是因为当我们写了一段document.write来产生一个HTML文档时,这个新文档的结构hin可能是这个样子的:
document.PNG如你所见,<script>标签在<p>标签前面,也就是说document.write只能在现有文档结构的最后添加元素,因此只要使用document.write,则必定有HTML标签在<script>标签之后。这样的标记顺序十分的混乱,且里面的<script>标签也不是我们想要的,这样让我对document.write的好感度一下降低零点。

反观用innerHTML来实现这个输出文档的功能,其输出文档结构如下:
innerHTML.PNGLook!这才是我们想要的行为与表现分离的文档结构!虽然innerHTML并不是DOM标准的组部分,但是已经包含在HTML5的规范中。几乎所有的浏览器都支持该属性,绝大多数情况下可以完美取代document.write方法。书上有一个很恰当的比方,“innerHTML就像一把大锤,而DOM则是一把手术刀”,我们可以用innerHTML大刀阔斧的直接插入网页内容,但是若想获取或修改DOM节点的属性等,我们还得需要DOM里更精确的方法如createElement与createTextNode等。

总结一下document.write与innerHTML的区别:

  • write是document对象的一个方法,直接写入到页面的内容流,如果之前没有调用document.open,浏览器会自动调用document.open。每次写完关闭之后重新调用该函数会导致页面被重写。重写内容会被添加到<script>元素后,破坏结构和行为分离的原则。
  • innerHTML则是DOM页面元素的一个属性,代表该元素的html内容,可以精确到单个元素来进行修改,良好的而体现了结构和行为分离的原则。

重回图片库:结构样式分离升级版

当有一段标记仅是为脚本服务的,即为了让DOM脚本处理它们,那么用DOM方法来创建它们才是最合适的选择

行为与结构分离的三层逐级进化概念:

  1. 给HTML文档引用外部脚本。
  2. 在HTML里设置挂钩,在外部js文件中给DOM节点添加事件,而不是在节点中添加类似onclick=“func()”这样的属性。
  3. 只是为了DOM操作而存在的DOM节点应该动态创建,而不是直接出现在标记里。

渐进增强与Ajax

Ajax是一种用来与服务器异步交互的技术,其核心为XMLHttpRequest,很多书中都会大谈特谈Ajax的方便之处,但《DOM编程》显然为一股清澈之流,作者认为能通过Ajax实现的应用一定也可以通过非Ajax技术来实现,且构建Ajax网站最好的方法是先构建一个常规的网站,再去Hijax它。这个观点跟平稳退化有着相似之处。十分欣赏作者在网站构建方面的缜密思维,总会考虑到如果这个方法可用,则怎么去做,如果这个方法不可用,再怎么去做。从不假设用户的行为与环境,而是从最基本的条件出发,这样使得少量的特殊用户也能很好的浏览我们的站点。

123
青菜

青菜

主攻前端方向,偶尔来点杂文

28 日志
5 分类
15 标签
GitHub zhihu
© 2018 青菜
Patience is bitter, but its fruit is sweet.