亢奋猫 发布的文章

Starting with MySQL 8 you no longer can (implicitly) create a user using the GRANT command. Use CREATE USER instead, followed by the GRANT statement:

mysql> CREATE USER 'root'@'%' IDENTIFIED BY 'root';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;

Caution about the security risks about WITH GRANT OPTION, see:

Grant all privileges on database

re: grant-all-privileges-to-root-user

前言

作为世界上最流行的开源数据库,MySQL各方面的功能都在不断完善,比如密码管理这一块,从一开始最简单的用户名密码、到 5.7 版本的 validate_password 插件、再到 8.0 版本丰富多彩的密码策略,已经完全可以媲美 DB2、Oracle、SQL Server 等大型商业数据库;今天就给大家带来 MySQL 8.0 新特性 — 密码管理。

密码管理

在MySQL 8.0版本中,针对密码管理这一块,做出了非常大的改进与完善,支持以下功能:

  1. 密码认证插件
  2. 密码过期策略
  3. 密码复用策略
  4. 密码修改验证策略
  5. 双重密码支持
  6. 密码强度策略
  7. 随机密码生成
  8. 登录失败追踪

密码认证插件

在MySQL 8.0版本中,支持以下3种密码认证插件:

(1)mysql_native_password:8.0之前默认

(2)caching_sha2_password:8.0默认

(3)sha256_password:可选

caching_sha2_password 作为 8.0 默认的密码认证插件,其安全性强于 mysql_native_password,性能优于sha256_password。但由于客户端和驱动的兼容性问题,建议还是采用 mysql_native_password 作为默认的密码认证插件。

如果采用 caching_sha2_password 作为默认的密码认证插件,那么在建立连接时,一定要指定RSA公钥,否则会报错 "ERROR 2061 (HY000): Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection."

密码过期策略

密码过期策略,可以通过系统参数进行设置,对所有用户生效

vim /etc/mysql/my.cnf

default_password_lifetime=180

也可以通过SQL语句,对指定用户进行设置

alter user [email protected]'%' password expire interval 180 day; -- 密码有效期180天
alter user [email protected]'%' password expire never;            -- 密码永不过期
alter user [email protected]'%' password expire default;          -- 采用默认的密码有效期

还可以手动指定某用户过期

mysql > alter user [email protected]'%' password expire;

密码复用策略

密码复用策略,可以通过系统参数进行设置,对所有用户生效

vim /etc/mysql/my.cnf

password_history = 6            -- 密码多少次不重复
password_reuse_interval = 365   -- 密码多少天不复用

也可以通过SQL语句,对指定用户进行设置

mysql > alter user [email protected]'%' password history 6;              -- 密码6次不复用
mysql > alter user [email protected]'%' password reuse interval 365 day; -- 密码365天不复用
mysql > alter user [email protected]'%' password history default reuse interval default; -- 采用默认复用策略

密码修改验证策略

密码修改前,需要指定旧密码,才能进行修改;这也是大大提高了安全性,大大降低了 客户端记住密码/临时离开工位 导致密码被别人篡改的风险。

可以通过系统参数进行设置,对所有用户生效

vim /etc/mysql/my.cnf

password_require_current = on --启用密码修改验证策略

也可以通过SQL语句,对指定用户进行设置

mysql > alter user [email protected]'%' password require current;          -- 启用密码修改验证策略
mysql > alter user [email protected]'%' password require optional;         -- 密码验证可选
mysql > alter user [email protected]'%' password require current default;  -- 采用默认密码验证策略

启用密码修改验证策略后,修改密码需要提供旧密码和新密码

mysql > alter user [email protected]'%' identified by 'new_password' replace 'old_password';

双重密码支持

相信很多的开发、管理员和 DBA 都遇见过这样一个场景:修改完数据库用户密码,业务系统某个/某些模块立刻不可用,报密码错误;其实这种情况很常见,业务系统模块那么多,有时候会有遗漏不奇怪。双重密码,支持旧密码和新密码同时登录,就可以很好地规避这个问题,平滑地实现密码修改。

保留旧密码

mysql > alter user [email protected]'%' identified by 'new_password' retain current password;

废弃旧密码

mysql > alter user [email protected]'%' discard old password;

密码强度策略

在 MySQL 5.7 版本中,提供了 validate_password 的插件,实现密码强度策略;在 MySQL 8.0 版本中,官方是将validate_password 改造成组件,并提供一系列系统参数,以实现密码强度策略。

首先,我们需要安装 validate_password 组件,通过以下SQL语句

mysql > INSTALL COMPONENT 'file://component_validate_password';
mysql > UNINSTALL COMPONENT 'file://component_validate_password';

然后,我们可以通过以下系统参数,进行密码强度策略设置:

mysql> SHOW VARIABLES LIKE 'validate_password%';
+--------------------------------------+--------+
| Variable_name                        | Value  |
+--------------------------------------+--------+
| validate_password.check_user_name    | ON     |
| validate_password.dictionary_file    |        |
| validate_password.length             | 8      |
| validate_password.mixed_case_count   | 1      |
| validate_password.number_count       | 1      |
| validate_password.policy             | MEDIUM |
| validate_password.special_char_count | 1      |
+--------------------------------------+--------+
7 rows in set (0.06 sec)

关于 mysql 密码策略相关参数:

validate_password.length  固定密码的总长度;
validate_password.dictionary_file 指定密码验证的文件路径;
validate_password.mixed.case_count  整个密码中至少要包含大/小写字母的总个数;
validate_password.number_count  整个密码中至少要包含阿拉伯数字的个数;
validate_password_special_char_count 整个密码中至少要包含特殊字符的个数;
validate_password.policy 指定密码的强度验证等级,默认为 MEDIUM;
    关于 validate_password.policy 的取值:
        0/LOW:只验证长度;
        1/MEDIUM:验证长度、数字、大小写、特殊字符;
        2/STRONG:验证长度、数字、大小写、特殊字符、字典文件;
mysql> set global validate_password.length=4;
mysql> set global validate_password.policy='LOW';

修改密码验证策略后我们就可以使用简单的密码进行登录了。

密码随机生成

在 MySQL 8.0 中,还支持随机密码生成

mysql > alter user [email protected]'%' identified by random password;

可以通过以下系统参数,对随机密码生成的长度进行控制

generated_random_password_length = 20  -- 随机密码生成长度

登录失败追踪

最后,再来看一下登录失败的问题;我们可以设置连续登录失败多少次,账号会被锁定多少天,具体如下:

mysql > alter user [email protected]'%' failed_login_attempts 3 password_lock_time 3; -- 失败3次锁定3天

总结

在 MySQL 8.0 版本中,密码管理这一块功能日趋完善,大大提高了MySQL数据库的安全性,尤其对政务行业、金融行业等监管要求高的企业来说,更具有吸引力。

基本原理

通过模糊特征匹配的方式进行追踪

流程:
1、用户打开分享链接时,给当前手机生成一个唯一id,并发送给服务器。
2、用户下载app后,打开app,用1中同样的策略生成一个同样的id,发送给服务器。

这样就能将打开app的用户和下载链接对应起来;
由于链接后面跟了渠道参数,然后就能将渠道和打开的用户对应起来。

详细流程:

生成带特殊参数的h5链接 -> 用户点开链接 -> h5计算设备ID -> 将设备ID和分享的链接参数绑定并上传服务器 -> 引导用户下载App -> 用户启动App -> App计算设备ID -> 从服务器查询和设备ID匹配的的参数 -> 启动完成 -> 获取到h5带的参数

服务提供商

  1. sharetrace ¥69/月
  2. openinstall ¥1000元/月
  3. shareinstall ¥618元/月
  4. xinstall ¥370元/月

我们知道做深拷贝的时候可以使用递归的方式也可以用 JSON.stringify + JSON.parse 这种看起来简单的方式。
那么 JSON.stringify + JSON.parse 这种方式真的好用吗?

我的经验告诉我:

JSON.stringify + JSON.parse 做深拷贝不安全,而且在大数据量的情况下存在性能问题,不推荐使用

下面我们主要围绕不安全的问题进行讨论,对于性能的问题简单提下。

为何不安全

不安全主要体现在两个方面:

  1. 拷贝过程中数据失真、丢失
  2. 处理特殊数据时候报错

数据失真、丢失

数据失真,丢失主要在这几种类型中有体现

Date对象拷贝后数据类型变成字符串

let obj = {
    d: new Date(),
};
console.log(JSON.parse(JSON.stringify(obj)));
// {d: "2020-08-12T04:47:40.958Z"}

正则对象、Error对象拷贝后变成空对象

let obj = {
    r: /\d+/gi,
    e: new Error('an error')
};
console.log(JSON.parse(JSON.stringify(obj)));
// {r: {}, e: {}}

对象里面的函数和undefined属性拷贝后属性丢失

let obj = {
    f: console.log,
    u: undefined
};
console.log(JSON.parse(JSON.stringify(obj)));
// {}

NaN、Infinity、-Infinity拷贝后变为null

let obj = {
    i: Infinity,
    l: -Infinity,
    n: NaN,

};
console.log(JSON.parse(JSON.stringify(obj)));
// {i: null, l: null, n: null}

