写点什么

极客大学 - 架构师训练营 第十一周作业

用户头像
9527
关注
发布于: 2020 年 12 月 02 日



作业一



题目一: 系统高可用

导致系统不可用的原因有哪些?保障系统稳定高可用的方案有哪些?请分别列举并简述



引起系统故障的一些主要原因



  • 硬件故障

  • 软件 bug

  • 系统发布

  • 并发压力

  • 网络攻击

  • 外部灾害

保障系统稳定高可用的方案

提高代码可用性

  • 采用高内聚、低耦合组件设计

  • 采用面向对象设计

  • 采用DDD 建模

使用服务集群

  • 假设只有一台服务器执行所有应用,只要有人不小心踩到了电源插头,就可以导致整个服务宕机。通常系统设计时通常会将应用部署到不同的服务器上:若一台服务器宕机的概率为 10%,即可用性为 90% ;三台同样的服务器的可用性就可以提高到 1 - 10% ^3 = 99.9%,可用性明显上升。当然,服务集群意味着更高的硬件成本,现在比较流行虚拟化容器等技术降低成本。

无状态组件

  • 部署服务集群是保证高可用的最基本需求:确保任何一个节点都可以断连、关机、升级,但是剩余的服务依旧正常工作。应用集群一般设计为无状态服务,通过 Session、cache 或是数据库共享信息。

Load Balancing

  • 负载均衡既是应对网络并发压力的解决方案,也可以在检测到某实例故障时,无缝切换流量,提高系统容错能力。

降级、熔断、限流

  • 降级也就是当我们的服务器压力剧增为了保证核心功能的可用性,而选择性的降低或是直接关闭一些次要功能

  • 熔断一般是指依赖的外部接口出现故障的情况断绝和外部接口的联系

  • 限流也就是系统规定了多少承受能力,只允许这么些请求能过来,拒绝超载请求

数据备份、恢复

数据库奔溃比服务器宕机危害更大,因为用户的数据很可能会就此丢失,后果不堪设想。数据库冗余备份是系统设计时必须的考量。每个数据中心都应该具有完整的备份,并事先计划好数据丢失和恢复的策略。

Failover

  • 失效转移指的是当主要组件异常时,其功能转移到备份组件。其要点在于有主有备,且主故障时备可启用并设置为主。通常的实现手段有:主从复制、主主复制,也可以结合数据分片等等技术。

异地多活

  • 服务集群、数据库扩展后,有些安全隐患依旧不可避免,比如地震、火灾这类自然灾害很可能导致整个机房遭遇重大破坏。为了避免这类事故,一般会在多地部署机房,实现异地容灾容错。当然地球爆炸时,异地多活也会失效,所以以防万一我们也可以将服务部署在卫星上

故障恢复计划

如上的架构设计仅仅是提高系统的可用性,但依旧不可能完全避免故障产生。因而还得建立一套系统的故障恢复流程:

  • 能及时地隔离故障设备,确保剩余系统功能正常

  • 建立故障历史记录,并追踪问题根源

  • 通过监控系统收集负载数据并分析趋势

  • 建立一系列恢复手册,并定期测试其实用性

  • 员工培训,以提高设计、部署、运维的能力

  • 还应制定安全策略,抑制安全漏洞



题目二: 密码验证函数

请用你熟悉的语言写一个用户密码验证函数,Boolean checkPW(String 用户ID, String 密码明文, String 密码密文), 返回密码是否正确 boolean 值,密码加密算法使用你认为认为合适的加密算法。



单向散列加密算法常用于提取数据,验证数据的完整性。发送者将明文通过单向加密算法加密生成固定长度的密文串,然后将明文和密文串传递给接收方。接收方在收到报文后,将解明文使用相同的单向加密算法进行加密,得出加密后的密文串。随后与发送者发送过来的密文串进行对比,若发送前和发送后的密文串相一致,则说明传输过程中数据没有损坏;若不一致,说明传输过程中数据丢失了。其次也用于密码加密传递存储。单向加密算法只能用于对数据的加密,无法被解密,其特点为固定长度输出、雪崩效应。

在本次题目中,我采取HMAC办法来进行加密

HMAC

HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code),HMAC运算利用哈希算法(MD5、SHA1等),以一个密钥和一个消息为输入,生成一个消息摘要作为输出。下面是以MD5算法的python实现代码:

