开发环境
操作系统:Windows 10 Pro
数据库:Mysql
服务器:Django
语言:Python3.6 + html + JavaScript + css
3.3 用户环境
Windows、Mac、Linux、Android、IOS,通过浏览器访问。支持的浏览器为 IE、火狐、谷歌浏览器。
3.4 业务数据流

3.5 功能模块划分

3.6 功能模块定义
3.6.1 管理员模块
管理员模块需要经过银行管理员登陆并认证后操作。管理员可以审核用户注册、获取用户异常,并根据异常信息冻结账户,所有的操作记录数据库。
3.6.2 用户操作
用户包括电商用户和普通用户,都可以进行状态修改,比如修改个人信息、修改密码。有敏感操作时需要转到认证模块作安全认证。
3.6.3 身份认证
银行系统的敏感操作之前需要有安全认证。验证内容包括数字证书是否真实、是否过期,登陆状态是否正常。
3.6.4 订单审核
执行 SET 协议,判断订单信息是否真实,再执行支付操作,转账信息写入数据库,并更改账户余额。
3.6.5 通信模块
电商平台根据提供的支付链接转到银行系统,此后双方执行类似于 SSH 的协议互相认证身份并协商出临时对称密钥,用于加密之后的通信。
3.7 功能模块划分
3.7.1 用户基本信息表
Users
字段 | 类型 | 长度 | 是否主键 | 是否非空 | 备注 |
uID | int | 32 | 是 | 是 | 用户 ID |
uName | Varchar | 32 | 否 | 是 | 用户姓名 |
Uphone | Varchar | 32 | 否 | 是 | 用户手机号 |
Uemail | Varchar | 32 | 否 | 是 | 用户邮箱 |
uPassword | Varchar | 128 | 否 | 是 | 登陆密码 |
uPaypasswd | Varchar | 128 | 否 | 是 | 支付密码 |
Time | Date | 否 | 是 | 创建时间 | |
Status | Varchar | 32 | 否 | 否 | 用户状态 |
3.7.2 管理员基本信息表
Admins
字段 | 类型 | 长度 | 是否主键 | 是否非空 | 备注 |
aID | int | 32 | 是 | 是 | 管理员 ID |
aName | Varchar | 32 | 否 | 是 | 用户姓名 |
aPassword | Varchar | 64 | 否 | 是 | 用户密码 |
3.7.3 转账信息
Transfer
字段 | 类型 | 长度 | 是否主键 | 是否非空 | 外键 | 备注 |
Serial | int | 32 | 是 | 是 | 流水号 | |
Suser | int | 32 | 否 | 是 | Users | 发起者 ID |
Duser | int | 32 | 否 | 是 | Users | 接收者 ID |
Scard | int | 32 | 否 | 是 | Crashcard | |
dcard | int | 32 | 否 | 是 | Crashcard | |
mID | Varchar | 32 | 否 | 否 | 电商 ID | |
Amount | Float | 否 | 是 | 金额 | ||
sBalance | Float | 否 | 是 | 发起者本次余额 | ||
dBalance | Float | 否 | 是 | 接收者本次余额 | ||
Time | Date | 否 | 是 | 日期 | ||
Remark | Varchar | 128 | 否 | 否 | 备注 |
3.7.4 银行卡信息
字段 | 类型 | 长度 | 是否主键 | 是否非空 | 外键 | 备注 |
kID | int | 是 | 是 | 银行卡号 | ||
Balance | Varchar | 32 | 否 | 否 | 余额 | |
User | int | 否 | 是 | Users | 用户 id | |
Time | Date | 否 | 是 | 创建时间 | ||
Status | Varchar | 32 | 否 | 否 | 卡状态 |
3.7.5 存取款信息
字段 | 类型 | 长度 | 是否主键 | 是否非空 | 外键 | 备注 |
Serial | int | 是 | 是 | 流水号 | ||
User | int | 否 | 是 | Users | 用户 ID | |
Amount | Varchar | 32 | 否 | 是 | 存取款金额 | |
Balance | Float | 否 | 是 | 用户本次剩余金额 | ||
Type | Varchar | 8 | 否 | 是 | 存/取 | |
Datafrom | Varchar | 32 | 否 | 否 | 存取款来源 | |
Time | Date | 否 | 是 | 日期 |
4.详细设计
4.1 注册
4.1.1 页面设计