改变对象的原型链

如果,对象的某个属性是由构造函数生成的,那么在拷贝后,他的 constructor 会指向 Object

var A = function () {
    this.a = 'a';
};
var a = new A();
var b = JSON.parse(JSON.stringify(a));
console.log(a.constructor, b.constructor);
// ƒ () {this.a = 'a'} ƒ Object() { [native code] }

特殊数据报错

这个简单的说就是如果对象中有环的话话就会报错,最简单的例子就是

console.log(JSON.parse(JSON.stringify(window)));

这个就会报错,所以在使用这种方式做深拷贝的时候也要注意环的问题。

为何性能差

关于性能的问题我这里不多说,推荐《如何提升JSON.stringify()的性能》这篇文章,这篇文章对JSON.stringify的性能问题说的很清晰,我也很认同。

参考文档

JSON.stringify深拷贝的缺点
如何提升JSON.stringify()的性能

应用举例

比如 AutoCAD、GoogleEarth、Unity、Unreal、PSPDKit、WebPack 等等。拿其中几个来简单说一下。

AutoCAD

这是一个用于画图的软件,在很长的一段时间是没有 Web 的版本的,原因有两个,其一,是 Web 的性能的确不能满足他们的需求。其二,在 WebAssembly 没有面世之前,AutoCAD 是用 C++ 实现的,要将其搬到 Web 上,就意味着要重写他们所有的代码,这代价十分的巨大。

而在 WebAssembly 面世之后,AutoCAD 得以利用编译器,将其沉淀了 30 多年的代码直接编译成 WebAssembly,同时性能基于之前的普通 Web 应用得到了很大的提升。正是这些原因,得以让 AutoCAD 将其应用从 Desktop 搬到 Web 中。

Google Earth

Google Earth 也就是谷歌地球,因为需要展示很多 3D 的图像,对性能要求十分高,所以采取了一些 Native 的技术。最初的时候就连 Google Chrome 浏览器都不支持 Web 的版本,需要单独下载 Google Earth 的 Destop 应用。而在 WebAssembly 之后呢,谷歌地球推出了 Web 的版本。而据说下一个可以运行谷歌地球的浏览器是 FireFox。

Unity 和 Unreal 游戏引擎

这里给两个油管的链接自己体验一下。

Unity WebGL 的戳这里:
https://youtu.be/rIyIlATjNcE
Unreal 引擎的戳这里:
https://www.youtube.com/watch?v=TwuIRcpeUWE

什么时候使用 WebAssembly?

说了这么多,我到底什么时候该使用它呢?总结下来,大部分情况分两个点。

对性能有很高要求的 App/Module/ 游戏。
在 Web 中使用 C/C++/Rust/Go 的库举个简单的例子。如果你要实现的 Web 版本的 Ins 或者 Facebook, 你想要提高效率。那么就可以把其中对图片进行压缩、解压缩、处理的工具,用 C++ 实现,然后再编译回 WebAssembly。

a70d0081607081471df4db435641b51a.jpg

如何描述一个Web应用的性能模型,简单来说,应该是 PV(PageView) + RT(Response
Time)。一般我们经常会看到一些网站发布数据说,我们的网站一天的PV是多少多少,这其实就就是一个很直观的性能数据。

PV 其实说的就是业务量:你的系统在可接受的 RT 内,所承受的 PV 就是系统的处理能力;当然我们更关注的是单位时间内的 PV,比如每秒的
PV,这代表你系统所能承受的并发能力;

我一贯的观点是,压力测试更多的是发现潜在的问题,其实没有办法告诉你他能支撑多大的业务量;这其中主要的问题在于业务的快速变化和用户行为的不可预知。增加服务器一般就是处理能力不够了,比如
cpu 繁忙,io 繁忙;

当然很多时候,业务系统的扩展性决定了你的应用是不是能通过增加服务器的方式提高处理能力;更多的是要找到问题,对症下药。

下面介绍两款免费的压力测试软件:

ab

ab 测试工具是 Apache 提供的一款测试工具,具有简单易上手的特点,在测试 Web 服务时非常实用。

ab 可以在 Windows 系统中使用,也可以在 Linux 系统中使用。这里我说下在 Linux 系统中的安装方法,非常简单,只需要在 Linux 系统中输入 yum-y install httpd-tools 命令,就可以了。

安装成功后,输入 ab 命令,可以看到以下提示:

ab.jpg

ab 工具用来测试 post get 接口请求非常便捷,可以通过参数指定请求数、并发数、请求参数等。

例如,一个测试并发用户数为 10、请求数量为 100 的的 post 请求输入如下:

