20181217
web 安全 XSS CSP CSRF 点击劫持 OAuth cdn
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * xss 攻击预防 * 目前地址栏的xss : http://xxx.html?name=<script>alert(1)</script> * 输入框,图片,富文本 * chrome 会自己转义 * * */ // 转义 < > " ' 空格不转义 function escapeHtml (str) { if(!str) return ''; // 地址栏的xss已经先被chrome 转义了,这代码无效了 str = str.replace(/&/g, '&') str = str.replace(/</g, '<') str = str.replace(/>/g, '>') str = str.replace(/"/g, '&quto;') str = str.replace(/'/g, ''') // html实体 return str } // js 转义 // 地址栏的xss已经先被chrome 转义了,这代码无效了 // let url = JSON.stringify(xx), /* * 富文本xss 过滤 * 黑名单过滤 * 白名单允许 * 插件 npm i xss * * * */ // 富文本过滤 function filter (str) { if(!str) return ''; // 黑名单过滤太多太麻烦 str = str.replace(/<\s*\/?script\s*>/g,''); // 过滤 <script> str = str.replace(/javascript:[^'"]*/g,''); // 过滤 javascript:alert(1) str = str.replace(/onerror\s*=\s*['"]?[^'"]*['"]?/g,''); // 过滤 onerror return str; } // cheerio 解析html, 语法类似 jq function filter2 (html) { if(!html) return ''; var cheerio = require('cheerio'); // 引入 cheerio var $ = cheerio.load(html) // 白名单 var whiteList = { 'img':['src'], 'font':['color','size'], 'a':['href'] } $('*').each(function (index, elem) { if(!whiteList[elem.name]){ // 清空白名单不存在的标签 $(elem).remove(); return; } for(var attr in elem.attribs){ if(whiteList[elem.name].indexOf(attr)===-1){ $(elem).attr(attr,null) // 清空白名单不存在属性 } } }) } // npm i xss 使用第三方库解决白名单 /* * csp 内容安全策略,用于指定哪些内容可执行 * 预防xss 攻击 * * <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';"> * 详见 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP * */ // nodejs 防xss // ctx.set('X-XSS-Protection',1); // 0是关闭 1是打开防御 功能不全面 // // ctx.set(`Content-Security-Policy`,`default-src 'self'`) //csp内容安全策略 /* * CSRF: 第三方网站不用访问你的网站,就拿到你的信息,并调用你的接口乱来 * * 打开某网站A,自己的账号信息保存在cookie中被获取,导致自己银行卡的钱丢了 * 1.设置cookie只能本网站访问 * cookie.set(sameSite = Strict) * * 2.加图形验证码/TOKEN等,必须经过本网站,防CSRF * nodejs npm i ccap 生成图形验证码 * * 3.加 token ,必须经过本网站,防CSRF, token的用户体验比图形验证码好 * * 4. referer 不是很好,有些访问没有referer * 验证referer值 禁止来自第三方网站的请求 * // nodejs * var referer = ctx.reques.headers.referer; * if(!/^https?:\/\/localhost/.test(referer)){ * console.log('error') * } * */ // nodejs: npm i crypto 加密 /* * 点击劫持:原理:自己的网站被第三方用Iframe内嵌, * 自己点击跳出来的网站后,发现自己的钱没了。 * 垃圾邮件点击劫持 * 本网站禁止内嵌,防止点击劫持 * nodejs ctx.set('X-Frame-Options','DENY'); 禁止页面内嵌 兼容ie8 * */ // 如果有iframe嵌入的话,两者值不一致,一刷新假页面,页面会跳转到真正的页面 // 如果被禁止js 则以下代码无效了 if(top.location !==window.location){ top.location = window.location } // <iframe sandbox = '' > iframe 的sandbox 可以禁用 js 等一堆功能 // http协议 窃听,篡cuan改 // 生成ca证书,放入项目中,引用ca证书,使用https来访问,proxy代理抓包则看不到任何请求数据 /* * 密码 md5等加密 mysql 注入 文件上传漏洞(后端文件内容检查、用nginx读取文件再返回、控制读写权限等,nodejs问题不大) * */ // 防止盗QQ,盗身份证进信息等 OAuth 引入 使用QQ登陆某网站,使用了用户授权 // cdn 加速 防dns攻击 https加密 token 时间戳, 签名
es6
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
let {sin, cos} = Math // 取出对应的参数 console.log(sin(30)) /** * 参数默认值 * @param name */ function f1 (name = 'kang') { console.log(name) } f1() /** * 扩展运算符 * @param a * @param param */ function f2 (a, ...param) { console.log(param) // [2,3,4,5,6,7] } f2(1, 2, 3, 4, 5, 6, 7) // 合并数组 let arr1 = [1, 2, 3] let arr2 = [4, 5, 6] let arr3 = [...arr1, ...arr2] console.log(arr3) /* * 箭头函数不能new 不能使用 arguments 可用 rest代替, 箭头函数中的this取决于函数的定义 * */ function Person () { } Person.name=123 // 静态属性 通过类名直接访问
分页 固定内容高 offsetHeight +滚动top scrollTop >= 实际高 scrollHeight
offsetWidth实际获取的是盒模型(width+border + padding)
offsetHeight内容的高(内容高+padding+border)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<template> <div> <div class="list" @scroll.passive="scrollData" ref="scrollOut"> <p class="a" v-for="(val,key) in list" :key="key">{{val.id}}/{{val.name}}</p> </div> </div> </template> <script> export default { data () { return { list: [ {id: 1, name: 'aa'}, {id: 1, name: 'aa2'}, {id: 1, name: 'aa3'}, {id: 1, name: 'aa4'}, {id: 1, name: 'aa5'}, {id: 1, name: 'aa6'}, {id: 1, name: 'aa7'}, {id: 1, name: 'aa8'}, {id: 1, name: 'aa9'}, {id: 1, name: 'aa10'}, {id: 1, name: 'aa11'}, ], canLoad: true } }, created () { document.title = '首页' }, methods: { /* * offsetWidth实际获取的是盒模型(width+border + padding) offsetHeight内容的高(内容高+padding+border) 固定内容高 offsetHeight +滚动top scrollTop >= 实际高 scrollHeight * */ scrollData () { let outH = this.$refs.scrollOut.scrollHeight, currentH = this.$refs.scrollOut.offsetHeight, offH = this.$refs.scrollOut.scrollTop console.log('curr',currentH,offH,'out',outH) if (currentH + offH >= outH && this.canLoad) { this.fetchUser() } }, fetchUser () { console.log('push') this.list.push({ id: 2, name: 222 }) this.canLoad=false } } } </script> <style lang="less" scoped> .list { overflow-y: scroll; max-height: 200px; } .a { line-height: 30px; font-size: 16px; } </style>
基本数据类型:栈内存
函数柯里化
在一个函数中,首先填充几个参数,然后再返回一个新的函数的技术,称为函数的柯里化。通常可用于在不侵入函数的前提下,为函数 预置通用参数,供多次重复调用。
const add = function add(x) { return function (y) { return x + y } } const myadd = add(2) console.log(myadd(10)) // 多次调用 console.log(myadd(20)) // 多次调用
数组去重: [...new Set([1,2,2,3,4,4,5])] // [1,2,3,4,5]
重绘:元素改颜色等样式不影响布局,重绘元素,损耗不大
回流:元素尺寸、结构变了,浏览器会重新渲染页面,称回流。 回流必重绘。
** 避免频繁操作样式,可汇总后统一 一次修改,把 DOM 离线后修改,比如:先把 DOM 给 display:none
(有一次 Reflow),然后你修改 100 次,然后再把它显示出来
页面初次渲染
浏览器窗口大小改变
元素尺寸、位置、内容发生改变
元素字体大小变化
添加或者删除可见的 dom 元素
激活 CSS 伪类(例如::hover)
查询某些属性或调用某些方法
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、scrollWidth、
getComputedStyle()
getBoundingClientRect()
scrollTo()
内存泄露: 定时器未清除、闭包变量
XSS: 地址栏加代码,转义
CSRF: 第三方网站不用访问你的网站,就拿到你的信息, 加图片验证码、加 token等
点击劫持:网站 被iframe, 禁止iframe js 判断 top.location!== window.location 两者要等才对
节流:scroll事件,resize窗口大小调整事件, 设置一个时间,时间到了,执行scroll 回调,这个时间内触发的scroll事件,被忽略
防抖:scroll事件,用户操作频繁,只认停止操作最后一次scroll事件,还是需要设置一个等待时间
apply call bind
let round = { pi: 3.14, area: function (r) { return this.pi * r * r } } console.log(round.area(2)) // pi改为3.14159 // 传个object 来改变this的指向 console.log(round.area.apply({pi: 3.14159}, [10])) // apply(obj,[]) console.log(round.area.call({pi: 3.14159}, '10')) // call(obj,'str') let newRound = round.area.bind({pi: 3.14159}, '10') console.log(newRound()) // bind 要再调用一下
Object.freeze(obj) 冻结对象,不能修改属性值,不报错,修改无效
Object.seal(obj) 冻结对象属性 ,不能删除属性,不报错,删除无效
MVVM: M 数据 V 视图 VM 监听数据改变更新视图,所以只需要关注数据逻辑,不需要操作DOM
VUE响应式原理:
vue采用数据劫持结合发布者-订阅者模式的方式,通过Object.definedProperty()劫持各个属性的setter,getter,在数据变动时,发布消息给订阅者,触发相应的监听回调
Observer.js 利用Object.definedProperty() 监听属性的getter, setter, 实现一个消息订阅器,通知订阅者Watcher,
Compile.js 解析模板指令,替换成数据,渲染视图,先把dom节点转换成文档碎片fragment,提高性能
watcher.js 属性变动,接收到消息通知时,调用自身的update()方法,触发Compile中绑定的回调,更新视图
watch: {
obj: {
handler (newValue, oldValue) {
console.log('obj changed')
},
deep: true
}
}
给 data 添加新属性 this.$set(this.obj, 'b', 'obj.b')
数组的delete
let arr = [1,2,3,4]
delete arr[1] // 下标不变 0,2 3
vue的 this.$delete // 下标重新变成 0,1,2
vue-router 默认 hash模式,单页应用,当url改变时,页面不会重新加载
还有history模式,利用h5的history.pushState来跳转,需要后台配置一个静态页面,当url匹配不到时时进行跳转
地址变更的监听, window.addEventListener('hashchange',()=>{})
vue-router作为一个插件,使用Vue.use()来注册;new Vue()时会执行vue-router注入的beforeCreate()钩子函数,执行 hashchange,会匹配到地址route, 赋值给vm._route, 被Vue的数据劫持拦截到,触发视图更新
vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 只用 state 和 mutations 不用常量,不用getters(过滤state数据) 和 actions(异步提交 mutation)
export default new Vuex.Store({
state: {
money: 10
},
mutations: {
money (state, val) {
state.money = val
}
}
})
getState(){
console.log(this.$store.state['money']) // get
},
setState(){
this.$store.commit('money',30) // set
}
main.js 全局 setVuex getVuex
Vue.prototype.setVuex = function (key, val) {
return this.$store.commit(key, val)
}
Vue.prototype.getVuex=function (val) {
return this.$store.state[val]
}
fetch
es6新出,只有网络故障时或请求被阻止时,才会reject() 后端返回404 500,要自己处理 promise 之前 是 xmlHttpRequest
scoreCalcAPI.js
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
let develop = 'xx' // 开发 let release = 'xx' // 测试 /* * 将用户在各应用的行为,发送给会管家,由会管家计算用户的分享,分销积分. * obj: 接口参数对象 * obj={ AppId: "xx", // 应用Id UserId: "xx", // 用户Id MarkId: "xx", // 触发表主键Id,如添加了一条分享邀约,此处填分享邀约的Id MarkDesc: "xx", // 触发内容,如添加了一条分享邀约,此处填分享邀约标题,若是评论,则填写评论的内容. ChannelCode: "xx", // 渠道编号 RuleType: xx, // 数值 } * */ function scoreCalcAPI (obj) { let URL, href = window.location.href if (href.indexOf('http://p')>0) { URL = production } else { URL = release } let xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { // 请求成功 console.log(xmlhttp.responseText); } } xmlhttp.open("post", URL+ 'api/commonapi/pushpoint', true); xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlhttp.send("pointdata=" + JSON.stringify(obj)); }
Synthesis.js canvas图片合成
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
export class SynthesisShare { constructor(w, h) { //初始化canvas let canvas = document.createElement("canvas") canvas.width = w canvas.height = h this.canvas = canvas this.ctx = canvas.getContext("2d") } /** * 合成图片元素 * @param elements 一个包含了SynthesisElement类的数组 * @returns {string} 合成后图片数据 */ synthesisImage(elements) { this.clear() if (Array.isArray(elements)) { for (let i = 0; i < elements.length; i++) { let e = elements[i] this.drawElement(e) } } else { this.drawElement(elements) } return this.canvas.toDataURL('image/png') } drawElement(e) { this.ctx.drawImage(e.image, e.x, e.y, e.w, e.h) } /** * 方便多次复用canvas,清理画面 */ clear() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); } } /** * 合成元素类 */ export class SynthesisElement { constructor() { this.url = '' this.x = 0 this.y = 0 this.w = 0 this.h = 0 this.image = null } /** * 初始化合成元素 * @param url 图片地址 * @param x x坐标 * @param y y坐标 * @param w 宽 * @param h 高 * @returns {Promise<void>} */ async init(url, x, y, w, h) { this.url = url this.x = x this.y = y this.w = w this.h = h this.image = await this.loadImage(url) } /** * 加载元素图片 * @param url * @returns {Promise<any>} */ loadImage(url) { // console.log(url); return new Promise((resolve, reject) => { let img = new Image(); img.setAttribute('crossOrigin', '*'); if (!url) { reject('err') } img.src = url img.onload = () => { resolve(img) } }) } }