win11兼容检查工具,检查工具下载地址
https://gitee.com/shanliren/WhyNotWin11/attach_files/759964/download/WhyNotWin11.exe
运行截图
新的windwos11为了安全要求硬件必须支持TPM,需要在主板的BIOS/UEFI配置中打开对应配置
win11兼容检查工具,检查工具下载地址
https://gitee.com/shanliren/WhyNotWin11/attach_files/759964/download/WhyNotWin11.exe
运行截图
新的windwos11为了安全要求硬件必须支持TPM,需要在主板的BIOS/UEFI配置中打开对应配置
当我们使用date命令来修改系统时间的时候,发现命令返回修改成功
date -s '2021-03-04 11:21:13'
通过上面的命令执行,可以看到返回修改成功后的系统时间
Thu Mar 4 11:21:13 CST 2021
当我们再次运行date命令来查看系统时间的时候发现时间还是修改之前的,原因是现代的linux操作系统都会默认运行NTP这个时间同步服务,如果需要手动修改系统时间,那么需要先关闭同步功能,通过命令 timedatectl可以看到系统的时间情况
root@nbear:/opt/c# timedatectl Local time: Thu 2021-03-04 09:45:24 CST Universal time: Thu 2021-03-04 01:45:24 UTC RTC time: Thu 2021-03-04 01:45:24 Time zone: Asia/Shanghai (CST, +0800) Network time on: yes NTP synchronized: no RTC in local TZ: no
可以看到默认 Network time on 这个选项是 yes的,所以我们需要先通过下面的命令关闭这个同步特性。
timedatectl set-ntp false Local time: Thu 2021-03-04 09:57:05 CST Universal time: Thu 2021-03-04 01:57:05 UTC RTC time: Thu 2021-03-04 01:57:05 Time zone: Asia/Shanghai (CST, +0800) Network time on: no NTP synchronized: yes RTC in local TZ: no
这样你就可以自己修改时间了,如果要恢复只需要运行
timedatectl set-ntp true
使用MySQL数据库的时候通常会听说有日志系统,最常听说的就是redolog和binlog,那么两种日志有什么不同呢。
首先,两个的层次不一样,学过MySQL的小伙伴都知道MySQL逻辑分层中有着 服务层(Server)和数据库引擎层,其中binlog就是服务层的功能,而redolog则是innodb这个数据库引擎自己的功能,逻辑结构如图所示:
其次在存储方面,虽然两者都是采用写入磁盘的数据结构,但是两个的方式并不一样。其中redolog采用一个类似环形存储区的方式进行存储,也就是有大小限制(my.cnf 中 innodb_log_file_size 用来设定redolog大小),一旦写到尾部则从新覆盖头部继续写;binlog则采用rotate分片的形式记录日志,可以不断追加,如果不特意配置则没有大小限制。
The redo log is a disk-based data structure used during crash recovery to correct data written by incomplete transactions.
Redolog有两个关键的位置点(position),其中write position就是从这个地方继续追加新的日志,而check point 则是将redo日志写回磁盘到达的位置,从write position到 checkpoint两者之间的空间就是剩余的可追加日志空间。如果write position已经追上了checkpoint,那就要求数据库引擎innodb赶紧将内容写回磁盘后才能记录写redolog。
再者存储格式和方法也不尽相同,binlog虽然叫做bin(以二进制形式存储),但是实际存储的是逻辑操作过程,也就是比如 insert into user id,username values(3,'root') 这种形式,而redolog更像是磁盘上发生的变化。
功能类似但不完全相同,binlog用于记录整个历史记录,而redolog虽然也能用于恢复,但是设计之初的主要目的是缓存未提交的修改,比如我们update user set gender='famele' where username like '%lucy' ,可能需要扫描很多行并且有很多行需要修改,如果直接去修改磁盘可能有性能问题,不如先把要操作的记录写到redolog之中,然后就可以返回已经更新完成,然后在空闲时间慢慢写回磁盘即可。
redolog的工作路程
redolog的基本工作流程,先写入redolog,写入完成返回给客户端写入成功,然后redolog再写回到磁盘,因此再写完redolog系统崩溃或者mysql进程崩溃,数据是不会丢失是可以恢复的。
两阶段提交
更新数据写入redolog,这时候的这个日志是 prepare状态,然后执行器生成binlog日志,并把binlog写入磁盘完成后,执行器调用数据库存储引擎的事务提交接口,引擎现在从prepare状态改成commit状态,于是更新完成。
两阶段提交可以保证binlog和redolog的一致性,避免恢复的时候使用不同日志导致数据不一致的问题。
GIT的配置体系和CSS类似,有着三级结构,分别是
系统级(system)、用户级(global)、当前项目级(local)
三个等级的配置文件越靠近当前项目则优先级越高,因此当前项目下的配置文件是最高的配置优先级
GIT Config工具的使用
高手们可以直接修改配置文件完成配置,但是GIT本身提供了配置命令行,可以使用git config 进行配置
git config usage: git config [<options>] Config file location --global use global config file --system use system config file --local use repository config file -f, --file <file> use given config file --blob <blob-id> read config from given blob object Action --get get value: name [value-regex] --get-all get all values: key [value-regex] --get-regexp get values for regexp: name-regex [value-regex] --get-urlmatch get value specific for the URL: section[.var] URL --replace-all replace all matching variables: name value [value_regex] --add add a new variable: name value --unset remove a variable: name [value-regex] --unset-all remove all matches: name [value-regex] --rename-section rename section: old-name new-name --remove-section remove a section: name -l, --list list all -e, --edit open an editor --get-color find the color configured: slot [default] --get-colorbool find the color setting: slot [stdout-is-tty] Type --bool value is "true" or "false" --int value is decimal number --bool-or-int value is --bool or --int --path value is a path (file or directory name) --expiry-date value is an expiry date Other -z, --null terminate values with NUL byte --name-only show variable names only --includes respect include directives on lookup --show-origin show origin of config (file, standard input, blob, command line)
GIT config的实战操作
列出GIT的配置项目
#这个命令会列出所有的配置项,包含 系统级/用户级/当前项目级 #不同级别下有相同的配置则分别都会列出来 git config --list
#只列出系统级的配置 git config --system --list #只列出用户级的配置 git config --global --list #只列出当前项目级的配置 git config --local --list
获取精准的配置项目
#获取当前git提交时用的用户名 #不加--system 这些范围作用域的话,如果各个作用域都有相同配置,则以 当前项目级>用户级>系统级 顺序获取 git config --get user.name
#调用编辑器进行修改配置
git config --global --edit
m = Map("ctw", translate("微信连WiFi")) s = m:section(TypedSection, "wifi") s.addremove = false --可以添加模块,如果true,每次点击添加都会添加完整的一个模块的内容让用户输入 s.anonymous = true
首先是把整个UCI文件 Map 一下,然后再通过 section 方法进行对应,匿名节点爱用 TypedSection 非匿名节点采用 NamedSection
m:section(TypedSection,"wifi","描述信息") m:section(NamedSection,"wifi","system","描述信息")
然后就是各个模块的映射:
Value 是普通的输入框
TextValue 则是类是textarea的文本输入框体
DummyValue 则是直接显示不可输入的内容框
Flag 则是复选框的,如果是勾选则为1 true 这样的关键词,没有勾选则为 0 false 这样的关键词
ListValue 则是下拉式列表框,这个框体里面可以提供下拉列表值
MultiValue 则是复选框,多个都被选中的时候会出现 option appid "1 2 3" 这样的内容
StaticList 则是复选框,但是会采用 list 方式存储而不是option 方式存储
FileUpload 文件上传,至少能记录文件名称
FileSelect 文件选择,选择设备系统中的文件名称
appid = s:option(Value, "appid", translate("<font color='red'>应用ID(appid)</font>"),"每个微信公共号唯一的appid标示") appid:value("智能预选择值内容") appid:depends("mode","fit"); #depends(key,value) 只有当对应的key=value的时候才能显示这个选项
appsecret = s:option(TextValue, "appsecret", translate("应用Secret(appsecret)"),"非必填项目,填写后可以用于高级功能")
enable = s:option(Flag, "enable", translate("Enable)) enable.rmempty = false -- 值为空时不删除 o = s:option(ListValue, "enable", translate("Enable")) o:value("1", translate("Enable")) o:value("0", translate("Disable")) o = s:option(MultiValue, "enable", translate("Enable")) o:value("1", translate("1")) o:value("2", translate("2"))
关于依赖值的设计,支持多个依赖
auth:depends({mode="sta", eap_type="peap", encryption="wpa2"})
显示密码形式
acct_secret.password = true encr.override_values = true encr.override_depends = true
非常有用的写法,可以方便快捷的通过开关设置字符串类型的值
encryption = s:option(Flag,"encryption",translate("是否启用加密")) encryption.default = encryption.enabled encryption.enabled = "wpa-psk2" encryption.disabled = "none"
button = modex:option(Button, "modename", "桥接模式") button.inputtitle = translate("切换") button.inputstyle = "apply" function button.write(self, section, value) AbstractValue.write(self, section, value) luci.sys.call("uci set network.lan.ifname=\"eth0.1 eth0.2\" ;uci del network.wan") --self.inputtitle = translate("路由模式") return end
——————————————————————————–
刷新按钮
hup = s2:option(Button, "_refresh", translate("Refresh")) hup.inputstyle = "reload" function hup.write(self, section) luci.http.redirect(luci.dispatcher.build_url("admin/network/network","")) end
——————————————————————————–
实现表单的效果
f = SimpleForm("sac_list", translate("AP列表"), translate("当设备工作在AC模式时显示AP状态列表")) f.reset = false f.submit = false
t = f:section(Table, {{client_ip="192.168.10.16",client_status="Online"},{client_ip="192.168.16.2",client_status="offline"}}) t:option(DummyValue, "client_ip", translate("IP地址")) t:option(DummyValue, "client_status", translate("设备状态"))
term = t:option(Button, "delete", translate("删除")) term.inputstyle = "remove" function term.write(self, section) null, self.tag_error[section] = os.execute("rm /tmp/ac/*") end
tftp是基于udp的快速文件传输协议,可以用于固件刷机以及文件交换来使用,ubuntu server部署ftp最佳实践操作
TFTP Server服务器端安装
1、安装 tftpd-hpa
sudo apt-get install tftpd-hpa
2、配置tftp服务的根目录,配置文件位于
# /etc/default/tftpd-hpa
内容修改为:
TFTP_USERNAME="tftp" TFTP_DIRECTORY="/opt" #需要777权限 TFTP_ADDRESS="0.0.0.0:69" TFTP_OPTIONS="--secure -c -s"
3、然后重启服务生效,服务会自动开机启动,需要用的时候直接将文件放置到 /opt 下面即可
service tftpd-hpa restart
4、这样即使下次整个系统重启tftp服务也会自动跟随启动
TFTP客户端的使用
windows客户端使用
tftp 192.168.10.254 put ipp.txt tftp 192.168.10.254 get ipp.txt #最后还可以添加一个重命名为文件名的参数
Linux客户端使用
tftp 192.168.10.254 -c put.ipp.txt tftp 912.168.10.254 -c get ipp.txt #最后还可以添加一个重命名为文件名的参数
Docker带来的主要方便是:程序自带运行运行环境,不用担心操作系统库的差异,也不需要提前安装运行所需的环境,比如我们部署好了MySQL之后,由不希望安装web服务器和php运行环境,直接使用Docker镜像完成一键部署。
首先我们找到phpmyadmin的镜像:
docker search phpmyadmin
我们选择第一个镜像,然后运行
docker run -d -e PMA_HOST=rds.ali.com -p 13306:80 phpmyadmin/phpmyadmin
解释一下参数:
-e PMA_HOST 指定要连接的MySQL 数据库HOST
-p 端口映射,将服务器的13306端口映射到容器内的80端口
总结:
使用Docker可以快速部署应用,这里运行一个命令,直接部署了MySQL的WEB管理phpmyadmin。
为什么不单独使用access_token还要搞出一个fresh_token?结论是:为了更加安全,具体情况我们娓娓道来。
OAuth2.0协议是通用的授权协议,用于第三方应用向用户获取授权并访问用户资源的场景,最美妙的是,用户并不需要将自己的凭据(比如账号密码)直接交给第三方应用。
OAuth 定义了四个角色,他们分别是:
①资源所有者(Resource Owner)
能够授权访问它的受保护资源的实体,如果他是一个人的时候,我们称他为 终端用户(end-user)
②资源服务器(Resource Server)
托管受保护资源的服务器,能够通过判定access_token是否有效来决定是否允许访问特定的资源。
③客户端(Client)第三方应用
客户端因为拿到了用户(资源所有者)的授权,所以可以代表用户访问他的资源。客户端并不指特定的实现(应用可以是服务器、桌面应用、或者设备)
④授权服务器(Authorization server)
用户发起请求,授权服务器验证用户的凭据后并根据用户的授权签发access_token(访问令牌)给客户端。
上面的图描述了四个角色之间的交互流程
(A)客户端向资源所有者请求授权,授权请求可以直接向资源所有者发起,也可以通过授权服务器作为中介间接进行(这种方式更好)。
(B)客户端拿到用户的授权许可,得到一个凭据,这个凭据就表示资源所有者同意授权了,可以使用本文档中定义的四种授权方法进行(当然也可以使用自定义扩展的方法)。授权方法主要由客户端决定,当然授权服务器支不支持也会受到影响。
(C)客户端拿着上一步获取到的凭据标示向授权服务器申请access_token(访问令牌)
(D)授权服务器检查凭据是否有效,如果有效则签发access_token给客户端。
(E)客户端通过携带access_token向资源服务器发起访问受保护资源的请求。
(F)资源服务器检查access_token的有效性,如果有效则提供对应资源。
客户端获取 凭据 最好的方法是通过授权服务器作为中介进行(步骤A和B中)
一句话表达式:第三方应用向用户要授权,将用户跳转到用户资源所在的授权服务器上,用户在授权服务器同意授权后授权服务器交给第三方应用一个ticket,然后第三方应用使用这个ticket获取真正能够访问用户资源的access_token.
这个流程中授权服务器一般会同时给第三方应用一个短期有效的access_token和一个长期有效的refresh_token,当access_token失效时可以使用refresh token获取新的access_token。这个流程是不是看上去脱裤子放屁,多此一举,其实并不是,通过下面这张图就能明白设计的原因:
refresh_token 只和授权服务器进行交互,而access_token则只和资源服务器通信。
看懂了吧,这就是最初设计的本质,按照设计,第三方应用的服务器保存refresh_token并通过refresh_token和授权服务器通信拿到access_token再交给自己的应用,这样安全性大大提升,服务器之间通信频率极低,且更难窃听。
下面翻译下RFC6749中对access_token和Refresh_token的描述
access_token
access_token是客户端透明的,这串字符串中包含了允许的授权范围以及有效时间等,资源服务器和授权服务器都知道这个access_token的情况
token自身也许包含一些额外的信息,比如token表示用户等(比如JWT形式)
token提供了一个抽象层,通过单一的token字符串的形式而不是比如用户名密码形式,资源服务器明白这个token的含义。
Refresh_token
刷新token的作用是为了获取access_token,refresh_token会比access_token时间更长,设计目的仅仅是 客户端和授权服务器之间通信用的,而access_token则是客户端和资源服务器之间通信用的。
JWT(JSON Web Token)官方说法是:工业化标准化(RFC7519)的两个实体中间信息交互的方法。具有无状态、去中心化、甚至体积小等各种优点,但是根据我的实际考察,发现以上说法存在很多问题,一般项目不要再使用JWT作为鉴权方式,那么我们先说下结论吧,首先是适用场景:
JWT使用场景非常有限,目前有两种类型比较适合采用JWT进行鉴权,分别是:
1、一次性验证,比如邮箱验证,可以在payload中包含iss签发者和过期exp信息(几乎是中小型项目的唯一适用场景)
2、Restful API的无状态认证,但是除非你的规模和淘宝一样大,否则完全比不上session的解决方案更有优势。
1、JWT结构紧凑,采用改进的BASE64编码进行,传输数据量小
这个是相对来说的,比起传统的Session方案,客户端只需要传递一个sessionid的情况,JWT是不是显得数据冗余。
2、JWT因为将鉴权信息携带在HTTP的头部,具有良好的跨域能力
这个观点前提是有问题的。首先看下跨域的概念,它很简单,只要 协议/主机地址/端口号 三个不完全一致,就会出现跨域问题,那么JWT真的可以解决这个问题么。问题的根本在于,请求携带token,token需要存储在用户本地,那么浏览器能够存储的地方只有cookie和localstorage,而两者都有不允许跨域访问(如果特意配置子域名是能够访问父域名的存储),因此有着源头问题,JWT并没有从根本上解决这个问题。如果是APP客户端,那就不存在跨域一说,毕竟所谓的跨域是浏览器行为,并不是请求没有发送到服务器,或者服务器没有响应,而是响应的数据浏览器收到了但是拒绝提供给跨域发起的浏览器网页应用。
3、JWT该协议更加安全
说起来你可能不信,使用JWT更加不安全,具体的问题有:
1)JWT内容虽然不可篡改,但是内容确实明文可读的,BASE64仅仅是编码而不是加密,因此JWT也不能在payload内携带敏感信息。
2)如果对payload进行非对称加密,那就会有额外的性能开销,比起现代的WEB框架都会使用加密的cookie的session方案并没有任何优势, 比如PHP的Laravel框架。
3)JWT的token无法在服务器进行撤销,意味着令牌一旦发出去就无法回收它的权限。如果你打算在服务器记录JWT的token令牌,我建议你还是不要尝试,因为会JWT从无状态变为有状态,违反本意。既然把token作为session来用,何不一开始就是用session。
4)具有抵抗CSRF的天生能力
我们先来看下描述:因为JWT的token添加到HTTP的头部或者query_string中传递,因此尝试跨站伪造请求攻击,因为无法携带真实token,所以攻击无效。
听起来视乎挺有道理,但是总感觉哪里不对,如果你也有这个感觉那就对了。和上面一样,需要解决源头问题,为了安全,JWT建议token存储在 localstorage当中,但是任何的脚本注入均可读取到localstorage的信息,这样并不安全。
那么你是否想到如果我们把信息存储在cookie当中,然后采用httponly的方式或者https的方式进行传输呢,是的这样的确很安全,那么问题也来了,为什么我们不一开始使用session+cookie的组合呢。
5)防重放攻击能力很差,至少和session方案一样差,或者说更差。
也许你会反驳,不是在JWT协议里面有一个 jti (JWTID)的字段么,我们每次都换个ID,那么不就无法重放了么。每次,这样的确可以防止重放攻击,那么问题来了,服务器如何检查这个jti是否正确呢,没错,去查询自己存储的jti,硬生生的成为有状态的协议。
4、无状态的水平扩展能力超强,人家超喜欢
因为JWT无状态,它的token在任何服务器上只要知道加密的secret都能解密,我的回应是:
技术层面的确是「好处」,但前提是你使用的是无状态 JWT Tokens。然而事实上,几乎没人需要这种横向扩展能力。有很多更简单的拓展方式,除非你在运维像淘宝这样体量的系统,否则根本不需要无状态的会话(Stateless sessions),利用老外的一句话:
You’re Going to Hit the Database Anyway
授权过程无论如何都需要访问数据库(不一定是数据库,必然是后端存储)查询用户的权限集合,你不可能将权限全部携带在payload当中。因此即使JWT的token解决了你是谁的问题,仍然需要去确认你能干什么,这正是Authentication和Authorization的区别。
5、单点SSO登录
在session携带额外的信息岂不更简单,JWT并没有优势,CAS系统都能很好地实现SSO单点登录,关于CAS系统的备份我们在其他文章中讨论。
相信这时候你已经开始骂我了,优点都是问题,那缺点还能玩么?
首先)不支持续签,因为任何内容改变都会出现token改变的问题,因此只能重新给用户下发新的token,传统的session方案只要服务器延迟session的有效期即可。
其次)无法在服务器端注销,这样被截获的token可以一直被使用直到他过期,如果后端专门准备一个token黑名单,那JWT又成为有状态的协议,为何不一开始使用session方案。
再者)JWT没有并发控制的能力,因为无状态属性,你不能将用户下线,一个token可以同时被多个客户端同时使用。
还有)….
说了这么多,JWT也不是没有好处的,比如数据是存储在客户端的,不用担心服务器挂掉而导致用户状态丢失,但是这个好处视乎也没有什么卵用。
JWT的适用场景非常有限,几乎只适用一次性邮件验证这样的场景,对于我们的中小型项目,如果必要,请不要再使用。
有什么不同意见和建议请在下面留言评论吧,匿名评论已开。