CSRF(跨站请求伪造)
通过手段欺骗用户访问第三方页面,通过用户在第三方页面执行常规的操作来进行欺骗,这些操作包含了用户已登陆网站的一些请求,当用户点击该请求时,请求会发往原站,同时由于用户已经登录,所以原站是能验证用户的,通常会判断为合法请求。防御:
1.表单增加hash值,就像laravel和django自带的表单csrf验证一样,而这个hash值和用户是对应起来的,第三方是无法同时拿到这两个东西的
2.验证码
最近团队由于项目需求的增多,需要单独设计一个权限系统出来,参考了网上很多的权限/访问控制系统以及一些设计原则比如RBAC(Role-Based Access Control),这里,记录一下自己对权限系统的一种构想。需要注意的是,RBAC中也有分组的概念,但是它的分组仅仅是为了将相同权限的人集中在一起。我的构想虽然没有分组的概念,但是也符合RBAC原则的,只是因为我做的更彻底,不仅将角色与权限直接关联,更是将用户与权限直接关联,产生了一定的冗余,更适用于人数少但是权限多的情况。
应用表
字段名 | 字段注释 |
---|---|
id | app_id |
角色表
字段名 | 字段注释 |
---|---|
id | role_id |
app_id | 应用ID |
name | 名称(英文) |
beta | 备注 |
权限表
字段名 | 字段注释 |
---|---|
id | privilege_id |
app_id | 应用ID,不提供表示通用的权限 |
name | 名称(英文),例如projects表示项目相关的权限,这里如果想要有更复杂的层级关系,可以用点号进行分割,但不建议增大权限的层次 |
beta | 备注 |
操作表
记录一些通用的操作,例如增删该查等。当然,这里的操作表可以省略,而直接将操作以父子关系放在权限表中。
字段名 | 字段注释 |
---|---|
id | operator_id |
app_id | 不提供表示通用的操作 |
name | 名称(英文),例如: add,delete,edit,query等,或者页面上index,create,admin等 |
beta | 备注 |
用户与权限关联表:此表与角色权限对应表之间是有一定冗余的,但是考虑到个性化需求以及第三方APP的需要,所以在可以忍受的范围内
字段名 | 字段注释 |
---|---|
id | |
user_id | 用户ID |
role_id | 角色ID: 为0表示该权限是独立分配的,独立分配的优先 |
privilege_id | 权限ID |
operator_id | 操作ID |
disabled | boolean |
角色与权限对应表
可以将用户与权限的关联表和角色与权限的关联表合并,使用target
来标识
字段名 | 字段注释 |
---|---|
id | |
role_id | 角色ID |
privilege_id | 权限ID |
operator_id | 操作ID |
用户与角色对应表
字段名 | 字段注释 |
---|---|
id | |
role_id | 角色ID |
user_id | 用户ID |
添加权限: 直接添加,并设置好其
给单独用户添加权限:保存在角色与权限表中,这样用户的实际权限就应该是用户绑定角色拥有的权限与用户单独分配的权限之和。
给单独用户删除权限:如果是单独添加并且绑定角色所没有的,直接删除关联关系;如果是绑定角色所关联的权限,那么将关联表中的权限标识为禁用;如果是单独添加并且绑定角色也有的,同样标识为禁用。所以最终,用户的实际权限是用户绑定角色拥有的权限与用户单独分配的权限之和减去禁用的权限,当然由于有冗余,直接在关联表中读取没禁用的权限即可。
删除用户角色: 直接根据role_id进行disabled,添加了就不直接删除
删除用户权限:直接根据role_id进行disabled,也不直接删除
其他平台登录:只需要在登录的时候获取该用户在该平台的所有的额权限即可,连分组都不需要,因为权限都是在权限系统中进行统一登记注册的,所以其他平台不用单独存储权限列表,而只需要获取获取当前用户的权限,放到缓存里面即可,例如redis,可以直接存储为一个列表user:privileges []
前端的自动化构建工具,能够自动化处理一些常见的任务:
全局安装:npm install gulp -g
当前项目安装: npm install --save-dev gulp
安装后,会在当前目录生成一个node_modules
目录,然后执行npm init
初始化当前项目,根据提示输入一些项目的基本信息,然后会生成一个package.json
文件
app/ # 开发目录,存放源文件
css
fonts
images
js
dist/ # 存放生产环境下的内容
gulpfile.js
node_modules/
package.json
var gulp = require('gulp'), # 去node_modules下导入相应的包
livereload = require('gulp-livereload'),
del = require('del'),
notify = require('gulp-notify'),
browserify = require('gulp-browserify'),
jshint = require('gulp-jshint');
gulp.task('scripts', function(){
//这里可以不用把所有的.js合并成一个,而是可以按需合并,比如每个文件都需要用到的就合并成一个,其他单独的则单独合并,不许return,写两个gulp.src即可,比如gulp.src('app/js/base.js').pipe......和gulp.src('app/js/new.js').pipe...
.pipe(browserify({
insertGlobals: true,
debug: !gulp.env.production
}))
.pipe(concat('base.js')) // 这是把上面所有的js文件合并为一个文件
.pipe(gulp.dest(DEST+'/js'))
.pipe(rename({suffix: '.min'}))
.pipe(uglify())
.pipe(gulp.dest(DEST+'/js'))
.pipe(notify({
message: 'Scripts task complete'
}));
gulp.task('clean', function(cb){ # 定义任务
del(['dist', cb]);
});
gulp.task('default', ['clean'], function(){
gulp.start('scripts');
});
gulp.task('watch', function(){
gulp.watch('app/js/index.js', ['scripts']); # 见识文件,当监控的文件变化时执行相应的任务
livereload.listen();
gulp.watch(['dist/**']).on('change', livereload.changed);
});
在命令行执行gulp task_name
就可以运行该任务了
这本书在最近一年里面特别火,以前好多从来不看书的朋友都在朋友圈里发这本书的照片,仿佛大家一下子爱上了文字。我当然不会因为人家看我就看的,只是每次一看到这本书的名字,就不由得想起日本的情感电视剧《深夜食堂》,不知道为什么,就感觉他们可能会有好多相似的地方,应该会和《深夜食堂》一样,在很平淡的叙述中体会其中的韵味,引人入胜。于是最后,我还是看了。它和《深夜食堂》有相似的,也有不同的。《深夜食堂》是把精彩的故事写平淡,而《解忧杂货店》则是把本应该跌宕起伏的故事写得平淡,然后一步一步将错综复杂的故事串起来,功力同样深厚,毕竟是东野圭吾的作品,不得不叹服。
浪矢杂货店,一个连接过去与未来的杂货店,连接了一个老头与三个坏孩子一起帮助别人排忧解难的故事。通过几个矛盾人物与他们之间的书信交流来推动故事发展,更精妙的是几个矛盾人物之间又刚好有一丝丝的联系,联系虽然不深,但是却足以让人看到其中环环相扣的逻辑,被作者的思维所震撼。
我一直都像很多国人一样,哪怕自己过得再堕落,哪怕自己不相信大道理也要自以为是地告诫别人,偶尔,也会很认真地回应别人的玩笑,因为“我们也要尽可能帮助他人,你没有无视他开玩笑写的问题,而是认真回答,所以才回一直牢记在心里”。我希望自己能力所能及地帮助他人,特别是刚入编程领域的小学弟们,我很希望他们来问我问题,哪怕最简单的,哪怕最幼稚的,我也要认认真真地回答。遗憾的是,我并不是什么大神,所以找我咨询的学弟微乎其微呀。
人生,重要的是选择。作为一个挣扎于选择困难症不能自拔的天秤座的我来说,其实,很多时候在犹豫不决的时候,决定其实早就在自己心中,只是,要么因为各种客观原因,要么就是自己主观上的回避。最常见的莫过于购物的时候,说简单点就是没钱,所以才会犹豫不决,这个倒很好解决,多挣钱就行。又比如人生中的抉择,月兔想参加奥运会,但是怕选不上所以找了一个照顾男友的理由;克朗想成为歌手,然后又以照顾父亲为理由放弃追逐梦想。追逐梦想,能够轻松实现自己想要的,谁又不想呢。只是现实很多时候过于残酷,我们不知道继续坚持会不会得到自己期待的结果,只是从现在的情形看来,结果肯定不会太好,这个时候如果来了一个很合理的,连自己都能说服的理由,又何尝不想放弃呢,放弃多好,努力不一定成功,但不努力,一定很轻松。似乎,只要不是自己的原因,这样的理由就非常容易被接受,也不至于被身边的人嘲笑。但是,终究不过是自欺欺人。明明知道自己想要的,但还是选择逃避。不要轻易被自己的努力感动,真的,你做的努力相比于真正成功了的人,是微乎其微的。
Command + Shift + 4
普通截图Command + Shift + 4
,然后按空格
,对指定窗口截图Command + Shift + 3
全屏截图Command + Shift + .
最好的工具mac-cleanup-sh
~/Library/Application Support/Code/User/workspaceStorage
: VS Code的工作区文件夹唉,但是所有的扩展都会重建这个文件夹,把年代久远的删除了
在Preferences
中不仅可以设置默认Profile
的窗口样式等,还是通过新建不同的Profile
来实现自动登录。例如:
这样如果想要进入某个服务器,只需要在iterm2
中点击顶部菜单Profiles->aliyun
即可直接进入服务器。对于复杂的输入密码的场景,可以参考Linux 手册的expect
进行配置
except
登录服务器的情况下,使用lrzsz
不会起作用首先使用brew install lrzsz
安装命令行工具
然后保存iterm2-send-zmodem.sh 和iterm2-recv-zmodem.sh两个脚本到/usr/local/bin
目录下
打开iterm2
,Perferences->Profiles->Advanced->Triggers->Edit
,添加如下trigger
1 | \*\*B0100 Run Silent Coprocess /usr/local/bin/iterm2-send-zmodem.sh |
1 | export ALL_PROXY=socks5://127.0.0.1:1080 # homebrew走ss代理 |
活动监视器->窗口->CPU使用率/CPU历史记录/GPU历史记录
1 | vim /private/etc/motd # 直接输入即可 |
1 | # lsof |
http://www.cnblogs.com/wormday/archive/2011/05/06/2038703.html
brew cask install android-file-transfer
可以管理小米手机上的文件1 | brew install mackup |
https://channaly.medium.com/how-debug-cordova-based-application-with-chrome-dev-tool-43e095a735b4
Cordova/Inoic/Phonegap
等hybrid项目Settings -> Safari -> Advanced
, 打开JavaScript
和Web Inspector
Preferences -> Advanced -> Show Develop menu in menu bar
Safari->Develop->iPhone
选择你的设备即可电脑左上角apple logo -> System Preferences -> Sharing -> Bluetooth Sharing
打开并设置读写权限File -> New Movie Recording
,在中间录像按钮边上点开的下拉菜单选择你的设备不要期待brew,最好直接从官网下载对应版本的dmg文件安装,都可以直接安装的,如果要使用多个不同的版本,也可以使用pyenv:
1 | brew install pyenv |
降级最简单的是直接从timemachine恢复,但是大多数情况下我们并没有timemachine,那么就需要U盘了
首先需要从苹果官方下载老版本的系统
然后执行以下命令制作系统盘,执行完成后会提示Done.
,如果多次尝试仍然报错,那么可能需要换个更大的U盘,或者换一个U盘。(我最开始用的16G的USB2.0,后来换成32G的USB3.0就可以了)
1 | # 其中Catalina.app是下载的系统的名称,MyVolume是U盘名称 |
开机时按住Option键,然后选择U盘进入,然后用磁盘工具格式化磁盘,重装系统即可
磁盘空间爆了,重启后spotlight一直显示正在索引: 原因可能是误删了索引的文件(索引文件确实有哦几个G),修复需要执行以下几个命令:
1 | sudo mdutil -i off / |
Library not loaded: /usr/local/opt/readline/lib/libreadline.6.2.dylib Referenced from: /usr/local/bin/gawk Reason: image not found: 执行下面这个命令更新所有brew
安装的包可以修复
1 | brew upgrade |
明明安装了xcode命令行工具却还是提示找不到,可以用这个命令重装一下:
1 | xcode-select --print-path # 一般会打印/Library/Developer/CommandLineTools |
Macos使用ssh登陆linux服务器无法显示中文,需要设置终端的字符集:
1 | # vim ~/.zshrc,在底部输入如下内容,然后保存重启终端 |
autoreconf: command not found: brew install autoconf && brew install automake
**xcrun: error: invalid active developer path, missing xcrun **: 重装xcode工具: xcode-select --install
macOS Big Sur无法使用VPN: 系统限制没办法,得修改VPN服务器的配置,参考苹果的官方说明
telnet: command not found: brew install telnet
Safari不能审查元素,没有审查元素按钮:得手动打开开发者菜单: Preferences -> Advanced -> Show Develop menu in menu bar
新版本是Show features for web developer
MacOs安装指定的java版本: https://www.azul.com/downloads/?package=jdk
zsh: bad CPU type in executable: 执行softwareupdate --install-rosetta
即使我写了这样的文章,我依然坚信设计模式只是某些垃圾语言用来规避其语言本身弊端的规范而已,不然,就只能写出糟糕的代码了。
工厂模式是一种类,提供了创建对象的某些方法,可以直接使用工厂类创建对象,而不是直接使用new。
优点:如果要改变所创建对象的类型,只需要修改该工厂即可。比如有个类需要读取用户数据来创建,原本是读取的数据库,现在要从文本读取,就得把那个类及其所有依赖都更改一遍。
php示例
1 | <?php |
抽象工厂模式提供了一种方式,可以将一组具有同一主题的单独的工厂封装起来。客户端程序不需要知道(或关心)它从这些内部的工厂方法中获得对象的具体类型,因为客户端程序仅使用这些对象的通用接口。抽象工厂模式将一组对象的实现细节与他们的一般使用分离开来。
php示例,代码来自维基百科
1 | <?php |
通过复制一个已经存在的实例来返回新的实例,而不是新建实例。
优点: 多用于创建复杂的或者耗时的实例,这种情况,复制一个已经存在的实例使程序运行更高效,活着创建值相等,只是命名不一样的同类数据。
php示例,代码来自IBM
1 | <?php |
介绍: 把一个类的接口变换成客户端所期待的另一种接口,Adapter模式使原本因接口不匹配或不兼容而无法在一起工作的两个类能够在一起工作。外部请求方式一样,内部实现方式不一样。
应用场景:
php实例,代码来自真实的归宿
1 | <?php |
把事务对象和其具体行为、具体特征分离开来,使它们可以各自独立的变化。
python实例,代码来自维基百科
1 | """圆形、三角形归于抽象的形状,而画圆、画三角形归于抽象的画图""" |
优点: 被观察对象不用具体了解观察者具体实现,而是由观察者去实现。被观察者忽略了依赖它的对象,它要关注在事件发生时触发该事件并发送消息给观察者即可。这个和依赖注入有点相近的地方。
php示例,代码来自IBM
1 | <?php |
包含了一些命令对象和一系列的处理对象,每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。
php示例,代码来自维基百科
1 | <?php |
指对象有某种行为,但是在不同的场景中,该行为有不同的实现算法。
抽象工厂与策略模式不同的地方在于,工厂是创建型模式,关注的是对象的创建,而策略是行为型模式,关注的是对行为的封装,对于工厂来说无论返回的对象内部是怎样的,只要是我想要的对象就行,而对于策略模式来说返回的对象即使不同也会有相同的方法/行为。
php示例,代码来自维基百科
1 | <?php |
工厂模式和策略模式的区别
如大多数网上的解释一样,工厂模式更注重对象的创建,策略模式更注重行为的不同。根据我的理解工厂模式更适用于创建不同的对象,这些对象拥有不同的方法。而策略模式则更多是针对有相同方法的对象。例如网上的文章经常举的例子一样,数据库的操作,其实每个数据库的操作都不一样,所以这里更适用于工厂模式。但是如果有后端一样的场景,那么策略模式就更方便了。例如
1 | # 工厂模式 |
1 | curl --silent --location https://rpm.nodesource.com/setup_6.x | bash - |
通过npm install -g hexo-generator-feed
安装
然后修改hexo/_config.yml
添加如下代码:
1 | plugins: |
通过npm install -g hexo-generator-sitemap
安装
然后修改hexo/_config.yml
添加如下代码:
1 | plugins: |
文章的抬头可以指定如下信息:
1 | title: "文章标题" |
nginx 403:可能是因为没有在public生成index.html
文件导致,检查是否执行过npm install
安装完所有的模块,以及themes
目录下是否存在该主题的文件
no content页面只有框架没有内容: 可能是历史的css文件遗留所致,此时可以清空public目录下的文件再重新生成
unknown block tag: load,出现类似的错误,极大可能是文章中出现了特定的没有转义的标签,例如这里就是很有可能就存在一个类似这种标签,这种标签只能使用代码块(三个单引号)而不能使用两个单引号来引用:
1 | {% load # 这样子开头 |
Error: Cannot find module ‘babel-runtime/regenerator’: 原因是没有安装这个包,可以执行npm install babel-runtime --save
每次看到selenium都觉得很牛,但是苦于文档(包括英文)太少,我到今天才真正完整地安装使用了一把。我不喜欢来一个项目就在自己电脑上搭一个运行环境,而是喜欢在docker或者虚拟机里进行操作,问题是docker或者虚拟机里并没有任何的可视化的浏览器,而Selenium又依赖于这些浏览器驱动,我是最讨厌安装驱动的,因为驱动这个东西电脑不同差距特别大,总是会出现各种问题。而在服务器上如何安装selenium或者splinter,这个过程在网上基本是找不到的,所以这里记录下自己的安装方法。
注:这里之所以要使用splinter
,而不只使用selenium
是因为splinter
在selenium
之上又封装了一层,使得接口更为简单。另外,即使是用的splinter
,也可以通过browser.driver
直接获取到selenium
的驱动的。
首先,需要安装必要的python包pip3 install splinter selenium xvfbwrapper
需要注意的是,splinter只有在使用浏览器的时候才需要安装selenium,如果仅仅是在flask或者django中进行测试是不需要的。
ChromeDriver首页-WebDriver for Chrome,下载对应操作系统的最新的chromedriver
1 | wget http://chromedriver.storage.googleapis.com/2.23/chromedriver_linux64.zip |
https://github.com/SeleniumHQ/docker-selenium
https://zhuanlan.zhihu.com/p/26810049?utm_medium=social&utm_source=qq
上面的方式是直接打开浏览器的方式,但是在Server上面没有界面,也就没有浏览器,这种情况就得安装单独的真对server的浏览器了。最先我想使用ChromeDriver,但是无论怎么折腾也安装不上,于是就用了Firefox,发现一篇很好的教程。它这个版本被称作Selenium headless firefox
。安装步骤如下:
1 | # 添加repository,并安装firefox |
1 | from splinter import Browser |
如果直接在本地有桌面环境的情况下进行测试那么,直接这样子:
1 | from splinter import Browser |
通过chrome_options.add_argument('')
可以设置非常多的浏览器的参数
1 | disable-infobars // 禁用网页上部的提示栏,比如2.28的webdriver开始会提示你Chrome正受到自动测试软件的控制,这个特性应该是chrome为了安全给加的 |
很多时候访问一个页面,在该页面可能会同时访问其他的资源,例如js,css,甚至其他一些关键信息。这时候就要求我们能够获取中间的所有的请求,但是selenium
是不带这个功能的,只能使用一些代理,例如:browsermob-proxy。其不需要安装,只需要release
下载bin
包,然后pip install browsermob-proxy
,然后在使用的时候指定路径即可。例如:
1 | from browsermobproxy import Server |
1 | browser.windows # 所有打开了的窗口 |
1 | browser.visit(url) # 访问URL |
1 | browser.title # 获取网页title |
Chrome driver crashes when opens a new tab
原因可能是服务器内存太低了,需要加大虚拟内存
selenium.common.exceptions.WebDriverException: Message: session not created exception,将webdriver更新到最新版基本上能解决问题
session not created: This version of ChromeDriver only supports Chrome version 77: 这是因为下载的chromedriver
版本和你当前系统已经安装的chrome
版本不一致造成的,需要对其中某一个进行升级或降级
Unable to connect host: 如果浏览器能正常访问,那么可能是chromedriver
打开网页有问题,导致splinter
未去访问目标网页,并不是网络连接问题
browsermob-proxy 地址已在使用中: 可以指定代理服务器的端口:
1 | from browsermobproxy import Server |
Getting Started with Headless Chrome
Setting up a Digital Ocean server for Selenium, Chrome, and Python
PHP的单元测试工具比Python好选择,基本上就是PHPUnit了。
1 | $this->setExpectedException(Exception class); // 断言将会出现某个exception |
Mockery默认包含在PHPUnit中,根据我的经验,学会了mock才算真正学会了单元测试,无论是Python还是PHP都是如此。使用它也解决了我之前以为依赖注入对测试来说极大地加大了复杂度的错误思想。使用方法如下:
1 | # 首先在一个测试类中 |
既然用上了单元测试,那为何不继续深入使用,添加代码覆盖率的测试,以前我认为覆盖率只是用来给我们玩玩儿的,当我真正开始写了后才知道,真的太有用了。首先,他让我了解到一个真相,那就是我自己写的代码,很多地方,我自己都不清楚,有些异常我根本不知道在什么情况下会抛,有好多重复的地方代码根本不应该存在,有好多逻辑没考虑完整。要查看代码覆盖率还需要安装Xdebug。
安装方式:
1 | sudo apt-get install php-pear php5-dev libcurl3-openssl-dev -y |
使用:
phpunit testfile.php --coverage-html foldername
即可生成html文件
Call to a member funciton make() on null
错误
只需要添加父类构造方法即可:
1 | function __construct(){ |
前几天,一位朋友突然问我道,为什么我的博客最近更新的都是有关读后感的,而少了以前那么多的技术干货。很欣慰,居然有朋友一直在看我的站点,虽然并没有看到它想看的,但是他所要的技术干货我现在其实也有,只是并没有放在主站,而是放在豪翔天下的wiki这个二级域名下。至于为什么,我想是因为厌倦了很多博客的内容样式,厌倦了很多教程的复制粘贴,才有了把它们一分为二的想法。
看了一下百度统计和谷歌Analytics,博客从13年至今,统计在内的PV总共有20000+,单日最高PV居然有1300+,但是平日里也就10左右。虽然我有用统计工具,但是几乎不会去关注这些统计结果,一直以来我写博客都是为了自己在写,即使是技术干货,也是为了方便自己以后查阅。但是最近又有了一些新的方法,我认为,我现在可以宣传自己了。以前的我很自卑,觉得自己写的东西,那肯定都是别人写过的,而且自己写的东西更是没有多少技术深度的东西,但是事实好像并不是这样。我最骄傲的地方就是与身边的不一样,我每天都在浏览着很多科技博客、技术新闻,以为自己一直是站在技术前沿的人,也意识到自己学的东西范围非常广,自己的深度也不够,眼前有很多路,却不知道该往哪里走。这就像一个瓶子,明明上面就是瓶颈了,但就是怎么做也钻不出去,甚至在往下掉。
到新公司也快半年了,新同事们很好,也很牛逼,感觉大家简直就像游侠一样,不想去北上广争夺,只想待在重庆这个小江湖,说实话,他们的技能水平,在北上广绝对也能称得上高手。这半年里,因为有人在旁边指导,在代码质量方面成长得挺快,至少现在写代码的时候能明显感觉到自己以前写的代码是有多么的bad smell。在给别人开发的时候也感觉自己的代码量真的太少了。以前都是自己开发自己的东西,觉得有时候有点错误没什么,但是给别人开发的过程中,一点儿不满意人家就会指出来,而且让人伤心的是,这里面起码有百分之五十确实是自己代码质量的问题。路漫漫其修远兮,编程之路,我还有很长的路要走。
前几天翻了以前的博客,我去,居然没有今年的年初计划,不禁思考,我这半年是怎么浑浑噩噩过来的,接下来的半年又该怎样浑浑噩噩地过呢。浑浑噩噩这个词我不喜欢,你说我懒我会接受,我懒得开心,但是说我浑浑噩噩,说我不思进取,那肯定是不行的。在这里,补一下下半年的目标: