在python字典组成的数组中,按照其中的字典的一个key的值进行排序

对接海康的系统,取出客流信息后,发现返回的结果是乱序的, 并没有按照时间先后顺序排列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
[
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 28,
"flowOutNum": 17,
"holdValue": 0,
"createTime": 1622070960029,
"updateTime": 1622073660035,
"statTime": "2021-05-27T07:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 25,
"flowOutNum": 44,
"holdValue": 0,
"createTime": 1622103360028,
"updateTime": 1622106060028,
"statTime": "2021-05-27T16:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 40,
"flowOutNum": 33,
"holdValue": 0,
"createTime": 1622078160028,
"updateTime": 1622080920032,
"statTime": "2021-05-27T09:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 33,
"flowOutNum": 41,
"holdValue": 0,
"createTime": 1622081760029,
"updateTime": 1622084460028,
"statTime": "2021-05-27T10:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 28,
"flowOutNum": 30,
"holdValue": 0,
"createTime": 1622096160028,
"updateTime": 1622098860029,
"statTime": "2021-05-27T14:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 32,
"flowOutNum": 24,
"holdValue": 0,
"createTime": 1622092560213,
"updateTime": 1622095260028,
"statTime": "2021-05-27T13:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 0,
"flowOutNum": 0,
"holdValue": 0,
"createTime": 1622045760027,
"updateTime": 1622048460027,
"statTime": "2021-05-27T00:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 0,
"flowOutNum": 0,
"holdValue": 0,
"createTime": 1622056560027,
"updateTime": 1622059260043,
"statTime": "2021-05-27T03:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 0,
"flowOutNum": 0,
"holdValue": 0,
"createTime": 1622049360031,
"updateTime": 1622052060029,
"statTime": "2021-05-27T01:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 15,
"flowOutNum": 21,
"holdValue": 0,
"createTime": 1622063760027,
"updateTime": 1622066460027,
"statTime": "2021-05-27T05:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 29,
"flowOutNum": 19,
"holdValue": 0,
"createTime": 1622067360029,
"updateTime": 1622070060027,
"statTime": "2021-05-27T06:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 0,
"flowOutNum": 0,
"holdValue": 0,
"createTime": 1622052960029,
"updateTime": 1622055660027,
"statTime": "2021-05-27T02:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 0,
"flowOutNum": 2,
"holdValue": 0,
"createTime": 1622060160028,
"updateTime": 1622062861776,
"statTime": "2021-05-27T04:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 30,
"flowOutNum": 28,
"holdValue": 0,
"createTime": 1622074560035,
"updateTime": 1622077260037,
"statTime": "2021-05-27T08:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 19,
"flowOutNum": 51,
"holdValue": 0,
"createTime": 1622085360028,
"updateTime": 1622088060029,
"statTime": "2021-05-27T11:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 27,
"flowOutNum": 23,
"holdValue": 0,
"createTime": 1622088960033,
"updateTime": 1622091660029,
"statTime": "2021-05-27T12:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 16,
"flowOutNum": 21,
"holdValue": 0,
"createTime": 1622099760030,
"updateTime": 1622102460029,
"statTime": "2021-05-27T15:00:00.000+08:00"
}
]

Python的 sorted 内置函数提供了一个lambda 参数可以对它排序。

1
2
data = hik_http_request(url, body).json()['data']['list']
return sorted(data, key=lambda e: e.__getitem__('createTime'))

