服务端鉴权最佳实践

上文中分析了常见的 API Auth 鉴权方案,本文再单独分析下服务端鉴权常用的一些实践。

Server Side 指的是 API 的使用方是一个服务器,而不是面向用户(浏览器、客户端 APP)


▎常见的方案中,都有哪些呢?

  • KeySecert (Basic Auth)
  • JWT

这几种方案差别上在:

  • Token 的生成
  • Token 的表达能力
  • 签名方式

下面几种方案的使用,进行详细的描述。


> KeySecert

一般在开放 API 的服务端那里申请访问,都会给一组下面的值:

UserID opsman@lanvige.com
AccessKey ID LTAISERp
AccessKeySecret xCkspAakY

有时也只会给 Secret,关于这一些,下面会单独讲。

在使用上,有以下三组常见情况,按 Token 传递方式,是否签名来分

Token 位置 明文 签名
HTTP Auth Header
HTTP Query Query
签名形式 Query

- 使用 HTTP Basic Auth

在使用上,可以有两种方式,其中一种就是,使用 HTTP Authorization 标准,

可以通过标准的 HTTP Basic Auth Scheme 来传递,把 key/secret 当作用户名密码来使用:

1
Authorization Basic b64(key:secrect)

- 签名形式 HTTP Digest Auth

还有另一种用法,不直接传递 Secret,传递会导致泄露,而是通过签名的形式,对每次的请求,按一定的规则进行签名。

签名的好处是,Secret 不会被泄露,而通过签名传递的数据,可以防止篡改,提升安全的同时,也让参数变的更有用,之前一些必要非敏感不能通过参数传递,只能服务端多几次查询的情况,现在可以通过签名的形式提供,更加安全。

如何对它进行签名呢?

Digest

参加 Digest 的方式,加上一些参数,然后再进行签名。

阿里云的实践:

把所有的参数排序,然后使用 Secret 对其进行签名

1
2
3
4
5
6
7
8
9
10
https://vpc.aliyuncs.com/?Action=DescribeVpcs
&Format=xml
&Version=2014-05-15
&Signature=xxxx%xxxx%3D
&SignatureMethod=HMAC-SHA1
&SignatureNonce=15215528852396
&SignatureVersion=1.0
&AccessKeyId=key-test
&Timestamp=2012-06-01T12:00:00Z


> JWT

再看下 JWT 的形式,其实上面 KeySecret 中的 签名方式,已经做完了所有的事情,但会发现,参数太凌乱了。如果把一些公用的参数收集在一起,然后放在一起进行传递。进行标准化,会更省心,好用。

这就是 JWT 要做的。它定义了一种结构,用 JSON 序列化,放在 HTTP Header 的 Authorization 中。通过 Payload 来表示要传递的数据部分。

JWT 是自签名的,可以安全的传递数据
签名方式上,有多种选择,像 HAMC-SHA,RSA-SHA,EcDSA-SHA 等对称和非对称签名。

- 请求格式

1
Authorization Bearer xxxxx
Payload
1
2
3
4
5
6
7
8
9
{
"iss": iss,
"iat": time.Now().UTC().Unix(),
"exp": expire.Unix(),
"jti": uuid.NewV4().String(),
"sid": access_key,
"sig": query_signature,
"sal": "sha1/sha256",
}
  • iss: 标明客户端身份
  • iat/exp: 时间属性
  • jti: Nonce 用法,防重放
  • sid: SessionID,用于表明本次的 Key
    技术上可取代 iss,但 JWT 的优势就是多放一些数据,少一些查询。
  • sig: 对整个请求进行的签名
  • sal: 签名算法,MD5/SHA1/SHA256

- 对称签名

对称签名是指 Key/Secret 的方式,密钥只是一个随机字符串,服务端和客户端都保存同样的值。

每次请求,通过签名进行请求。

算法是唯一的: HMAC-SHA

- 非对称签名

非对称目前有很多主流算法,像 RSA、EcDSA,Ed 等,主流 JWT 库也都实现了相应的算法,可以在 JWT Header 中指定。

客户端保留私钥,服务端保留公钥。

每次请求,客户端用私钥进行签名,服务端用公钥进行验签。

非对称签名的性能消耗要大一些。但安全性也会高一些。

> 对比

JWT 有标准库,有格式定义,比较优雅
KeySecret 没有,都是自己实现的

JWT 默认实现自签名,而 KeySecret 没有,可以自定义实现
实现签名后,可以通过 URL/Header 安全的传递一些数据,减少服务端查询,增加性能。

更推荐 JWT 的用法。


▎设计

> 权限

每个 AccessKey,就像创建了一个子用户一样,要对其进权限的管理。

OAuth 2 中对授权范围做了一些定义,Scope,可以参考下。

但在技术层面上,可以当这个 Key 为一个用户,做 RBAC。

> 是否需要多个不同的 Key

有的是给个 KeyID,有的则是只给一个 Secret,

有 ID 的好处则是一个用户同时可以拥有多个不同访问账号,有效解决不同账号的过期时限问题,如果一个账号密钥泄露,可以不影响业务的情况下进行更换。

同时,最新的实践中,也会为不同的 Key 分配不同的权限。可以把不同权限的 Key/Secert 用在不同的场景中。

> 表设计

id type scheme user_id scope publick_key secret expried_at created_at
005f21 jwtrs bearer 30075 MFwwDQwEAAQ== 2020-09-20 2020-04-01
005f23 jwtes bearer 30075 MFwwDQwEAAQ== 2020-09-20 2020-04-01
005f27 jwtps bearer 30075 MFwwDQwEAAQ== 2020-09-20 2020-04-01
69274f jwths bearer 30075 69ed732 2020-09-20 2020-04-01
203782 secret token 30075 90ed7a1 2020-09-20 2020-04-01
203782 secret / 30075 90ed7a1 2020-09-20 2020-04-01
203112 sign / 30075 MFwwDQwEAAQ== 2020-09-20 2020-04-01

jwths 表示是对称加密
jwtrs 非对称加密


▎其它

> 时效性问题

一般 token 时效性如何设计,在 JWT 中可以通过 EXP 来进行限制,但这是一种客户端行为。

服务端可以在拿到 JWT 时,验证下 exp - iat <= limit

> 长期 Token

有时为了接入简单,会需要一个长期可以用的 Token,当前的省力办法就是用 JWT 签发一个长期的 Token。

但这样不安全,也不符合上一个时效性问题。

如果不需要签名的话,可以用 Key/Secret 中的 HTTP Auth 方案。


▎REF::

API 鉴权

▎常见的鉴权模型

  • OAuth 2.0
  • ClientSide A <-(token)-> B, A -> B
  • ServerSide A -> B

> OAuth 2.0

这是一种平台上常见的方法,用户授权去拿它的数据。

是一种用户参与的主动行为。

> ClientSide

Client 不一样,Client 都是向服务端申请一个 token 令牌,再由这个令牌去查信息的。
原因主要是 Client 轻量,不适合保存私钥,也不适合计算 token。

Client 用于用户自己使用网页登录的模式。

> ServerSide

Server 方式,用于两个系统间通信,而 Client 的方式,则用于向手机端签发一个 token。

ServerSide 也有两种常用的方式

  • app key、secret 模式
  • 公私钥签名模式

- app key、secret 模式

就像是 basic 验证时的用户名密码,secret 就是 digest 了。

因为服务端吗,不经过复杂的链路。也不需要啥安全的。

- 公私钥签名模式

还有一种就是 A 用私钥进行签名,
B 用公钥进行验签。


▎两种类型

平时,我们一个系统里,会有用户和平台两种角色

用户只能看到自己的数据,而平台可以看到整体的数据。

平台 != Admin

> 用户角色的 API

而用户,根据角色不同,又分为

  • 用户
  • 商家
  • 客服/管理员

其各自有着不同的功能和权限。通常我们会用不同的域来对其做区分:

  • /uv1
  • /mkv1
  • /admv1

▎授权模型

根据用户的角色,对应其可以使用的授权模型:

User Maker Admin APP
ServerSide Y Y Y Y
ClientSide Y Y Y /
OAuth 2.0 Y / / /

▎谈谈 APP 授权

> 使用及场景

似乎不常见哪个平台,对自己平台数据有开放 API 的,这些都是内部关键数据。

都是自己的人员,比如说微服务中的同公司其它服务,也就是说这里不用对权限做太多的限制?

> app 的权限范围

如果不设计权限范围的话,那不就乱来了?

app 关联到同户,不然找不到人

> 归属权

  • 归属到一个用户
  • 一个组织

如果是关联到用户下面,那这和用户的自己的 token,除了权限上的不同,其它都一致了。

> 申请流程

这个应该不能申请,只能后台批。

> auth 定义

app 是关连到用户,还是组织?

表名:tokens / authkeys

字段:

  • user_id
  • scope
  • session_id
  • private_key

▎JWT - ServerSide 公私钥签名模式

1
2
3
4
5
type MyJWTClaims struct {
jwt.StandardClaims
AppID string `json:"appid,omitempty"` // appid: appid
Mode string `json:"mode,omitempty"` // mode: Side
}

通过 mode 字段标识是一个服务端对服务端的 token

> JWT 定义

jti - serverside 用,表示一次性,uuid
iss - serverside 用,表示是谁,用户是谁
sid - serverside 用,给谁

uid - clientside 用,表示谁

app 模式

iss - ????
jti - 一次性
sid - 表示 app 要用的 key

app 是谁呢?要标识吗?


▎REF::

Git 201 – Git LFS in Action

Git Large File Storage (LFS) 是一个由 GitHub 开发的 Git 扩展项目, 用于增强 Git 对大文件追踪的支持.

Git 对于一些二进制,及大文件的处理一直不是很好,二进制文件的对比,差量存储都未能优化处理。

这导致了在 Git 的仓库中,会储存二进制文件的所有历史全量版本。这样一来,会给储存带来很大的困扰,同时在进行 Clone/Fetch/Pull 时都会产生大量的流量,特别是在一些网络不大好的地方。

而 Git LFS 就是为了解决这一问题而产生的工具。它将标记的大文件保存至另外的地方,而在主仓库仅保留其轻量级指针。这样在获取版本时,只更新主库对应的大文件,会更加轻量,高效。

主要的特征如下:

  • 超大文件支持
  • 减小仓库占用空间
  • 更高的远程获取效率
  • 和 Git 操作保持一致

▎安装

1
2
# macOS with HomeBrew
$ brew install git-lfs

为 Git 账号进行初始化设置(只需要一次)

1
$ git lfs install

▎使用

> 配置 LFS

在需要使用 LFS 的项目中,通过 git lfs trace 配置需要加入 LFS 的文件,可以使用通配符。

编辑完之后,会创建或修改项目的 .gitattributes,也可以通过手工修改该文件来进行配置。

1
$ git lfs track "*.bin"

> 将 .gitattributes 加入到仓库中

1
2
3
$ git add .gitattributes

$ git commit -m "track files using Git LFS"

执行 push 时会输出一些 LFS 的指示

1
2
3
4
5
6
7
$ git push

Uploading LFS objects: 100% (1/1), 104 KB | 0 B/s, done.

Enumerating objects: 6, done.
Total 4 (delta 2), reused 1 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.

> 查看文件的状态

1
$ git show HEAD:cat.bin

输出当前文件关于 LFS 的一些值:

1
2
3
4
5
6
7
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
version https://git-lfs.github.com/spec/v1
oid sha256:ce5c0e9713c54368d4a6b9a30c46503bac70c293091f0e60e30aa14f6813cca0
size 103890

▎操作命令

> 基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git lfs track
# 将一个或者一类文件以 git lfs 的方式加入到版本控制中 (实质是修改 .gitattributes 文件)

$ git lfs untrack
# 将一个或者一类文件从仓库中移除

$ git lfs status
# 类似于 git status , 显示 git lfs 方式的文件在 暂存区的状态

$ git lfs lock
# 锁定一个或者一些文件, 只允许当前的用户对这些文件进行修改, 防止在多人协作的场景下冲突

$ git lfs unlock
# 同上, 解锁一个或者一些文件

> 其它操作

显示当前仓库的 env 配置:

1
$ git lfs env

LFS 文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ git lfs ls-files
# 展示使用 git lfs 方式的文件列表

$ git lfs prune
# 删除全部旧的 Git LFS 文件

$ git lfs fetch
$ git lfs pull
# 不通过 git 获取文件

$ git lfs push
# 不通过 git 单独提交文件

$ git lfs checkout
# 取消文件修改,切换版本

正常情况下会随着 git pull/push 一起执行
如果在 git pull/push 的过程中断了, 导致二进制文件没有被拉取的时候, 可以使用这些命令(支持断点续传,速度不慢)

▎常见问题

> Git LFS 迁移 - 已提交 git 文件的处理

已经被 git 提交了,怎么转到 LFS 里呢?

这时就要把 git 的旧记录也一并删除,不然该文件及历史版本,会一直存在 Git 的数据中,每次获取都占用大量带宽和存储空间。

1
2
3
4
5
6
7
8
$ git lfs migrate

# 用来将当前已经被 git 储存库保存的文件以 git lfs 的保存 (将 git 对象转为 lfs 对象)
# 例如如果将当前远程不存在的的所有 mp4 文件清除
$ git lfs migrate import --include="*.mp4"

# 如果是已经上传到其它服务器的内容, 则需要指定分支 (可能需要 push --force)
$ git lfs migrate import --include="*.mp4" --include-ref=refs/origin/master --include-ref=refs/origin/dev --include-ref=refs/origin/test

上述操作,会将所有的 git 对象,转换为 git-lfs 对象,git 历史记录会受影响。

但 .git 目录下依然储存着该文件,需要进行清空处理:

1
2
# 然后使用如下命令清理 .git 目录
$ git reflog expire --expire-unreachable=now --all && git gc --prune=now

> 如何只获取仓库本身,而不获取任何 LFS 对象?

如果相关工作不涉及到被 Git LFS 所管理的文件的话,可以选择只获取 Git 仓库自身的内容,而完全跳过 LFS 对象的获取。

1
$ GIT_LFS_SKIP_SMUDGE=1 git clone git@github.com:username/my_lfs_repo.git destination_dir

使用该方式 Clone 仓库后,又需要这些 LFS 管理的文件了,如何再次拉取下文件呢?

我们可以通过如下 Git LFS 的 pull 命令进行拉取:

1
$ git lfs pull

> 如何获取历史版本

Git LFS 文件,每次提交时,都会将文件的索引记录在对应的 Git 的 commit 中。

所以只要通过 git checkout 就能恢复出对应的版本。

> 更复杂的场景

如果一个项目中,有很多用 LFS 管理的文件,有一些目录(或文件)不是通用的,但是另一些是必要的。那么如何设置在 clone 或 checkout 时只下载必要(默认)的 LFS 上的文件呢?需要时,再去下载另一部分文件。

可以用在 git config 中设置(要先保证该路径已被 git lfs track 追踪):

1
$ git config -f .lfsconfig lfs.fetchexclude="*.mp4"

这个命令会生成一个.lfsconfig文件(如果不存在),并在其中加入。

1
2
[lfs]
fetchexclude = *.mp4

这样配置后,每次 checkout 和 clone 就会过滤掉当前目录下以 .mp4 的为结尾的文件。

那如果需要这些文件时,要怎么办呢?

可以通过上文中的 git lfs pull 进行复写,如:

1
git lfs pull -X ''

就会在拉取时,去除 fetchexclude 选项。

▎GitHub 中关于 LFS 的使用

GitHub 在 Billing 账单页面可以账号的 LFS Data 使用情况。

默认每个账号都有 1GB 的免费存储空间和 1GB 的免费带宽,可以通过购买,来添加更多的容量及带宽。

▎REF::

简单的 ID 生成器设计

关于在分布式系统里如何生成各种 ID 的问题,对于小的业务来说一直是件麻烦的事情。

小业务注定不像大厂的服务一样,有超多的服务器,很多的开发者组合,所以方案要求简单,并不太高的性能。

ID 要求

  • 唯一性
  • 保护业务隐私(不能透漏有多少用户数)
  • 数值型(更少占用空间)
  • 单向增长,精确有序 (写入性能保证)

常见的方式是有两种:

  • 中心式发号器
  • 单机发号规则

中心式发号器需要额外的服务,增加了成本,一旦发号器出了问题,会影响业务。

单机发号,在号码的规则中,约定一定的位数属于某台单独的机器,也需要在机器上执行一定的配置。

这两种对于小企业小业务来讲,都不方便。

用户 ID 的方案

该方案仅使用数据库,搞一个 Cron 进程,不断的维护一个表的 ID 队列,等待业务去消费。

简单的说,做一个小的业务规则。不断的向数据库中生成一些等待使用的 ID,然后业务在用时,从数据库中查最小可用的 ID,做为本次的实例的 ID 即可。

> 流程

用户 ID 从 3000001 开始,从 ID 中选最后一条记录,然后随机 (0-9 间)选一个数值用作跳跃值,然后加在最后一个 ID 上,生成一个新 ID,写入表中。

通过 Cron 进程,在用户表中,维护 1000 个这样的未消费 ID 队列(小时级别的调度就可以了)。

每次有新用户注册,就取最小一个未消费的 ID 值。然后更新状态到待消费。

等用户填写完信息后,将该值变成正常用户状态。

状态值

  • unspent
  • pending
  • normal
  • disable

其中 unspent/pending 这两个就是为了 ID 生成器所专门设计。

- 好处

好处是,简单啊,哪怕有多个服务,也不用关心 ID 的问题,解决了分布式的麻烦。

通过数据库的解决方案,成本较小。实施简单。

订单 ID 的方案

订单和用户不同的是,定单可以使用大长度数值。

不同于普通 ID,定单还有额外的要求:

  • 可以快速分析出有用信息 (在定单中加入时间内容)

- 关于 uint64

uint64 是定单所使用的 ID 值生成范围:

uint64 取值范围:0 - 18446744073709551615

去掉第一个 1 后,可以拆解为如下字段:

1
8446 7440 7370 9551 615

- 符合 uint64 的设计

基于这样的要求,将时间作为定单上的信息,时间天然是自增长的。

1
2020 0210 1212 1455 123
  • 2020 0210 - 日期
  • 1212 1455 - 时间(厘秒单位)中午 12 点 12 分 14 秒 55 厘秒
  • 123 - 随机数

当然也可以把前面的 20 去掉,毕竟一个系统存活几十年的概率几乎为零。

1
2020 0210 1212 1455 123

这样可以在时间精度上进行加长,并且在随机数上进行加长,可以根据实际情况来做调整

1
2002101212 14550 123

在一个小型定单系统里,在厘秒的时间段内+三位随机数,重复的概率非常低(没有做过大型测试),但理论上非常可行。

1
2
3
4
+---------------+----------------+----------------+
|timestamp(cs)14 | timestamp(cs)2 | random(3) |
+---------------+----------------+----------------+
id = sign + delta seconds | workerid | sequence

总结

ID 生成器有很多很好的方案,像 Twitter 的 Snowflake,大型项目非常推荐,但对于小项目来说,一个可行的方案不仅是速度,可行性,还有非常重要的成本。

这个方案,是实践下来一个很不错的方案。

NAS 清洁小记

春节期间,把手机,相机里的照片统一整理了次,放进了 NAS,做了一年的收尾。

这款 716+II 是 16 年末买的,也已经服役三年之久了,这三年来机器本身从没出过问题,使用上也带来了不少的便捷性。堪称近几年买过最省心好用的产品。性价比这回事不是很重要,省心比较重要。

简单的回顾下这几年的使用情况吧

- 省心好用到不记得TA的存在

  • 买回来就放在一个角落里,然后有年夏天小区总是断电,于是配了个电源。
  • 硬盘有过一次坏道,在质保期内,换了块新的。

其它就没了

- 群晖卖的是软件

这句话是对的,Synology 的配套软件才是最好用的,像 DS Photos,DS Video 装在 Apple TV 上,可以方便的看照片,电影。后来出的 Synology 系列主要面向团体用户,像 Driver,Moments。Driver 是我最常用的。在多机之间同步,重要文档几乎都放在这上面了。

几年来也总结了一套成熟可信的同步方案。

- 电源

好像这个世上除了 Apple 外,其它商家都不会做电源,弄一个大的外置电源,好丑也不方便。虽然你不会动它,但就是不好看。

- Wi-Fi

没有 Wi-Fi 就让他的摆放位置很受限制,只能放在路由器的边上。

- 备份问题

现在只有两个盘位,做备份吧,就只剩下一个盘的容量了,又不够用。

没有备份总觉得不踏实。

关于备份,有想过换一个 918+,或者额外加一个 5 盘位的拓展,但都没实施,毕竟也没那么强需求,而且现在东西换代太快了。

清理

把机器搬出来,机身上全是灰,没有做防尘处理,就简单的摆在一个架子上。

这里还找不到钥匙了,左下角硬盘的孔位是要用钥匙旋转开锁的,但实在找不到了,后来用螺丝刀慢慢打开的。

拆下风扇,做个清理。灰真大 😓

原配 2G 的内存,这次的原意是加两根大内存

拆机后发现,只有一个内存插槽,并且手里要换的内存也是 2G 单条。尴尬。只能当是清洁了。

软件开发周期

▎为什么要有周期

生命周期的每一个周期都有确定的任务,并产生一定规格的文档(资料),提交给下一个周期作为继续工作的依据。按照软件的生命周期,软件的开发不再只单单强调“编码”,而是概括了软件开发的全过程。

  • 周期带来明确,让每个人知道当下在何时、何地、要做何事。
  • 周期带来秩序,让所有人知道如何分工。
  • 周期带来简化,软件工程太复杂,只有把工程分解为小周期可理解可完成的
  • 周期带来成就,开发者可以很快看到成果,而不是一味的编码
  • 周期带来减损,短周期可以让需求者迅速做反应,而不是等整个流程结束后才知道产品和需求不对应

▎周期中包括

  • Propose(Requirement) | 提案(需求)
  • Development | 开发
  • Integration + Testing | 集成和测试
  • Release | 发布

在周期中,有很多规范可以参考,像 Agile/SCRUM,这也是整个工作期间,用过的最多的一种开发方式。

SCRUM 中定义了需求的标准,优先级的制定,开发的一些方式,集成和测试的建议,这套体系比较复杂,有机会再细讲。但好的体系学习成本高,背后的收益也大,可以在团队中树立标准的沟通语言,构建共同的认知体系,降低总成本。

另一个要提到的点是在周期中技术的应用,软件开发不仅是在开发软件,更多的是在利用现在工具整合流程,提升效能。有时,技术的采用就是标准的采用。


▎常见软件开发周期

  • 两周

以上是我见过主流的开发周期。

> Annual | 年

以年为单位的,主要是一些大型软件,以开发语言,操作系统为主。

开发语言 Node.js / Java / Python 等
操作系统 macOS / Ubuntu 等

这种系统的特点就是庞大,且有着明确的发布日期,需求的来源也有着明确的标准。

> Monthly | 月

一些偏传统的公司会采用一种发布列车的方式,每月定制发布时间。如果一个功能能在发布列车启动前做好准备,就可以上车。

总体来说,月还是偏少见的。

> Biweekly/Weekly | 双周/单周

从 MS/EF,双周一直是比较推荐的,原因是一个标准周期里的项目有点多:需求及评审,开发,集成,测试,发布,回顾。放在单周中,会挤压开发时间,让开发者疲于应对。

单周应用于一些特殊时刻,需要产品尽快上线,有种做一点上一点的感觉。


▎实践

> 角色:

  • Product | 产品
  • Designer | 设计
  • Developer | 开发
  • Tester | 测试

> 流程图

这是我在上家公司所实施的流程,图中表示了不同的周期,不同的角色,在一个时间线里,分别要做的事务。

在每个生命周期里,流程是有严格的依赖的关系,产品先行,设计其实,然后是开发,测试,直至发布。

产品是建议先行最少一个迭代的,设计要在下一个迭代开始前,准备好大量的稿件,不要等开发阶段开始后还在等设计。

开发和测试同行,其次是集成和测试同步进行。

在开发期间,同时处理线上的一些问题。


▎举例

年度 | Python Annual Release Cycle

Python 于 2019 年末宣布使用 12 个月的发布周期,委员会认为有一个一致的时间表会对社区有所帮助:

  • 开发者能制定自己的工作计划
  • 社区知道何时参与测试,提供反馈

https://lwn.net/Articles/803679
https://www.python.org/dev/peps/pep-0602


▎REF::

https://en.wikipedia.org/wiki/Systems_development_life_cycle
https://en.wikipedia.org/wiki/Software_release_life_cycle
https://stackify.com/what-is-sdlc

Windows 10 那些事

自从用了 Macinotosh 后,已经好多年没有在操作系统上纠结了,最近配了一台 NUC,顺带体验了下 Windows 10,发现一些有意思的事。

▎版本

不管愿不愿接受,Windows 10 会是该系列最终的版本了。也就是不会再有 11 了。

原因也很悲惨,Win 10 是单机操作系统的顶峰了,这个市场的机会已经没了。微软的重心也到了云和服务上。就连这个操作系统部门都被合并到了云业务部门。Office 现在出售的方式也主要是订阅了,Office 365。

无论你有多爱它,都要接受这个事实。

即然不会有新的操作系统,那 Windows 10 代表的就不再是版本了,而是一种品牌。版本则由另外的数字来代表。

微软使用了一种全新的命名方式,Windows 10 19H1 是微软于 2019 年春季推出的一个版本(H 是德文 Halbjahr 缩写,译为 6 个月)连起来 H1 就是年度上半年,所以 19H1 即 2019 年上半年发布版本。

这是 Windows 发布的一个版本的信息

- 更新

最近爆发了很多 Windows 升级的问题,作为在微软工作过的开发者,特别能理解。

一款在战略上不再是顶级的产品,资源配置上肯定会下降不少。在流程上也会为了这种配置做简化,像测试的简化。

回想过去,那些优秀的人才在帮 Windows 不停的付出,找到各种问题,代价就是高额的支出。

当然了,这里没有对和错,公司做出的决策,都会由公司来承受最终的代价。


▎Settings 设置

这个面板是面镜子,通过它可以看到很多。

Settings 像是个未完工的产品,很多时候,依然要去控制面板里找到一些设置的地方。

新闻上也有很多微软致力于迁移的文章,但,Windows 产品已是明日黄花,所以这种迁移十有八九都不会再发生了。就像一个城市,在最高点时开始建新城,随后没落了,整个新旧城的结构就这样保留了下来。

这也又一步印证了,属于我们童年、中年的那个时代过去了。


▎内核

Windows 10 有个很有意思的点,看起来它像一个双内核的系统。就是设置和控制面板。

在 Settings 中的 Region 中可以设置 Country 和 Format,但这对于一些旧的软件来并不起作用(银行 APP)。

而在控制面板的 Region Settings 中,可以单独设置 System local,并启用 Unicode UTF-8 为世界语言支持。

通过这个,可以发现,Windows 10 在对待 APP 是有两个内核(暂时这么称呼)来处理的。

对比 macOS 15.12 在 32/64 位的一刀切,Windows 承受了太多的过去。


▎32 位

WoW64(Windows on Windows 64)应该是 Win7 时开始提供的一个功能,即提供一个虚拟技术,让 32 位的应用程序正常地运行在 64 位的 Windows 中。

我在微软时还测过这类的功能,转眼间十年过去了,这项技术依然在为各种软件提供服务,也就是国内市场上目前依然是 32 位软件的居多。像银行,税务等。

能理解,32 能用,还搞啥 64,万一用户是个 32 位的旧系统。

看,这就是 Windows 文化,从来都是能用即可。没有棱角。


▎DevBox

作为一名开发者,Windows 能用吗?

在使用的这几天来看,并不行。

- 专业的 Termial

Windows 没有专业的 Terminal,这一点就不行,开发者大量的工作,都是在命令行里进行完成的。没有专业的命令行,生产效率完全无法根上。

- *nix 内核

虽然 Windows 也能方便安装各种开发软件,但没有 *nix 的内核,好多东西都无法参与,但好在现在 WSL 做的还可以。

Windows 的 FS 不支持 Soft link,我的个人习惯是把很多配置做成可同步 Sync 的,真正的文件在 Dropbox 中,而目录下用的都只是 link。