def generate_hash(salt, password, method):
salt = salt.encode("utf-8")
password = password.encode("utf-8")
mac = hmac.HMAC(salt, password, method)
return mac.hexdigest()



通常来说如果两个用户密码相同,那么他们密码的单向散列之后的哈希值也是相同的。因而出现了一种叫“查表法”的算法破解哈希密码,主要的思想就是预计算密码字典中的每个密码,然后把哈希值和对应的密码储存到一个用于快速查询的数据结构中。但是查表法只有在所有密码都以相同方式进行哈希加密时才有效;我们可以通过“随机化”哈希来阻止这类攻击,于是当相同的密码被哈希两次之后,得到的值就不相同了。比如可以在密码中混入一段“随机”的字符串再进行哈希加密,这种字符串被称作盐值。



盐值是可以被暴露,但是要保证每个用户唯一,而且不要太短:

  • 用相同的盐,意味着相同的密码又得到了相同的散列值,无法对抗“查表法”。有些算法包裹了好几层的散列算法事实上是没有意义的。

  • 盐太短,又容易暴力破解,安全性又不够用了



本题中我采取随机算法来产生盐值

_sys_rng = SystemRandom()
SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
DEFAULT_PBKDF2_ITERATIONS = 150000
def gen_salt(length):
"""Generate a random string of SALT_CHARS with specified ``length``."""
if length <= 0:
raise ValueError("Salt length must be positive")
return "".join(_sys_rng.choice(SALT_CHARS) for _ in range(length))



编程环境



代码说明

整个代码我采用了flask的框架来进行一个web应用的实现,整体代码包括了前端简单的login界面,数据库sqlite,以及后端代码flask框架。

代码组成:

├── project
│   ├── __init__.py
│   ├── auth.py
│   ├── db.sqlite
│   ├── main.py
│   ├── models.py
│   ├── security.py
│   └── templates
│   ├── base.html
│   ├── index.html
│   ├── login.html
│   ├── profile.html
│   └── signup.html
└── requirements.txt
  • template - 前端网页模版

  • project - 后端flask代码

  • db.sqlite - 数据库文件,存储用户登陆信息



核心加密代码 (security.py):

import hmac
from random import SystemRandom
# Define global variables
_sys_rng = SystemRandom()
SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
DEFAULT_PBKDF2_ITERATIONS = 150000
def gen_salt(length):
"""Generate a random string of SALT_CHARS with specified ``length``."""
if length <= 0:
raise ValueError("Salt length must be positive")
return "".join(_sys_rng.choice(SALT_CHARS) for _ in range(length))
def generate_hash(salt, password, method):
salt = salt.encode("utf-8")
password = password.encode("utf-8")
mac = hmac.HMAC(salt, password, method)
return mac.hexdigest()
def generate_password_hash(password, method="sha256", salt_length=8):
"""Hash a password with given hash method and salt length.
The format for the hashed string looks like this::
method$salt$hash
:param password: the password to hash.
:param method: the hash method to use (one that hashlib supports). Can
optionally be in the format ``pbkdf2:<method>[:iterations]``
to enable PBKDF2.
:param salt_length: the length of the salt in letters.
:return: the hashed string
"""
salt = gen_salt(salt_length)
hash_value = generate_hash(salt, password, method)
return f'{method}${salt}${hash_value}'
def check_password_hash(password_hash, password):
"""Check a password against a given salted and hashed value.
:param password_hash: a hashed string
:param password: given password string
:return: True if the password matched, False otherwise.
"""
if password_hash.count("$") < 2:
return False
method, salt, user_hash_value = password_hash.split("$", 2)
gen_hash_value = generate_hash(salt, password, method)
return user_hash_value == gen_hash_value
if __name__ == '__main__':
user_password = generate_password_hash('test')
print(user_password.split('$',2))
print(check_password_hash(user_password, 'test'))



代码运行
  • 在终端输入 flask run





  • 注册用户



  • 登陆验证



  • 登陆成功



发布于: 2020 年 12 月 02 日阅读数: 28
用户头像

9527

关注

还未添加个人签名 2020.04.22 加入

还未添加个人简介

评论

发布
暂无评论
极客大学 - 架构师训练营 第十一周作业