排序后的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
[
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 0,
"flowOutNum": 0,
"holdValue": 0,
"createTime": 1622045760027,
"updateTime": 1622048460027,
"statTime": "2021-05-27T00:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 0,
"flowOutNum": 0,
"holdValue": 0,
"createTime": 1622049360031,
"updateTime": 1622052060029,
"statTime": "2021-05-27T01:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 0,
"flowOutNum": 0,
"holdValue": 0,
"createTime": 1622052960029,
"updateTime": 1622055660027,
"statTime": "2021-05-27T02:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 0,
"flowOutNum": 0,
"holdValue": 0,
"createTime": 1622056560027,
"updateTime": 1622059260043,
"statTime": "2021-05-27T03:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 0,
"flowOutNum": 2,
"holdValue": 0,
"createTime": 1622060160028,
"updateTime": 1622062861776,
"statTime": "2021-05-27T04:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 15,
"flowOutNum": 21,
"holdValue": 0,
"createTime": 1622063760027,
"updateTime": 1622066460027,
"statTime": "2021-05-27T05:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 29,
"flowOutNum": 19,
"holdValue": 0,
"createTime": 1622067360029,
"updateTime": 1622070060027,
"statTime": "2021-05-27T06:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 28,
"flowOutNum": 17,
"holdValue": 0,
"createTime": 1622070960029,
"updateTime": 1622073660035,
"statTime": "2021-05-27T07:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 30,
"flowOutNum": 28,
"holdValue": 0,
"createTime": 1622074560035,
"updateTime": 1622077260037,
"statTime": "2021-05-27T08:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 40,
"flowOutNum": 33,
"holdValue": 0,
"createTime": 1622078160028,
"updateTime": 1622080920032,
"statTime": "2021-05-27T09:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 33,
"flowOutNum": 41,
"holdValue": 0,
"createTime": 1622081760029,
"updateTime": 1622084460028,
"statTime": "2021-05-27T10:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 19,
"flowOutNum": 51,
"holdValue": 0,
"createTime": 1622085360028,
"updateTime": 1622088060029,
"statTime": "2021-05-27T11:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 27,
"flowOutNum": 23,
"holdValue": 0,
"createTime": 1622088960033,
"updateTime": 1622091660029,
"statTime": "2021-05-27T12:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 32,
"flowOutNum": 24,
"holdValue": 0,
"createTime": 1622092560213,
"updateTime": 1622095260028,
"statTime": "2021-05-27T13:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 28,
"flowOutNum": 30,
"holdValue": 0,
"createTime": 1622096160028,
"updateTime": 1622098860029,
"statTime": "2021-05-27T14:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 16,
"flowOutNum": 21,
"holdValue": 0,
"createTime": 1622099760030,
"updateTime": 1622102460029,
"statTime": "2021-05-27T15:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 25,
"flowOutNum": 44,
"holdValue": 0,
"createTime": 1622103360028,
"updateTime": 1622106060028,
"statTime": "2021-05-27T16:00:00.000+08:00"
},
{
"groupId": "b5900064-b435-4886-8061-2ba39148c7c6",
"groupName": "主入口",
"flowInNum": 7,
"flowOutNum": 8,
"holdValue": 0,
"createTime": 1622106960027,
"updateTime": 1622106960027,
"statTime": "2021-05-27T17:00:00.000+08:00"
}
]

使用layui时,由jinja2动态生成的checkbox,如何在赋值的时候选中

官网的例子 https://www.layui.com/demo/form.html

1
2
3
4
5
6
7
8
9
10
11
<div class="layui-input-block">
<input type="checkbox" name="like[write]" title="写作">
<input type="checkbox" name="like[read]" title="阅读" checked="">
<input type="checkbox" name="like[game]" title="游戏">
</div>

layui.$('#LAY-component-form-setval').on('click', function(){
form.val('example', {
"like[write]": true //复选框选中状态
});
});

如果此时input是动态生成的,比如这样:

1
2
3
4
5
<div class="layui-input-inline" style="width:260px">
{% for role in roles %}
<input type="checkbox" name="role[{{ role.role_name }}]" value="{{ role.role_name }}" title="{{ role.role_name }}">
{% endfor %}
</div>

如果用这样的写法, 发现不好用,在浏览器里debug后发现,key被直接当错字符串传下去了,并没有翻译成真正的值,真是坑。