4.1.2 实现方法
输入地址 127.0.0.1:8001,自动跳转到登陆页面,点击注册按钮进入注册页面。
传输安全:将 CA 服务器提供的证书放到 html 页面,用户客户端提交表单时首先提取证书中的公钥字段,利用公钥加密表单,传输到服务器。服务器接收到信息后首先用私钥解密,再执行下一步操作。
前端加密 Signup.html
<script>
functionformAck(){
alert("success");
varcert=document.getElementById("cert").value;#从html中获取证书
varcertInfo=JSON.parse(cert).info.certInfo
varpubKey=JSON.parse(certInfo).PublicKey#从证书中获取公钥
varencrypt=newJSEncrypt();
encrypt.setPublicKey(pubKey);
vardata={
name:$('#name').val(),
idcard:encrypt.encrypt($('#idcard').val(),true),
phone:encrypt.encrypt($('#phone').val(),true),
email:encrypt.encrypt($('#email').val(),true),
passwd:encrypt.encrypt($('#passwd').val(),true),
paypasswd:encrypt.encrypt($('#paypasswd').val(),true)
};
console.log(data)
$.ajax({
url:"/signup/",
type:"POST",
data:data,
success:function(data1){
alert(data1)
window.location.href="/login/"
}
})
}</script>
后端加密 Views.py
defsignup(request):
ifrequest.method=="GET":
returnrender(request,"signup.html")
ifrequest.method=="POST":
name=request.POST.get("name",None)
idcard=tools.DecodeDecrypt(request.POST.get("idcard",None)).decode()
phone=tools.DecodeDecrypt(request.POST.get("phone",None)).decode()
email=tools.DecodeDecrypt(request.POST.get("email",None)).decode()
passwd=tools.DecodeDecrypt(request.POST.get("passwd",None)).decode()
paypasswd=tools.DecodeDecrypt(request.POST.get("paypasswd",None)).decode()
数据库存储密码:登陆密码和支付密码不能直接存入数据库,而是存加 salt 的哈希值。

4.2 登陆
4.2.1 页面设计

4.2.2 实现方法
用户点击登陆后,客户端向服务器发送一个 post 请求,包含 公钥加密后的ID 和登陆密码。服务器接收后,用系统私钥解密,解密成功后:
验证登陆密码的加 salt 哈希和数据库存储是否一致,一致的话执行下一步操作,不一致返回登陆界面
服务器设置 session,添加用户 ID 和 name 字段,之后每次操作验证登陆信息时,检查这两个 session 值是否存在即可
服务器返回“success“字段,客户端接收后执行 href="/viewuserinf/" ,执行信息查看操作
传输安全:用户在登陆界面提交的表单,前端用公钥加密,服务器用私钥解密,具体操作同注册的传输安全
4.3 信息查看
4.3.1 页面设计

4.3.2 实现方法
请求查看页面的请求类型全部为 get 型,url 为 127.0.0.1:8001/viewuserinf/,服务器接收到该请求后,首先验证session 的 id 字段是否存在,存在才可继续查看,不存在则清楚所有 session 并返回登陆界面。
Views.py
defviewuserinf(request):
ifrequest.method=="GET":
try:
id_session=request.session["id"]
tmp=Users.objects.get(id=int(id_session))
returnrender(request,"user_inf_show.html",{"inf":tmp})
except:
returnrender(request,"login.html")
returnrender(request,"login.html")
4.4信息修改
4.4.1 页面设计

4.4.2 实现方法
由于用户姓名、身份证号是认证信息,不可修改,登陆密码和支付密码不在此模块。所以这里只能修改手机号和电子邮箱。
获取信息修改页面的请求方式是 get,用户点击“信息修改”按钮或者直接在 浏 览 器 地 址 栏 中 输 入 url : http://127.0.0.1:8001/edituserinf/ ,就会返回信息修改页面。
信息修改的表单提交使用 post 请求,用户输入的表单信息用公钥加密后传输,服务器接收后用私钥解密,然后检查 session 的 id 字段是否存在,存在则修改成功,返回信息查看界面,否则返回登陆界面。
4.5 更改密码
4.5.1 页面设计


4.5.2 实现方法
获取修改密码界面的请求方式是 get,用户在登陆状态下,点击“修改密码”或 者 在 浏 览 器 地 址 栏 输 入 url:127.0.0.1:8001/editpasswd/获得界面。用户可选择登陆密码和支付密码,更改密码必须输入原密码。
表单提交的请求方式是 post 请求,客户端使用公钥加密表单,服务器接收请求后用私钥解密,之后:
检查服务器 session 中是否存在 id 字段,存在继续下一步,不存在返回登陆界面
MD5加盐计算密码哈希值,与数据库存储的哈希值对比,相等,执行下一步,否则返回信息修改界面重新提交
计算新密码的加 salt 哈希值,存入数据库。修改完成后,如果是修改支付密码,返回信息查看界面,如果是修改登陆密码,删除 session 后返回登陆界面
安全设计 :客户端公钥加密传输表单,客户端私钥解密,保证传输安全。修改密码必须验证原密码,并且密码只存储哈希值,保证密码数据库安全。
4.6 银行卡管理
4.6.1 页面设计
查看所有个人银行卡:

添加银行卡:
4.6.2 实现方法
用户获取银行卡管理界面的请求方式为 get,在登陆状态下点击“银行卡管理”或者 url 输入 127.0.0.1:8001/cardmanage/ 进入银行卡管理界面。服务器将银行卡信息返回给用户,每张银行卡信息包括卡号、建立时间、余额和状态。由于涉及到金额,银行卡一旦添加不可删除。
在银行卡管理模块可以选择添加银行卡,获取页面的请求方式仍是 get。获取到界面后,用户必须输入支付密码,点击确认添加,向服务器发送 post 请求,表单内容为 公钥加密后的支付密码。
服务器接收到请求后,首先检查 session 的 id 字段是否存在,存在执行下一步,否则删除全部 session 返回登陆界面。解密表单内容,获取明文支付密码,计算加 salt 哈希值,对比数据库支付密码哈希值,相同后即可分配银行卡。
银行卡添加成功后,分配一个唯一卡号,状态默认为正常,余额默认为 0。随后返回银行卡管理界面。
4.7 转账
4.7.1 页面管理


4.7.2 实现方法
在登录状态下,用户通过点击“转账”或者输入 url: 127.0.0.1:8001/transfer/向服务器发送 get 请求,进入转账界面。
用户选择本方卡号,输入对方卡号、转账金额、支付密码后确认转账,javascrpt使用公钥加密表单后传输给服务器。服务器接收并私钥解密后:
检查登陆状态,即检查 session 的 id 字段是否存在,不存在返回登陆界面,存在则进入下一步
计算用户输入的 paypasswd 的加 salt 哈希值,与数据库提取的密码哈希值对比,相同则进入下一步,不同则返回转账界面重新输入
将此转账信息写入数据库,再将对应的银行卡做余额更改。执行成功后进入转账查询界面。
安全:
客户端公钥加密后传输,防止旁路攻击
密码存哈希值,明文不出现数据库
转账记录表和银行卡表中记录的更改在确认转账可以执行后,才一起更改防止数据不同步
tmp1=Transfer(time=time,
scard=CrashCard.objects.get(id=scard),
dcard=CrashCard.objects.get(id=dcard),
suser=Users.objects.get(id=uid),
duser=Users.objects.get(id=CrashCard.objects.get(id=dcard).user.id),
amount=amount,
scbalance=CrashCard.objects.get(id=scard).balance-amount,
dcbalance=CrashCard.objects.get(id=dcard).balance+amount)CrashCard.objects.filter(id=scard).update(balance=scbalance-amount)CrashCard.objects.filter(id=dcard).update(balance=dcbalance+amount)tmp1.save()
4.8 存款
4.8.1 页面设计

4.8.2 实现方法
在登录状态下,用户点击“存款”或者输入 url: 127.0.0.1:8001/ userdeposit/ 向服务器发送 get 请求,进入存款界面。
在存款界面,输入存款卡号和金额,用 RSA 公钥加密表单,传输给服务器。
服务器解密后,获取卡号和金额,更改数据库银行卡表单,完成操作。返回存取款查询界面。
4.9 取款
4.9.1 页面设计

4.9.2 实现方法
取款操作和存款操作的实现方法类似。不同的内容为:
取款的 url 为 127.0.0.1:8001/userdraw/
取款需要验证支付密码,支付密码需要计算加 salt 哈希值与数据库存储哈希值对比
4.10 转账查询
4.10.1 页面设计

4.10.2 实现方法
在登录状态下,用户点击转账查询或者输入url: 127.0.0.1:8001/ viewtransfer/向服务器发送get请求,进入转账查询界面。
服务器检查登陆状态,即验证 session 的 id 字段是否存在。存在则将该用户的转账记录返回客户端。
4.11 存取款查询
4.11.1 页面设计

4.10.2 实现方法
在登录状态下,用户点击转账查询或者输入url:127.0.0.1:8001/ viewdrawdeposit/向服务器发送get请求,进入转账查询界面。
存取款查询的实现方法与转账查询方法基本相同。
5.结束语
常规网站的设计如果不考虑安全,那么网站能用但是风险极高
加密传输可使用 RSA 公钥前端加密,后端解密
本次项目的实现让我了解了很多信息安全的知识,增加了动手能力
本门课程收获很大