ab -n 100  -c 10 -p 'post.txt' -T 'application/x-www-form-urlencoded' 'http://test.api.com/test/register'

post.txt 为存放 post 参数的文档,存储格式如下:

usernanme=test&password=test&sex=1

附上几个常用参数的含义:

-n:总请求次数(最小默认为 1);
-c:并发次数(最小默认为 1 且不能大于总请求次数,例如:10 个请求,10 个并发,实际就是 1 人请求 1 次);
-p:post 参数文档路径(-p 和 -T 参数要配合使用);
-T:header 头内容类型(此处切记是大写英文字母 T)。

当我们测试一个 get 请求接口时,可以直接在链接的后面带上请求的参数:

ab -c 10 -n 100 http://www.test.api.com/test/login?userName=test&password=test

输出中,有几项性能指标可以提供给我们参考使用:

  • Requests per second:吞吐率,指某个并发用户数下单位时间内处理的请求数;
  • Time per request:上面的是用户平均请求等待时间,指处理完成所有请求数所花费的时间 /(总请求数 / 并发用户数);
  • Time per request:下面的是服务器平均请求处理时间,指处理完成所有请求数所花费的时间 / 总请求数;
  • Percentage of the requests served within a certain time:每秒请求时间分布情况,指在整个请求中,每个请求的时间长度的分布情况,例如有 50% 的请求响应在 8ms 内,66% 的请求响应在 10ms 内,说明有 16% 的请求在 8ms~10ms 之间。

JMeter

6ffe85677e50bb75152d45526a7ba667.png

该工具使用方法很多地方都有介绍,此处不在赘述。

使用方法:https://zhuanlan.zhihu.com/p/36526346
官网:https://jmeter.apache.org/

个人感觉,React 就像王者荣耀里的露娜,上手不容易,玩好更难,但上限很高,真的玩好了会非常秀。
Vue 就有点像亚瑟,上手容易,但上限不高,即使再熟练,也搞不出太秀的操作。

从团队角度:

  • 团队技术水平高,代码规范,用 React 更灵活。
  • 团队技术水平一般,用 Vue 更保险,出 bug 几率低点,也不容易写出很难维护的代码。

从个人角度:

  • 使用 React 的同时也是在加深自己对编程、函数式等等概念的理解,提升思考能力。慢慢的会有所积累和成长。
  • 而使用 Vue 一般能更快完成任务。但是个人感觉在技术方面的成长有限。

通过使用该工具我们可以轻松调试iOS或macOS的系统推送,我们只需要准备一个证书,准备设备token,就可以直接和 apns 通信并发送推送信息。通常我们可以用次工具判断证书是否正确配置。

Pusher

OS X and iOS application and framework to play with the Apple Push Notification service (APNs)

osx1.png

什么是注解 (Annotation)

注解其实就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相对应的处理。

为什么要用注解

注解可以给类、方法上注入信息。

常见注解

java.lang包下存在着5个基本的Annotation,其中有2个Annotation我们是非常常见的了。

@Overried

@Overried是告诉编译器要检查该方法是实现父类的,可以帮我们避免一些低级的错误。

比如当我们写了@Overried之后,我们在实现equals()方法的时候,把euqals()打错了,那么编译器就会发现该方法并不是实现父类的,于是就会给予错误。

@Deprecated

该注解也非常常见,Java在设计的时候,可能觉得某些方法设计得不好,为了兼容以前的程序,是不能直接把它抛弃的,于是就设置它为过时。

自定义注解

我们自定义的注解是可以带成员变量的,定义带成员变量的注解叫做元数据Annotation

public @interface MyAnnotation {
    String username();
}

使用注解

// 注解拥有什么属性,在修饰的时候就要给出相对应的值
@MyAnnotation(username = "kangfenmao")
public void add() {}

读取注解

@MyAnnotation(username = "kangfenmao")
public void add() {
  MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
  String username = annotation.username();
}

总结

注入对象的步骤:

  1. 得到想要注入的对象属性
  2. 通过属性得到注解的信息
  3. 通过属性的写方法将注解的信息注入到对象上
  4. 最后将对象赋给类。

注解其实就是两个作用:

  1. 让编译器检查代码
  2. 将数据注入到方法、成员变量、类上

如果没有响应注解内容,注解可以说是没有实用价值。让注解真正发挥作用,主要就在于注解处理方法。

JDK 常用注解

  1. 基本 Annotation 在 lang 包下,用于常用于标记该方法,抑制编译器警告等
  2. 元 Annotaion 在 annotaion 包下,常用于修饰其他的 Annotation 定义