1
2
3
4
5
6
7
8
var arr = data['roles'].split('#');
$("input").prop("checked",false);
$.each( arr, function( index, value ) {
key = "role["+value+"]";
form.val('example', {
key: true //复选框选中状态
});
});

所以只能改成这样的写法,用选择器点击上,但是样式没有发生变化,检查页面发现,checkbox的按钮实际上已经显示checked了。

1
2
3
4
5
6
var arr = data['roles'].split('#');
$("input").prop("checked",false);
$.each( arr, function( index, value ) {
key = "role["+value+"]";
$("input[name='"+key+"']").prop("checked",true);
});

查了文档后发现,只要重新渲染一下就可以了。

https://www.layui.com/doc/base/faq.html#form

1
form.render('checkbox');

飞天信诚软件锁 rockey arm 在web环境下开发记录

需求:

开发了一套web的系统,服务器端用的Python实现,现在需要客户用浏览器登录的时候检查客户电脑上是否插上了软件锁加密狗,如果有则继续登录,如果没有果断拒绝登录。

硬件采购:

最后选择了飞天信诚的rockey ram,淘宝评价看起来不错。
https://www.ftsafe.com.cn/products/rockey/ROCKEY-ARM
从售前获取到的开发包是Rockey1Smart–V1.0007-20180201。

工作原理

加密狗支持一系列的加密算法,当前的加密狗版本是内置RSA、ECC、DES、SHA1和国密算法(SM2、SM3、SM4)等高数据加密算法,我们这里直接选中RSA。
加密狗提供管理软件和接口,允许我们写入需要的对应各种算法的密钥,同时又提供接口供我们调用,传入明文,就会得到密文。同时加密狗又能保证我们写入的密钥是完全不可以读出来的,这样就保证了私钥的安全。
在需要验证加密狗的时候,比如登录的时候,我们先从服务器端申请一个10位的随机字符串,然后在浏览器调用JS API获取到加密狗计算的密文,然后把密文和账号密码一同发往服务器,服务用公钥和存在session里的随机字符串去验证这段密文是否有效,如果是就正常返回逻辑。
另外如果服务器也需要保护的话,服务器上也插上软件锁加密狗,但是加密狗里存的是公钥,服务器端验证的时候直接调用加密狗的算法去验证,而不是自己算,这样就保证了公钥的安全。

开发工作:

准备部分

  1. 客户端OS装上一个EXE的插件,插件在开发包里的位置sdk\Extends\Samples\Multi-Browser\JavaScript\JSRockeyArmWebSocketSetup_x64.exe, 或者JSRockeyArmWebSocketSetup.exe。
  2. 据观察这个插件安装完成后会启动一个websocket服务,运行在浏览器里的js代码会发消息给这个websocket服务,然后由这个websocket服务去跟软件锁加密狗沟通,所以这个插件按照设计应该是可以跨浏览器的。
  3. 使用sdk\Tools\RyARMTool.exe配置软件锁加密狗,点击左侧密码安全,点中间的“生成”按钮,进行唯一化操作,牢记管理员密码!!!如果丢了,只能返厂了。据观察同一批的密码锁生成的ID和管理员密码是一样的,所以记得退出后重新登录,然后回到密码安全,然后修改一下管理员密码。
  4. 选择“文件管理”,“文件类型”选“RAS私钥文件”,然后“生成”做出的两个按钮,分别给公钥私钥文件命个名,比如dog.Rsapri和dog.Rsapub,然后点击生成,就产生了一对密钥。
  5. 点“创建”按钮,填入文件ID“0002”,创建, 这个对应后面js文件里RsaPriFileID。
  6. 点刚刚创建的文件,然后“写私钥”,选择刚刚创建的“dog.Rsapri”。
  7. 利用sdk\Tools\Rsa2PemTool.exe将刚刚生成的秘钥对转成可读的pem文件,注意选择“RSA到PEM”,得到dog.pri.pem和dog.pub.pem。注意后面要用到这个dog.pub.pem。
  8. 如果后期发现这个文件的顶部不是“—–BEGIN PUBLIC KEY—–”,而是多了一个RSA,需要在这个网站转成der,然后再把der转成pem,就可以了。https://www.ssleye.com/der_pem.html

