隨著互聯網的飛躍式發展,移動支付已越來越受歡迎并且已成為常態,很多三方支付公司推出了很多支付方式如快捷支付、認證支付、掃碼支付等等??旖葜Ц逗驼J證支付可分為移動app控件和移動HTML5網頁。用戶第一次使用快捷支付或認證支付進行支付的時候,需先綁定銀行卡。在綁定銀行卡的過程中,需要驗證銀行卡信息。不同銀行、不同銀行卡驗證的要素不一樣,有些需要驗證四要素,有的需要驗證八要素。對于需要驗證銀行卡的交易密碼的情況,怎樣保證交易密碼的安全不被別人所竊取呢?為了保證交易密碼不在傳輸過程中被竊取,出現了安全傳輸隨機數字密碼鍵盤。
安全傳輸隨機數字密碼鍵盤怎么實現呢?今天給大家詳細的介紹安全傳輸隨機數字密碼鍵盤的原理和代碼實現。下圖是實現的數
字鍵盤效果:
一、實現原理
用戶點擊“交易密碼”輸入框,頁面異步向后臺發送“獲取密碼鍵盤”的請求,后臺接收到請求之后隨機生成“1234567890與隨機密文的對應關系”和“隨機密文”和“1234567890圖片“的對應關系,然后把它們關系放入dto實例中并放入redis中,最后把隨機密文以集合的方式返回到頁面,頁面js獲取到密文集合后以循環的方式向后臺請求對應的數字圖片流,并展示在頁面。
當用戶點擊數字鍵盤中的數字圖片,就會把圖片對應的密文放入到pkey隱藏輸入框中,多個數字以逗號隔開,當點擊支付的時候,就會把peykey隱藏輸入框的值傳入到后臺,后臺從redis中取出“密文”與“1234567890數字“的對應關系,就取出了對應交易密碼。
二、具體實現
1).HTML5頁面
頁面主要展示密碼輸入框和支付按鈕,需要導入JQuery、bootstrap及pwdkey.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
|
<%@ page language="java" import="java.util.*" contentType="text/html; charset=UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ page pageEncoding="UTF-8"%> <% String path = request.getContextPath(); %> <!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd"> < html > < head > < meta name = "viewport" content = "width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" /> < meta http-equiv = "Cache-Control" CONTENT = "private,must-revalidate" > < link rel = "stylesheet" href='<c:url value = "/js/bootstrap/css/bootstrap.min.css" />'> <!-- 引入js腳本文件 begin --> <!--[if lt IE 9]> <script src="http://cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="http://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script> <![endif]--> < script src = "<c:url value=" /js/JQuery/jquery-1.10.0.min.js"/>"></ script > < script src = "<c:url value=" /js/bootstrap/js/bootstrap.min.js"/>"></ script > < script type = "text/javascript" src = "<c:url value=" /js/pwdkey.js"/>"></ script > < title >xxx付款</ title > < style type = "text/css" > .input-out { padding-top: 20px; } .btn-out { margin:30px 10px; } .btn-out button { width: 100%; background: #5CACEE; border: #5CACEE; color: #fff; height: 50px; border-radius: 3px; font-size: 18px; font-family: "Microsoft Yahei", "??????", SimHei, Tahoma, Arial, Helvetica, STHeiti; } .keyboard { background: #fff; } .keyboard table { width:100%; text-align:center; } .keyboard table td { padding: 15px; } .keyboard table a, .keyboard table a:hover, .keyboard table a:focus { color: #333; text-decoration: none; } .input-out label { color:#D2D1D1; font-weight: normal; font-size: 16px; } .bottom { color:#888888; margin-bottom: 15px; text-align:center; margin-top:100px; } .bottom a { color:#888888; } .bottom img { vertical-align: middle; width: 18px; } </ style > </ head > < body > < form action="<%=path%>/pay" method="post" id="from"> < div class = "content" > < div class = "input-out pass-label" style = "border-bottom: 1px solid #ddd;padding-left: 12px;" random = "2321321321" path="<%=path%>" > < label id = "pin" >交易密碼</ label > </ div > </ div > < div class = "btn-out" > < button type = "button" class = "btn btn-default" ontouchstart = "check();" id = "pay" >支付</ button > </ div > </ form > < div class = "bottom" id = "bottom-out" > < img src = "<c:url value=" /images/phone.png"/>" /> < span >客服電話:4000-xxx-xxx</ span > </ div > <!-- jianpan--> < div class = "keyboard" style = "display:none;" id = "keyboard" > < table class = "table-bordered" id = "key_table" > </ table > </ div > </ body > </ html > |
2).密碼鍵盤js代碼
用戶點擊“交易密碼”輸入框,頁面異步向后臺發送“獲取密碼鍵盤”的請求,后臺接收到請求之后把隨機密文以集合的方式返回到頁面,頁面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
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
$(document).ready( function () { $( "#pay" ).removeAttr( "disabled" ); $( "input" ).click( function () { hideKey(); }); $( "button" ).click( function () { hideKey(); }); $( ".pass-label" ).click( function () { var rangdom = $( this ).attr( "random" ); var path = $( this ).attr( "path" ); pwdkey( this , rangdom, path); }); window.addEventListener( "onorientationchange" in window ? "orientationchange" : "resize" , hengshuping, false ); }); function hengshuping() { if (window.orientation == 180 || window.orientation == 0) { $( "div#keyboard td" ).each( function () { $( this ).css( "padding" , "15px" ); }); } if (window.orientation == 90 || window.orientation == -90) { $( "div#keyboard td" ).each( function () { $( this ).css( "padding" , "8px" ); }); } window.scrollTo(0, $( ".pass-label" ).offset().top); } function pwdkey(obj, rangdom, path) { $( '.keyboard' ).addClass( "navbar-fixed-bottom" ); $( '.keyboard' ).css({ "z-index" : "9999" }); if (rangdom == null || rangdom == "" ) { alert( "無法加載密碼鍵盤,請刷新后重試!" ); return false ; } if ($( "#pkey" ).val() == null || $( "#pkey" ).val() == "undefined" ) { $(obj) .html( $(obj).html() + '<input type="hidden" name="pkey" id="pkey" />' ); } $( "#pin" ).html( "交易密碼" ); setCssNomal(); $( "#pkey" ).val( "" ); $ .ajax({ type : 'post' , url : path + "/common/pkey.do" , cache : false , async : false , data : { rangdom : rangdom }, success : function (data) { if (data == null || data == "" || data == "undefined" || data.length != 10) { alert( "無法加載密碼鍵盤,請刷新后重試!" ); return false ; } else { var key_table = $( "#key_table" ); key_table.html( "" ); var content = '<tr>' ; for ( var i = 0; i < 12; i++) { if ((i + 1) % 3 == 0 && i != 0 && i <= 5) { content = content + '<td style="width:33%;" key="' + data[i] + '" ontouchstart="return ontouch(this);"><img src="' + path + '/common/getKey.do?key=' + data[i] + '&rangdom=' + rangdom + '" style="height:20px;" id="key_img"/></td></tr><tr>' ; } else if (i <= 7) { content = content + '<td style="width:33%;" key="' + data[i] + '" ontouchstart="return ontouch(this);"><img src="' + path + '/common/getKey.do?key=' + data[i] + '&rangdom=' + rangdom + '" style="height:20px;" id="key_img"/></td>' ; } else if (i == 8) { content = content + '<td style="width:33%;" ontouchstart="return deleteOne();"><img src="' + path + '/images/keys/delete.png" style="height:20px;" id="key_img"/></td>' + '</tr><tr>' ; } else if (i < 11) { content = content + '<td style="width:33%;" key="' + data[i - 1] + '" ontouchstart="return ontouch(this);"><img src="' + path + '/common/getKey.do?key=' + data[i - 1] + '&rangdom=' + rangdom + '" style="height:20px;" id="key_img"/></td>' ; } else { content = content + '<td style="width:33%;" onclick="return _ok();"><img src="' + path + '/images/keys/ok.png" style="height:20px;" id="key_img"/></td>' + '</tr>' ; } } key_table.html(content); setTimeout( function () { $( "#keyboard" ).show(); }, 600); hengshuping(); } }, error : function () { alert( "無法加載鍵盤,請刷新后重試!" ); } }); } function ontouch(obj) { var pkey = $( "#pkey" ).val(); var key = $(obj).attr( "key" ); if (pkey == "" ) { $( "#pin" ).html( "" ); } var content = $( "#pin" ).html(); if (content != "" && content.length >= 6) { return false ; } if (pkey != "" ) { key = "," + key; } pkey = pkey + key; $( "#pkey" ).val(pkey); $( "#pin" ).append( "*" ); setCssKey(); } function deleteOne() { var pkey = $( "#pkey" ).val() + "" ; if (pkey == "" ) { return false ; } var local = pkey.lastIndexOf( "," ); if (local == -1) { $( "#pkey" ).val( "" ); $( "#pin" ).html( "交易密碼" ); setCssNomal(); } else { pkey = pkey.substring(0, local - 1); var content = $( "#pin" ).html(); content = content.substring(0, content.length - 1); $( "#pkey" ).val(pkey); $( "#pin" ).html(content); } } function _ok() { $( "#key_table" ).html( "" ); $( "#keyboard" ).hide(); } function showkey() { $( "#keyboard" ).show(); } function hideKey() { $( "#key_table" ).html( "" ); $( "#keyboard" ).hide(); } function setCssKey() { $( "#pin" ).css({ "font-size" : "18px" , "color" : "#030303" , "font-weight" : "normal" , "letter-spacing" : "1px" }); } function setCssNomal() { $( "#pin" ).css({ "font-size" : "16px" , "color" : "#D2D1D1" , "font-weight" : "normal" }); } |
3).獲取密碼鍵盤后臺方法
該方法將隨機生成“1234567890與隨機密文的對應關系”和“隨機密文”和“1234567890圖片“的對應關系,然后把它們關系放入dto實例中并放入redis中,最后把隨機密文以集合的方式返回到頁面。具體代碼如下:
獲取密碼鍵盤:
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
|
/** * * @Description: 獲取密碼鍵盤 * @param request * @param rangdom 隨機字符串 * @return * */ @SuppressWarnings ( "unchecked" ) @ResponseBody @RequestMapping (value = "common/pkey.do" , method = RequestMethod.POST) public Object digitkeyboard(HttpServletRequest request, String rangdom) { LOG.info( "[獲取密碼鍵盤digitkeyboard][parames:outOrderId=" + rangdom + "]" ); try { if (StringUtils.isBlank(rangdom)) { return "" ; } PwdKeyDto pwdkey = PwdKeyUtils.digitkeyboard(); redisUtil.set( "pwdkey_" + rangdom, pwdkey, redisUtil.getDigitkeyimeOut()); return pwdkey.getRundomKeys(); } catch (Exception e) { LOG.error( "[獲取密碼鍵盤digitkeyboard][error:{}" ,e); } return "" ; } |
密碼PwdKeyDto:
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
|
/** * * @ClassName: PwdKeyDto * @Description: 密碼映射Dto * @author xxxx <a target="_blank" href="mailto:xxxx@xxx.com" rel="external nofollow" >xxxx@xxx.com</a> * @date 2015年6月25日 上午11:01:20 * */ public class PwdKeyDto implements Serializable{ /** 描述*/ private static final long serialVersionUID = 1L; private List<String> rundomKeys; // 隨機Keys private Map<String, String> valueKeyMaps; // 密文和明文映射 private Map<String, String> imgKeyMaps; // 密文和明文映射 public PwdKeyDto() { super (); // TODO Auto-generated constructor stub } public List<String> getRundomKeys() { return rundomKeys; } public void setRundomKeys(List<String> rundomKeys) { this .rundomKeys = rundomKeys; } public PwdKeyDto(List<String> rundomKeys, Map<String, String> valueKeyMaps, Map<String, String> imgKeyMaps) { super (); this .rundomKeys = rundomKeys; this .valueKeyMaps = valueKeyMaps; this .imgKeyMaps = imgKeyMaps; } public Map<String, String> getValueKeyMaps() { return valueKeyMaps; } public void setValueKeyMaps(Map<String, String> valueKeyMaps) { this .valueKeyMaps = valueKeyMaps; } public Map<String, String> getImgKeyMaps() { return imgKeyMaps; } public void setImgKeyMaps(Map<String, String> imgKeyMaps) { this .imgKeyMaps = imgKeyMaps; } } |
生成鍵盤的PwdKeyUtils工具類:
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
|
/** * * @ClassName: PwdKeyUtils * @Description: 密碼處理工具類 * @author xxxx <a target="_blank" href="mailto:xxxx@xxxx.com" rel="external nofollow" >xxxx@xxxx.com</a> * @date 2015年6月25日 上午11:03:24 * */ public class PwdKeyUtils { private final static Map<String, String> imagesValueMap; /** * * @Description: 獲取密碼鍵盤映射關系 * @param imagesValueMap * @return * */ static { imagesValueMap = new HashMap<String, String>(); imagesValueMap.put( "0" , "images/keys/0.png" ); imagesValueMap.put( "1" , "images/keys/1.png" ); imagesValueMap.put( "2" , "images/keys/2.png" ); imagesValueMap.put( "3" , "images/keys/3.png" ); imagesValueMap.put( "4" , "images/keys/4.png" ); imagesValueMap.put( "5" , "images/keys/5.png" ); imagesValueMap.put( "6" , "images/keys/6.png" ); imagesValueMap.put( "7" , "images/keys/7.png" ); imagesValueMap.put( "8" , "images/keys/8.png" ); imagesValueMap.put( "9" , "images/keys/9.png" ); } public static PwdKeyDto digitkeyboard() { List<String> rundomKeys = new ArrayList<String>(); // 隨機key映射 Map<String, String> valueKeys = new HashMap<String, String>(); // 密文和明文映射 Map<String, String> imgKeyMaps = new HashMap<String, String>(); // 密文和圖片映射 List<String> keys = new ArrayList<String>(); for ( int i = 0 ; i < 10 ; i++) { keys.add(i + "" ); } for ( int i = 0 ; i < 10 ; i++) { Random r = new Random(); int index = r.nextInt(keys.size()); String key = keys.get(index); keys.remove(index); String randomkey = randomKey( 24 ); rundomKeys.add(randomkey); valueKeys.put(randomkey, key); imgKeyMaps.put(randomkey, imagesValueMap.get(key)); } PwdKeyDto dto = new PwdKeyDto(rundomKeys, valueKeys, imgKeyMaps); return dto; } /** * * @Description:獲取動態key * @param num * key位數 * @return * */ public static String randomKey( int num) { StringBuffer sb = new StringBuffer( "" ); char [] chars = { '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' , 'I' , 'J' , 'K' , 'L' , 'M' , 'N' , 'O' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'U' , 'V' , 'W' , 'X' , 'Y' , 'Z' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' }; for ( int i = 0 ; i < num; i++) { int id = ( int ) Math.ceil(Math.random() * 60 ); sb.append(chars[id]); } return sb.toString(); } /** * * @Description:解密pin * @param request * @param pin * @return * */ public static String decryptPinData(HttpServletRequest request, String ciphertextpin) throws Exception { if (StringUtils.isNotBlank(ciphertextpin)) { Map<String, String> valuekeys = (Map<String, String>) request .getSession().getAttribute( "valuekeys" ); if (valuekeys == null || valuekeys.size() != 10 ) { throw new Exception(); } String[] ciphertextpins = ciphertextpin.split( "," ); StringBuffer sb = new StringBuffer( "" ); for (String ctpin : ciphertextpins) { sb.append(valuekeys.get(ctpin)); } } return null ; } } |
4).獲取圖片流后臺方法
用戶頁面獲取到隨機密文集合后以循環的方式向后臺請求該方法獲取對應的數字圖片流。具體代碼如下:
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
|
/** * 獲取key圖片 * * @throws Exception */ @RequestMapping (value = "/common/getKey.do" , method = RequestMethod.GET) public void getKey(String key, String rangdom, HttpServletRequest request, HttpServletResponse response) throws Exception { LOG.info( "[獲取密碼鍵盤key(getKey)][parms:key=" + key + "]" ); PwdKeyDto pwdkey = (PwdKeyDto) redisUtil.get( "pwdkey_" + rangdom); if (pwdkey == null || pwdkey.getImgKeyMaps() == null ) { LOG.error( "獲取圖片[getKey]:未獲取到對應的鍵盤的映射關系" ); throw new Exception(); } Map<String, String> imagekeys = pwdkey.getImgKeyMaps(); String path = imagekeys.get(key); String rootPath = request.getSession().getServletContext() .getRealPath( "/" ); path = rootPath + path; LOG.info( "[獲取密碼鍵盤key(getKey)][path=" + path + "]" ); if (StringUtils.isNotEmpty(path)) { try { InputStream fis = new FileInputStream( new File(path)); BufferedInputStream bis = new BufferedInputStream(fis); OutputStream fos = response.getOutputStream(); BufferedOutputStream bos = new BufferedOutputStream(fos); String fileName = "image." ; String[] strs = path.split( "\\." ); fileName = fileName + strs[strs.length - 1 ]; setFileDownloadHeader(request, response, fileName); int byteRead = 0 ; byte [] buffer = new byte [ 8192 ]; while ((byteRead = bis.read(buffer, 0 , 8192 )) != - 1 ) { bos.write(buffer, 0 , byteRead); } bos.flush(); fis.close(); bis.close(); fos.close(); bos.close(); } catch (Exception e) { LOG.error( "獲取圖片[path:" + path + "])失敗:" + e.toString(), e); } } } |
5).用戶支付
當用戶點擊數字鍵盤中的數字圖片,就會把圖片對應的密文放入到pkey隱藏輸入框中,多個數字以逗號隔開,當點擊支付的時候,就會把peykey隱藏輸入框的值傳入到后臺,后臺從redis中取出“密文”與“1234567890數字“的對應關系,就取出了對應交易密碼。具體代碼如下:
頁面提交支付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
|
function check() { hideKey(); var pin= "" ; pin=$( "#pkey" ).val(); if (pin== "" ||pin==undefined) { bool= false ; alert( "請輸入交易密碼" ); return false ; } else { var keys=pin.split( "," ); if (keys.length!= 6 ) { alert( "請輸入6位交易密碼" ); return false ; } } $.ajax({ type : 'post' , url : "test/pay.do" , data : { random: "2321321321" , pin:pin }, cache : false , success : function(data) { if (data.success) { alert(data.message); } else { } }, error : function(){ alert( "系統異常,請重試!" ); } }); } |
后臺解析密文方法:
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
|
/** * * @Description: 支付 * @param pin 交易密碼密文 * @param random 隨機碼 * @return * */ @ResponseBody @RequestMapping (value = "/test/pay.do" , method = RequestMethod.POST) public Object pay(String pin,String random, HttpServletRequest request) { try { LOG.info( "[支付(pay)][params:pin=" + pin + ",random=" +random+ "]" ); if (StringUtils.isNotBlank(pin)) { StringBuffer sb = new StringBuffer( "" ); PwdKeyDto pwdkey = (PwdKeyDto) redisUtil.get( "pwdkey_" + random); if (pwdkey == null || pwdkey.getValueKeyMaps() == null ) { return new Result( false , "密碼鍵盤已失效,請重新輸入密碼" ); } Map<String, String> valuekeys = pwdkey.getValueKeyMaps(); String[] pins = pin.split( "," ); if (pins.length != 6 ) { return new Result( false , "交易密碼位數不對" ); } for (String pinKey : pins) { String val = valuekeys.get(pinKey); if (StringUtils.isBlank(val)) { return new Result( false , "密碼鍵盤已失效,請重新輸入密碼" ); } sb.append(val); } /** * sb.toString()就是明文交易密碼,下面就是具體的支付操作 */ } return new Result( true , "成功" ); } catch (Exception e) { LOG.error( "[支付(pay)][error:{}]" ,e); return new Result( false , "支付異常,請重試!" ); } } |
以上就是對使用spring+html5實現安全傳輸隨機數字密碼鍵盤的介紹和代碼實現,大家有什么疑問或設計的不合理的地方可以一起討論。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。