w8-网络遗书demo
首先最近搜集到关于一些https的资料,先记录下来,做个备份,这个早晚要用到
网上流传的所谓「支付宝偷偷添加根证书,将造成安全隐患」的说法是否正确?
Demo(数据库)
继续上周的demo,目前我先做数据库的表设计,这个表我打算先在demo中统一用一张表来测试,然后最后在正式版通过拆分来提高效率。
数据库的表项目上周有以上部分
{
'name':name, //用户名
'password':password, //密码
'note':note, //遗书内容
'reciver':reciver, //接收人
'heatbeatmail':heatbeatmail, //心跳邮箱
'heatbeatrate':heatbeatrate, //心跳频率
'heatbeatdelay':heatbeatdelay //心跳过期
}
现在来认真重新设计这个表,首先是用户名密码。
用户名和密码,应该是通过平台注册来的,且用户名应该是个id号而不是用户的真实姓名,所以应该拆分成id、name、password三项。
{
'id':id, //用户id
'password':password, //密码
'name':name //用户名
}
另外,平台应该支持第三方的认证系统,这样的话,用户就可以不设置平台自身密码,而只用id就可以了,所以需要确认下author2.0中数据库中的字段怎么设计,这个有点麻烦可以先不放在demo中做。
再来是遗书部分,遗书应该可以不止能够写一封,一个人应该可以写多封遗书,而且应该有个上限,避免可能出现的不必要的麻烦,暂定上限为10的话,所以遗书的字段应该是note01~note10,随机加的,先测试时候暂时用一个note1和note2。
然后应该可以指定接收人接收指定的留言内容,这里面应该是个嵌套,比如说contact01:{mail:'[email protected]',phone:'13821647463',name:'小白'},然后还应有note01:[content01,content02]
{
'note01':note, //遗书内容
'noteLink':['content01','content02'], //遗书关联
'contact01':{
mail:'[email protected]',
phone:'13821647463',
name:'小白'
}, //接收人1
'contact02':{
mail:'[email protected]',
phone:'13821647464',
name:'小黑'
} //接收人2
}
最后是心跳部分,其实c2d2和曹帅讨论过这种心跳机制,他认为Perpetu的死亡被反告知的方式似乎更合理一些,因为只要死亡的话,亲属肯定会知道,到时候拿着某个code来和服务端确认会更好一点,而且这样后台就可以做的很简单了;但我其实个人更喜欢心跳模式,但看样子两种模式都有人喜欢,我决定两个都做,但当前我打算在demo版本先实现心跳模式再说。
我琢磨了一段时间,觉得可以使用时间戳的方式来做,首先用户可以输入的是
{
'heatbeatmail':heatbeatmail, //心跳邮箱
'heatbeatrate':heatbeatrate, //心跳频率
'heatbeatdelay':heatbeatdelay //心跳过期
}
而其实还需要一个最后更新的时间戳,来标注最后一次得知用户心跳的时间,然后所有的心跳频率与心跳过期时间均在这个时间戳上叠加即可,所以配合规则应该是这样的
{
'heatbeatMail':heatbeatmail, //心跳邮箱
'heatbeatRate':heatbeatrate, //心跳频率
'heatbeatDelay':heatbeatdelay, //心跳过期
'heatbeatUpdate':heatbeatUpdate, //心跳更新时间
'heatbeatSync':heatbeatSync, //心跳待同步时间
'heatbeatFinal':heatbeatFinal //心跳过期时间
}
其中应该满足如下规则:
- 心跳待同步时间=心跳更新时间+心跳频率
- 心跳过期时间=心跳待同步时间+心跳过期=心跳更新时间+心跳频率+心跳过期
- 心跳更新时间应该随着用户的返回消息而更新
- 心跳更新时间、心跳频率、心跳过期这三者任意值的更改,都应该连带更新心跳待同步时间和心跳过期时间
- 如果系统检测到当前时间≥心跳待同步时间,且≤心跳过期时间,则每天发送一次消息询问心跳
之前没做过mongodb的比较查询,搜了下发现这样做是可行的
如果日期可以直接拿来当整数比较的话逻辑大概是这样:
find({"heatbeatSync":{"$lte":today},"heatbeatFinal":{"$gte":today}})
检索后,我觉得还应该设置一个参数,确定用户是否已经确实死亡,所以应该设置一个布尔参数进行判断,deathConfirm,还应该设置一个联系人的邮件是否已经投放,notesendConfirm,这样的话还应该有:
{
'deathConfirm':'False', //死亡确认
'notesendConfirm':'False' //留言发送确认
}
另外,这样的服务还应该可以停止,再加上一个用户的手机号码,所以总的一张基本完整的数据表应该是这样的:
{
'id':id, //用户id
'password':password, //密码
'name':name, //用户名
'phone':phone, //手机号码
'note01':{
'content':content, //遗书内容
'link':['content01','content02'] //遗书关联
} //遗书相关
'note02':{
'content':content, //遗书内容
'link':['content03'] //遗书关联
} //遗书相关
'contact01':{
mail:'[email protected]',
phone:'13821647463',
name:'小白'
}, //接收人1
'contact02':{
mail:'[email protected]',
phone:'13821647464',
name:'小黑'
}, //接收人2
'contact03':{
mail:'[email protected]',
phone:'13821647465',
name:'无常'
}, //接收人3
'heatbeatMail':heatbeatmail, //心跳邮箱
'heatbeatRate':heatbeatrate, //心跳频率
'heatbeatDelay':heatbeatdelay, //心跳过期
'heatbeatUpdate':heatbeatUpdate, //心跳更新时间
'heatbeatSync':heatbeatSync, //心跳待同步时间
'heatbeatFinal':heatbeatFinal, //心跳过期时间
'deathConfirm':'False', //死亡确认
'notesendConfirm':'False', //留言发送确认
'serviceState':'True' //服务状态
}
然后根据这张表重新调整一下用户的输入框内容,首先是wtf表单数据格式定义:
class DemoForm(Form):
# 用户注册id名
userID = TextField('用户名', validators = [DataRequired()])
# 密码输入框
password = PasswordField('密码',validators = [Length(min = 8, max = 40)])
# 用户名输入框
name = TextField('真实姓名', validators = [DataRequired()])
# 用户手机输入框
phone = TextField('手机', validators = [DataRequired()])
# 遗书输入框
content01 = TextAreaField('留言1', validators = [DataRequired()])
content02 = TextAreaField('留言2', validators = [DataRequired()])
# 接收人输入框
contact01Name = TextField('接收人1姓名', validators = [DataRequired()])
contact01Mail = TextField('接收人1邮箱', validators = [DataRequired()])
contact01Phone = TextField('接收人1电话', validators = [DataRequired()])
contact02Name = TextField('接收人2姓名', validators = [DataRequired()])
contact02Mail = TextField('接收人2邮箱', validators = [DataRequired()])
contact02Phone = TextField('接收人2电话', validators = [DataRequired()])
contact03Name = TextField('接收人3姓名', validators = [DataRequired()])
contact03Mail = TextField('接收人3邮箱', validators = [DataRequired()])
contact03Phone = TextField('接收人3电话', validators = [DataRequired()])
# 心跳邮箱
heatbeatmail = TextField('心跳邮箱', validators = [Email()])
# 心跳频率
rate_choices = [('1', '每周'), ('2', '每月'), ('3', '每3个月')]
heatbeatrate = SelectField('心跳频率',choices = rate_choices, default = '2')
# 心跳延迟
delay_choices = [('1', '1周后'), ('2', '1个月后'), ('3', '3个月后')]
heatbeatdelay = SelectField('心跳延迟',choices = delay_choices, default = '2')
然后是web显示的表单页面:
<form action="/" method="POST">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.userID.label }} {{ form.userID(class="form-control") }}
</div>
<div class="form-group">
{{ form.password.label }} {{ form.password(class="form-control") }}
</div>
<div class="form-group">
{{ form.name.label }} {{ form.name(class="form-control") }}
</div>
<div class="form-group">
{{ form.phone.label }} {{ form.phone(class="form-control") }}
</div>
<div class="form-group">
{{ form.content01.label }} {{ form.content01(class="form-control note") }}
</div>
<div class="form-group">
{{ form.content02.label }} {{ form.content02(class="form-control note") }}
</div>
<div class="form-group">
{{ form.contact01Name.label }} {{ form.contact01Name(class="form-control") }}
</div>
<div class="form-group">
{{ form.contact01Mail.label }} {{ form.contact01Mail(class="form-control") }}
</div>
<div class="form-group">
{{ form.contact01Phone.label }} {{ form.contact01Phone(class="form-control") }}
</div>
<div class="form-group">
{{ form.contact02Name.label }} {{ form.contact02Name(class="form-control") }}
</div>
<div class="form-group">
{{ form.contact02Mail.label }} {{ form.contact02Mail(class="form-control") }}
</div>
<div class="form-group">
{{ form.contact02Phone.label }} {{ form.contact02Phone(class="form-control") }}
</div>
<div class="form-group">
{{ form.contact03Name.label }} {{ form.contact03Name(class="form-control") }}
</div>
<div class="form-group">
{{ form.contact03Mail.label }} {{ form.contact03Mail(class="form-control") }}
</div>
<div class="form-group">
{{ form.contact03Phone.label }} {{ form.contact03Phone(class="form-control") }}
</div>
<div class="form-group">
{{ form.heatbeatmail.label }} {{ form.heatbeatmail(class="form-control") }}
</div>
<div class="form-group">
{{ form.heatbeatrate.label }} {{ form.heatbeatrate(class="form-control") }}
</div>
<div class="form-group">
{{ form.heatbeatdelay.label }} {{ form.heatbeatdelay(class="form-control") }}
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
最后是处理post后的表单接收,和插入数据库前的数据细节整理:
# 如果接收到post提交
if request.method == 'POST':
userID = request.form['userID']
password = request.form['password']
name = request.form['name']
phone = request.form['phone']
content01 = request.form['content01']
content02 = request.form['content02']
contact01Mail = request.form['contact01Mail']
contact01Phone = request.form['contact01Phone']
contact01Name = request.form['contact01Name']
contact02Mail = request.form['contact02Mail']
contact02Phone = request.form['contact02Phone']
contact02Name = request.form['contact02Name']
contact03Mail = request.form['contact03Mail']
contact03Phone = request.form['contact03Phone']
contact03Name = request.form['contact03Name']
heatbeatmail = request.form['heatbeatmail']
heatbeatrate = request.form['heatbeatrate']
heatbeatdelay = request.form['heatbeatdelay']
heatbeatUpdate = str(time.strftime("%Y-%m-%d %H:%M:%S"))
heatbeatSync = str(time.strftime("%Y-%m-%d %H:%M:%S")) + 'heatbeatrate'
heatbeatFinal = str(time.strftime("%Y-%m-%d %H:%M:%S")) + 'heatbeatrate' + 'heatbeatdelay'
# 设置插入数据库的内容
demo_data = {
'userID':userID,
'password':password,
'name':name,
'phone':phone,
'note01':{
'content':content01,
'link':['content01','content02']
},
'note02':{
'content':content02,
'link':['content03']
},
'contact01':{
'mail':contact01Mail,
'phone':contact01Phone,
'name':contact01Name
},
'contact02':{
'mail':contact02Mail,
'phone':contact02Phone,
'name':contact02Name
},
'contact03':{
'mail':contact03Mail,
'phone':contact03Phone,
'name':contact03Name
},
'heatbeatMail':heatbeatmail,
'heatbeatRate':heatbeatrate,
'heatbeatDelay':heatbeatdelay,
'heatbeatUpdate':heatbeatUpdate,
'heatbeatSync':heatbeatSync,
'heatbeatFinal':heatbeatFinal,
'deathConfirm':False,
'notesendConfirm':False,
'serviceState':True
}
# 插入数据
collection.insert_one(demo_data)
测试一下,确认是可以输入成功的。
Demo(服务模块)
除了flask完成的web组件儿,我还需要两个函数服务,一个用于检查给到期同步的人发送心跳确认邮件,另一个检查心跳过期的人发送遗嘱邮件。
或者按功能区分,一个模块专负责检查数据库,然后把查到的整理成合适的json格式传递出去,另一个模块专门跑邮件服务,负责把接收到的json解析成邮件发送,这种划分其实更科学也更灵活,而且很容易扩展,但要用到至少3个container,所以demo场景不使用这种方式划分。
而且为了测试还应该设置一个按钮,当触发时发送邮件测试联通性。
检查数据库发心跳邮件的逻辑应该是:
- 检出所有应该发确认心跳的用户数据对象
- 检出这些对象中的邮箱、与用户名字段
- 向这些邮箱发送带用户名和restapi接收的链接
发过期邮件的逻辑很类似:
- 检出所有过期的用户数据对象
- 检出这些对象中的邮箱、用户名、手机号码、联系人联系方式等字段
- 给联系人发遗嘱链接
- 告知用户信息已传达
这两个函数都是每天运行一次,测试就是直接获取用户的这些对象,然后整理成邮件可触发函数
这里flask还需要配合实现两件事:
- 提供一个REST的post口服务来接收用户心跳回应,但并不需要提供web界面
- 提供一个遗嘱查看的界面
这里面最好做的是触发函数,先做个触发的测试
# 邮件服务测试
def service_test(name,usermail,note,contact,contactmail,heatbeatUpdate):
content = '您好,这是一封网络遗嘱服务邮件,受到' + name + '的委托' + '代他发送给您的留言如下:' + '\n' + note
from_addr = raw_input('enter mailaddr:')
password = raw_input('enter password:')
to_addr = contactmail
smtp_server = 'smtp.163.com'
msg = MIMEText('<html><body><h1>whenmgone网络遗书</h1>' +
'<p>' + content + '</p>' +
'</body></html>', 'html', 'utf-8')
msg['From'] = _format_addr('whenmgone网络遗书服务 <%s>' % from_addr)
msg['To'] = _format_addr(contact + ' <%s>' % to_addr)
msg['Subject'] = Header('来自' + name +'先生的遗嘱', 'utf-8').encode()
server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()
name = '良朝'
usermail = 'liangchaob'
note = '快进到我的碗里来'
contact = '小冷'
contactmail = '[email protected]'
heatbeatUpdate = 12
service_test(name,usermail,note,contact,contactmail,heatbeatUpdate)
其实很简单,不过是把邮件发送给封装成一个函数,把变量传进去而已。
然后我要做的是想办法把人筛出来,就用之前的比较函数来做,由于我还没搞清楚怎么做日期计算,所以打算先用整数来代替: 首先我先要想办法插入整数的日期:
直接在主函数上改,先设置个1,8,15
# heatbeatUpdate = str(time.strftime("%Y-%m-%d %H:%M:%S"))
# heatbeatSync = str(time.strftime("%Y-%m-%d %H:%M:%S")) + 'heatbeatrate'
# heatbeatFinal = str(time.strftime("%Y-%m-%d %H:%M:%S")) + 'heatbeatrate' + 'heatbeatdelay'
heatbeatUpdate = 1
heatbeatSync = 8
heatbeatFinal = 15
再设置个4,14,24
heatbeatUpdate = 4
heatbeatSync = 14
heatbeatFinal = 24
再来个10,25,35
heatbeatUpdate = 10
heatbeatSync = 25
heatbeatFinal = 35
全部插入后,我使用比较来筛选该发同步邮件的人,我选择14这个数字,当今天是14时候,第一个和第二个都应该被返回
# coding: utf-8
# 导入pymongo模块
import pymongo
# 设置数据库地址
client = pymongo.MongoClient('172.16.191.163', 27017)
# 设置数据库名
db = client['demodb']
# 设置表名
collection = db['demotable']
today = 14
# 筛选出应该发同步邮件的人
for i in collection.find({"heatbeatSync":{"$lte":today},"heatbeatFinal":{"$gte":today}}):
print i
返回后如下:
$ python testdb.py
{u'notesendConfirm': False, u'heatbeatDelay': u'1', u'note02': {u'content': u'\u4f60\u5728\u54ea\u91cc', u'link': [u'content03']}, u'name': u'\u7528\u62371', u'phone': u'123', u'contact02': {u'mail': u'[email protected]', u'name': u'c2', u'phone': u'122'}, u'userID': u'id1', u'deathConfirm': False, u'heatbeatMail': u'[email protected]', u'serviceState': True, u'heatbeatFinal': 15, u'password': u'P@ssw0rd', u'note01': {u'content': u'\u597d\u4e45\u4e0d\u89c1', u'link': [u'content01', u'content02']}, u'heatbeatRate': u'1', u'heatbeatSync': 8, u'_id': ObjectId('566a7144e3465f0819850d48'), u'contact01': {u'mail': u'[email protected]', u'name': u'c1', u'phone': u'121'}, u'contact03': {u'mail': u'[email protected]', u'name': u'c3', u'phone': u'123'}, u'heatbeatUpdate': 1}
{u'notesendConfirm': False, u'heatbeatDelay': u'2', u'note02': {u'content': u'\u6ca1\u8def\u5c31\u6ca1\u8def', u'link': [u'content03']}, u'name': u'\u7528\u62372', u'phone': u'18681649691', u'contact02': {u'mail': u'[email protected]', u'name': u'\u8fd8\u662f\u826f\u671d', u'phone': u'123414144'}, u'userID': u'id2', u'deathConfirm': False, u'heatbeatMail': u'[email protected]', u'serviceState': True, u'heatbeatFinal': 24, u'password': u'P@ssw0rd', u'note01': {u'content': u'\u5c71\u91cd\u6c34\u590d\u7591\u65e0\u8def', u'link': [u'content01', u'content02']}, u'heatbeatRate': u'2', u'heatbeatSync': 14, u'_id': ObjectId('566a72c6e3465f081b03b990'), u'contact01': {u'mail': u'[email protected]', u'name': u'\u826f\u671d', u'phone': u'11231314'}, u'contact03': {u'mail': u'[email protected]', u'name': u'\u5fc5\u987b\u662f\u826f\u671d', u'phone': u'13441441'}, u'heatbeatUpdate': 4}
乱糟糟的,我改下查询条件将其中只查询用户id和用户名,筛出来:
collection.find({"heatbeatSync":{"$lte":today},"heatbeatFinal":{"$gte":today}},{'userID':1,'name':1})
得到结果精简了很多:
$ python testdb.py
{u'_id': ObjectId('566a7144e3465f0819850d48'), u'userID': u'id1', u'name': u'\u7528\u62371'}
{u'_id': ObjectId('566a72c6e3465f081b03b990'), u'userID': u'id2', u'name': u'\u7528\u62372'}
结果是准确的,然后照猫画虎,把最终遗书函数发出来
ftoday = 20
# 筛选出应该发遗嘱邮件的人
for i in collection.find({"heatbeatFinal":{"$lte":ftoday}},{'userID':1,'name':1}):
print i
选择20,看能否把第一个筛出来:
$ python testdb.py
{u'_id': ObjectId('566a7144e3465f0819850d48'), u'userID': u'id1', u'name': u'\u7528\u62371'}
选择50,看能否把所有的筛出来:
$ python testdb.py
{u'_id': ObjectId('566a7144e3465f0819850d48'), u'userID': u'id1', u'name': u'\u7528\u62371'}
{u'_id': ObjectId('566a72c6e3465f081b03b990'), u'userID': u'id2', u'name': u'\u7528\u62372'}
{u'_id': ObjectId('566a7421e3465f081db88783'), u'userID': u'\x08id3', u'name': u'\u8001\u4e09'}
测试完成,发现都是ok的。
Demo(服务模块2)
然后我不应该拿这个整数瞎糊弄了,我应该换成日期计算器,它应该具备以下功能:
- 可以把日期转换成可计算的整数,好用数据库来筛对象
- 具备日期加减计算的功能,好设置日期
查到了以下资料:
这个时候我发现,如果想要实现日期计算,和数据库内通过比较时间筛数据,那么这条时间记录就最好是个「时间戳」格式,这种情况下,比较就会非常简单,但是要注意最后想要呈现出来的时候都要先转化一下,而且测试后发现用于日期计算的timedalta是在datetime函数中的,这个要注意引入进来。
而且我尝试着help(datetime.timedelta())结果发现:
| Data descriptors defined here:
|
| days
| Number of days.
|
| microseconds
| Number of microseconds (>= 0 and less than 1 second).
|
| seconds
| Number of seconds (>= 0 and less than 1 day).
这个只支持最粗到weeks和days,所以可能不好完成精确的月与年日期计算,好在这个服务上这个应该并不看重,先忽略掉。
然后还有一点,就是datetime.datetime.now()得到的是个时间元组,计算完成后还是需要把它额外转成时间戳才能入库,需要这两步:
>>> a=datetime.datetime.now()
>>> time.mktime(a.timetuple())
1449939085.0
首先获取当前时间时间戳是:
>>> a=datetime.datetime.now()
然后同步频率设置heatbeatrate,延迟heatbeatdelay
heatbeatrate=7
heatbeatdelay=7
获取下次同步时间heatbeatSync与过期时间
b = a + timedelta(days=heatbeatrate)
c = a + timedelta(days=heatbeatrate+heatbeatdelay)
最后都将其变成时间戳
heatbeatUpdate=time.mktime(a.timetuple())
heatbeatSync=time.mktime(b.timetuple())
heatbeatFinal=time.mktime(c.timetuple())
print a
print b
print c
测试一下,很顺利:
>>> a=datetime.now()
>>> heatbeatrate=7
>>> heatbeatdelay=7
>>> b = a + timedelta(days=heatbeatrate)
>>> c = a + timedelta(days=heatbeatrate+heatbeatdelay)
>>> b
datetime.datetime(2015, 12, 22, 0, 20, 40, 850559)
>>> c
datetime.datetime(2015, 12, 29, 0, 20, 40, 850559)
>>> heatbeatUpdate=time.mktime(a.timetuple())
>>> heatbeatSync=time.mktime(b.timetuple())
>>> heatbeatFinal=time.mktime(c.timetuple())
>>> print a
2015-12-15 00:20:40.850559
>>> print b
2015-12-22 00:20:40.850559
>>> print c
2015-12-29 00:20:40.850559
>>> print heatbeatUpdate
1450110040.0
>>> print heatbeatSync
1450714840.0
>>> print heatbeatFinal
1451319640.0