前端代码

  1. 引入3个js文件
    1. sdk\Extends\Samples\Multi-Browser\JavaScript\base64.js
    2. sdk\Extends\Samples\Multi-Browser\JavaScript\test.js
    3. sdk\Extends\Samples\Multi-Browser\JavaScript\websocket.js
  2. 登录页面
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <div class="login-content">
    <div class="form">
    <img src="../static/images/login/avatar.svg">
    <h2 class="title"> </h2>
    <div style="min-height:50px;vertical-align:top;text-align;color:orange"><h5 id="feedback"></h5></div>
    <div class="input-div one">
    <div class="i">
    <i class="fa fa-user"></i>
    </div>
    <div class="div">
    <h5>用户名</h5>
    <input id="login-user" type="text" class="input">
    </div>
    </div>
    <div class="input-div pass">
    <div class="i">
    <i class="fa fa-lock"></i>
    </div>
    <div class="div">
    <h5>密码</h5>
    <input id= "login-pass" type="password" class="input">
    </div>
    </div>
    <input id="login-button" type="submit" class="btn" value="登录">
    </div>
    </div>
  3. JS逻辑, 页面打开的时候就去检查加密狗状态,登录按钮触发后再去调用加密接口。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    var ctrl = null;
    var websock = true;
    var b = new Base64();
    var ArmHandle; //加密锁句柄


    function message(status, shake = false, id = "") {
    if (shake) {
    $("#" + id).effect("shake", {
    direction: "right",
    times: 2,
    distance: 8
    }, 250);
    }
    document.getElementById("feedback").innerHTML = status;
    $("#feedback").show().delay(3000).fadeOut();
    }

    //从服务器端获取安全码
    var get_safe_code = function () {
    $.post({
    type: "GET",
    url: "/api/auth/login",
    error(response){
    message('错误:无法连接服务器,请稍后重试。',true, "signup-box");
    },
    success(response) {
    //调用加密算法
    Arm_RsaPri(response['safe_code'])
    }
    });

    };

    var login = function(ras_code){
    $.post({
    type: "POST",
    url: "/api/auth/login",
    data: {
    "username": $("#login-user").val(),
    "password": $("#login-pass").val(),
    "rsa_code": ras_code
    },
    error(response){
    message('错误:用户名或者密码错',true, "signup-box");
    },
    success(response) {
    window.location.href = '/home'
    }
    });
    }

    function checkSafeDog(){
    try{
    ctrl = new AtlCtrlForRockeyArm("{33020048-3E6B-40BE-A1D4-35577F57BF14}");
    }catch (e){
    ctrl = null;
    websock = false;
    }
    ctrl.ready(function(){
    ctrl.Arm_Enum(function(result, response){
    if (!result){
    alert("Arm_Enum error. " + response);
    }else{
    rtn = response;
    // alert("找到锁的个数为:" + rtn);
    }
    });
    Index = 0;
    ctrl.Arm_Open(function(result, response){
    if (!result){
    alert("Arm_Open error. " + response);
    }else{
    ArmHandle = response;
    console.log("加密锁句柄为:" + ArmHandle);
    if (parseInt(ArmHandle) < 0){
    message('错误:加密狗连接失败,请插入后刷新。',true, "signup-box");
    }else{
    message('加密狗连接成功')
    }

    }
    });
    });
    }



    //Rsa私钥运算
    function Arm_RsaPri(code){
    Handle = ArmHandle;
    RsaPriFileID = 0002;
    RsaPriFileSize = 1024; //文件ID为RsaPriFileID的位数,取值为1024或2048
    RsaPri_Flag = 0;
    RsaPriInData = b.encode(code);

    ctrl.Arm_RsaPri(function(result,response){
    if (!result){
    alert("Arm_RsaPri error. " + response);
    return null;
    }else{
    //私钥加密后的数据经过了base64编码传出
    RsaPriData = response;
    console.log("密文:" + RsaPriData);
    if (parseInt(RsaPriData) < 0 ){
    message('错误:加密狗连接失败,请插入后刷新。',true, "signup-box");
    }else{
    //调用登录
    login(RsaPriData)
    }
    }
    });

    }

    $(document).ready(function () {
    // 添加登录时间给登录按钮
    $(document).on("click", "#login-button", get_safe_code);
    $(document).keypress(function (e) {
    if (e.which === 13) {
    get_safe_code();
    }
    });
    //页面启动的时候检查加密狗状态
    checkSafeDog();
    });