但 Windows 不支持。


▎AppData

Linux 系统习惯把用户的应用数据存放在 ~/.xxx 目录下。

Windows 现在也有一个 AppData,但不得不说,Roaming,Local,LocalLow,这几者的关系,实在让人费解。


▎其它

如果您不是从事音视频编辑、软件开发,Windows 是一款真心不错的产品。目前还没有其它系统能代替它的地位。

肺炎时期的日记

这是一个我个人完全不擅长的领域,所以开头怎么写,我也不知道。

> 流水账

年初四,按网友的说法,今天是接着不动。

上海的雨已经连续下了一周了,每日都是湿冷,好久没有运动的身体显的很臃肿。

回忘这艰难的假期,漫长如过了一个世纪,但细数起来,也不过才一周而已。

上周初,公司聚餐时,大家还在聊武汉是不是有瞒报,然后假期初还去了次迪斯尼,满城中口罩数量已经多了起来,但还是充满着热情和忙碌的。

疫情来的太快,没有一点防备,都来不及做准备,大家就各被按在了家中。

而居家的时间如蜗牛般的离去,微博朋友圈变成了疫情播报地,每天看的谈的都是疫情的进展,这一过就是多天。

> 回忘 Sars

那时,我还在读高中,那时都是在校住宿的,整个非典期间都是封校,关于外面,只有一些新闻联播可以看,并没有太多的感触和记忆。

封校期间,父亲给我送过一次东西,大约有香蕉和板蓝根,见不到人,只能放在门卫处,然后经过消毒去领取。

这次病毒期间,听母亲讲了很多那时的事,像回乡的人被堵,其实和今日何其相仿。

我们已经战胜过一次,我们终将会战胜这一次。

但这些苦难一定要加在这个民族身上吗,从某个角度来讲,是的,像吃野味,迷信,这些行为,如果不能有效去除,这些疾病会一直像噩梦伴随。

> 封城记

23 号凌晨忽然发布公告,上午 10 点封城。自小,这是第一次见过封城的行为,而随即上演的是大逃离。

河南位邻湖北正北,这次是被重点表扬的省份,因全省尽封路,禁止人员流动,外来人口报备。

这些让我想到战时的情景,而这次正是一场没有硝烟的战争,对象是一种新兴传染病病毒。

但城市并不生产物资,封城的后续就是城市供应链的打断,各种紧缺会出现。所以还是建议大家稍囤积点备灾物资。但依然希望不会,毕竟中国不仅是一个大国,还是一个生产强国。

> 责任人

武汉在这次重大事故中的各种决策都非常糟糕。

像今日最火的一张图,如果我能回到一个月前,内容是:一个月前香港对武汉病毒非常重视,而回复都是香港阴谋论。

从瞒报,人大会,万家宴,到春晚。真可谓步步凌乱。

从物资储备丰富到当天的医务人员联名求助;从凌晨封城禁令到执行 8 小时空白期,从禁止城市通行影响医务人员到调度私车服务公务职员;

更为悲痛的是早期暴露问题的医生还被依法办理了。。。

> 人,真实的人

官方数字都很冰冷,无论是多少人感染多少人离去,都没有真切感。

但人的故事,都很悲伤。

微博上那些短短的文字,记录着一个个家庭的悲惨去离。

最美逆行者的图文渲染着人们的激情。

好像苦难又一次成为这个民族的武器,但大苦很快会忘记,而个人家庭的记忆永远无法抹去。

在这场灾难里,对人的关怀上,我们的表现并不好。

武汉人像过街老鼠,酒店不准入住,甚至看到无处收留的例子。加油站不准加油,鄂A 不敢上路。

他们说,武汉人不是病毒,只是倒霉版的你。

今天对武汉人的刻薄,改日在另一件事上,刻薄的对象就是自己。

> 这个时代

网络带来的信息洪流并没有让人们变得更睿智,相反,躲在看不见的另一处充满着阴暗和毒辣。

人们已无法分辨真实和虚假,也没有能力认真和同情。

这个事情,只能告诫自己,遇到陌生的事情,不要轻易站队,多读专业文献书刊,人生漫漫,学无止境。

最后,信仰科学!