服务端

  1. Python API。这个地方比较坑的是,python的RSA加密库有好几个版本,尝试了所有,发现都无法正常用公钥验证,怀疑和证书的版本有关系,最后没办法选择使用PHP来搞定。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    @ns.route('/login')
    class Userlogin(Resource):
    def post(self):
    form = user_form.UserForm(request.form)
    if form.validate():
    username = request.form['username'].lower()
    password = request.form['password']
    rsa_code = request.form['rsa_code']
    if rsa_code:
    result = check_rsa_code(rsa_code)
    if not (result and 'safe_code' in result and result['safe_code'] == session['safe_code']):
    return json_failed('加密狗密文校验失败')
    else:
    return json_failed('需要加密狗密文字段')
    flag, token = user_dao.credentials_valid(username, password)
    if flag:
    return json_success(token=token)
    else:
    return json_failed('用户名或者密码错')
    else:
    return json_failed()

    # 返回安全码
    def get(self):
    safe_code = ''.join(str(random.choice(range(10))) for _ in range(10))
    session['safe_code'] = safe_code #放入session用于验证
    return json_success(safe_code=safe_code)


    def check_rsa_code(rsa_code):
    check_url = "http://<php_server_ip_port>/decode.php?encrypted='%s'" % rsa_code
    act = getattr(requests, 'post')
    res = act(check_url).json()
    return res

    def json_success(msg='', **kwargs, ):
    if "code" not in kwargs:
    code = 200
    else:
    code = kwargs['code']
    return dict({'status': 'success', 'msg': msg}, **kwargs), code


    def json_failed(msg='', **kwargs):
    if "code" not in kwargs:
    code = 500
    else:
    code = kwargs['code']
    return dict({'status': 'failed', 'msg': msg}, **kwargs), code
  2. PHP代码,第4步中的check_url指向的服务器,建议直接使用phpstudy的一键部署PHP环境。
    1
    yum install -y wget && wget -O install.sh https://notdocker.xp.cn/install.sh && sh install.sh
    把下面的代码放入/www/admin/localhost_80/wwwroot/decode.php, 其中的$public_key来自dog.pub.pem。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <?php

    $public_key = '-----BEGIN PUBLIC KEY-----
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxx
    -----END PUBLIC KEY-----';

    $pu_key = openssl_pkey_get_public($public_key);

    $encrypted=rawurldecode(urlencode(urldecode($_GET['encrypted'])));

    $decrypted = "";

    openssl_public_decrypt(base64_decode($encrypted),$decrypted,$pu_key);

    header('Content-Type:text/json;charset=utf-8');
    $str = array
    (
    'safe_code'=>$decrypted
    );

    $jsonencode = json_encode($str);
    echo $jsonencode;
    ?>

后期工作

现在只是在登录界面进行了验证,理论上在Token等授权有效期内,如果用户不访问登录界面,加密狗就不会被验证,所以加密狗是可以被拔下来在其他的电脑上登录的。
后面可以添加如下的功能去预防这种情况。

  1. 每次API访问后,服务端在after_request拦截器里都更新一下session里的安全码,并把安全码放入到reponse的header里。
  2. 浏览器收到response后,把安全码放入到cookie中。
  3. 再次发起请求时,把cookie里的安全码进行加密并放到request的header里发送出去。
  4. 服务端在before_request拦截器里取出header里的密文进行验证。

这样基本上就能实现时刻验证加密狗的存在了,但是应该会损失一下效率,在客户端不多的情况下,完全可以使用。

安全提取字符串中的数字

去除输入中的字母和其他字符,只留下数字

1
2
3
4
5
data = filter(lambda ch: ch in '0123456789.', str(data))
if len(list(data)) > 0:
return float(str(''.join(list(data))))
else:
return 0.0

一个从天天基金网上爬取信息的工具

给朋友帮忙,需要根据天天基金–场内交易基金净值折价率一览表把每个基金的前十名的股票持仓信息做到同一个表格中,用于后面的基金分析和挑选。

列表: http://fund.eastmoney.com/cnjy_jzzzl.html

总结几点:

  1. Python操作Excel还是略有些复杂,尤其是需要在表格中加入超链接,如果目的最后是为了得到表格,那么可以把数据生成一个网页table,然后直接拷贝到表格中,数据会很规整。
  2. Chrmoet的debug工具中,可以直接选中DOM元素,然后右键拷贝它的XPAHT。
  3. Python的lxml包在解析xpath时是无法识别tbody标签的,所以如果拷贝出来的XPATH如果有tbody,一定要删掉,否则解析的结果是[]。
  4. 天天基金网的网页的元素会因为不同的基金导致取出来的元素的XPATH略有不同,真是坑。

代码地址: https://github.com/skeetwu/ETF

PyCharm 路径符号从斜线变成了人民币符号

PyCharm 路径符号从斜线\变成了人民币¥符号, 这个应该是字体的原因引起的。

修改方法:

  1. Setting – Appearance – Override default fond by (not recommmended), 去掉选中。
  2. Setting – Edit – Font – Show only monospaced fonts (选中) – 字体推荐(Source Code Pro)。

这样应该就可以正常的显示路径了。

2020 end

一场疫情,改变了太多!
所在的部门,从全site最有前途的组一下子变成了最风雨飘摇的组。上半年忙死,下半年组里动荡,大家都接近于划水摸鱼的状态。
但是不管无论未来如何,都要把自己收拾利索,无论何时都要有能力去面对新的工作挑战。


年终统计,CSFS ticket总数373,刷出了一个破纪录的数据,开心,完结撒花。
goodbye 2020, good luck 2021!

统计
小组排名

Ansible从一个target node取值并赋值给所有的target node

Ansible操作三个node,其中有一个pod(app_api_pod_name)运行其中的一个上。
Step 1 需要判断这个pod中有没有存在一个文件,这个地方希望只在第一个node1上执行就可以了,这样其他的node都会skip,
如果是这样skip其他的node的时候,仍然会进行赋值,导致最后的结果是CA_exist无法registe,除了node1,其他node拿到CA_exist都是空值。
所以这个地方就理解了,ansible的所有变量,包括常量和临时变量都是按node进行区分和隔离的,不能共享。

Step 2 就是用来解决这个问题,在其他的node的变量组把这个值给赋值上,关键取值 hostvars[groups[‘role_control’][0]].CA_exist.rc

1
2
3
4
5
6
7
8
9
10
11
12
13

- name: check if CA exist in app-api
become: yes
shell: "kubectl exec -it {{ app_api_pod_name.stdout_lines[0] }} cat /etc/pki/ca-trust/source/anchors/xxxca.pem -n xxxx"
ignore_errors: yes
register: CA_exist
when: "inventory_hostname == groups['role_control'][0] and app_api_pod_name is defined"

- name: set if need to copy CA
set_fact:
need_copy: "{{ hostvars[groups['role_control'][0]].CA_exist.rc }}"
when: "hostvars[groups['role_control'][0]].CA_exist is defined and hostvars[groups['role_control'][0]].CA_exist.rc == 1"