门禁码
diff --git a/oauth/src/main/kotlin/com/supwisdom/oauth/oauth.kt b/oauth/src/main/kotlin/com/supwisdom/oauth/oauth.kt
index 8fb70be..79acca5 100644
--- a/oauth/src/main/kotlin/com/supwisdom/oauth/oauth.kt
+++ b/oauth/src/main/kotlin/com/supwisdom/oauth/oauth.kt
@@ -62,7 +62,7 @@
@GetMapping("/login")
fun loginView() = "login"
- @GetMapping("/index")
+ @GetMapping(value = ["/", "/index"])
fun indexView() = "index"
}
diff --git a/oauth/src/main/kotlin/com/supwisdom/oauth/security.kt b/oauth/src/main/kotlin/com/supwisdom/oauth/security.kt
index 0fdf884..191178b 100644
--- a/oauth/src/main/kotlin/com/supwisdom/oauth/security.kt
+++ b/oauth/src/main/kotlin/com/supwisdom/oauth/security.kt
@@ -46,6 +46,7 @@
}
user.lastlogin = DateUtil.getNow()
oAuthUserService.saveUser(user)
+ defaultTargetUrl = "/index"
super.onAuthenticationSuccess(request, response, authentication)
} else {
throw UserLoginFailException("登录错误")
@@ -131,7 +132,7 @@
// 设置 Web MVC 应用权限
http.authorizeRequests()
.antMatchers("/login", "/login/form", "/userinfor").permitAll()
- .antMatchers("/static/**").permitAll()
+ .antMatchers("/css/**").permitAll()
.antMatchers("/code/image").permitAll()
.antMatchers("/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
diff --git a/oauth/src/main/resources/templates/index.html b/oauth/src/main/resources/templates/index.html
new file mode 100644
index 0000000..6efd74e
--- /dev/null
+++ b/oauth/src/main/resources/templates/index.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+
+<html xmlns:th="http://www.thymeleaf.org">
+<head>
+ <title>用户中心</title>
+ <meta name="_csrf" th:content="${_csrf.token}"/>
+ <!-- default header name is X-CSRF-TOKEN -->
+ <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
+ <link rel="stylesheet" type="text/css" th:href="@{/css/weui.min.css}"/>
+ <link rel="stylesheet" type="text/css" th:href="@{/css/jquery-weui.css}"/>
+</head>
+<body>
+用户中心
+</body>
+</html>
\ No newline at end of file
diff --git a/oauth/src/main/resources/templates/login.html b/oauth/src/main/resources/templates/login.html
index f3ef3d6..c6660ad 100644
--- a/oauth/src/main/resources/templates/login.html
+++ b/oauth/src/main/resources/templates/login.html
@@ -6,8 +6,8 @@
<meta name="_csrf" th:content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
- <link rel="stylesheet" type="text/css" th:href="@{/static/css/weui.min.css}"/>
- <link rel="stylesheet" type="text/css" th:href="@{/static/css/jquery-weui.css}"/>
+ <link rel="stylesheet" type="text/css" th:href="@{/css/weui.min.css}"/>
+ <link rel="stylesheet" type="text/css" th:href="@{/css/jquery-weui.css}"/>
</head>
<body>
<p style="color:red;padding: 0 0 10px 0;"
@@ -18,13 +18,13 @@
th:name="${_csrf.parameterName}"
th:value="${_csrf.token}"/>
<div class="weui-cells__title">统一身份认证</div>
- <div class="weui-cells" style="margin-top:30%;">
+ <div class="weui-cells" >
<div class="weui-cell">
<div class="weui-cell__hd">
<label class="weui-label">账号</label>
</div>
<div class="weui-cell__bd">
- <input class="weui-input" type="text" id="username" name="username" placeholder="学工号、手机号、邮箱">
+ <input class="weui-input" type="text" id="username" name="username" placeholder="学工号\手机号\邮箱">
</div>
</div>
<div class="weui-cell">
@@ -37,7 +37,7 @@
</div>
</div>
- <button type="submit" class="weui-btn weui-btn_plain-primary">登 录</button>
+ <button type="submit" class="weui-btn weui-btn_plain-primary" style="margin:30px;">登 录</button>
</form>
</body>
</html>
\ No newline at end of file
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/DoorQRCodeParam.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/DoorQRCodeParam.java
new file mode 100644
index 0000000..e047a7a
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/DoorQRCodeParam.java
@@ -0,0 +1,23 @@
+package com.supwisdom.dlpay.api.bean;
+
+
+import com.supwisdom.dlpay.api.APIRequestParam;
+import com.supwisdom.dlpay.api.annotation.Sign;
+import com.supwisdom.dlpay.api.exception.RequestParamCheckException;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotNull;
+
+@Getter
+@Setter
+public class DoorQRCodeParam extends APIRequestParam {
+ @Sign
+ @NotNull(message = "二维码不能为空")
+ private String qrcode;
+
+ @Override
+ public boolean checkParam() throws RequestParamCheckException {
+ return true;
+ }
+}
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/DoorQrcodeResponse.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/DoorQrcodeResponse.java
new file mode 100644
index 0000000..dd77797
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/DoorQrcodeResponse.java
@@ -0,0 +1,17 @@
+package com.supwisdom.dlpay.api.bean;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class DoorQrcodeResponse extends ApiResponse {
+ private String userid;
+ private String citycardno;
+ private String bankcardno;
+ private String status;
+}
diff --git a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/ConsumePropxy.java b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/ConsumePropxy.java
index b5e1f3b..62584f7 100644
--- a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/ConsumePropxy.java
+++ b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/ConsumePropxy.java
@@ -22,4 +22,6 @@
@PostMapping("/thirdpay/finish")
ThirdPayResponse thirdpayFinish(@RequestBody ThirdPayfinishParam param);
+ @PostMapping("/qrcodequery")
+ DoorQrcodeResponse qrcodequery(@RequestBody DoorQRCodeParam param);
}
diff --git a/payapi-sdk/src/test/java/com/supwisdom/dlpay/paysdktest/CitizenCardPayProxyTest.java b/payapi-sdk/src/test/java/com/supwisdom/dlpay/paysdktest/CitizenCardPayProxyTest.java
index 36705fe..8229dcf 100644
--- a/payapi-sdk/src/test/java/com/supwisdom/dlpay/paysdktest/CitizenCardPayProxyTest.java
+++ b/payapi-sdk/src/test/java/com/supwisdom/dlpay/paysdktest/CitizenCardPayProxyTest.java
@@ -173,6 +173,17 @@
assertThat("qrcodeConfirm:" + response.getRetmsg() + response.getException()+",query="+response.isRequireQuery(),
response.getRetcode(), equalTo(0));
}
+ @Test
+ public void qrcodeQuery(){
+ ApiLoginHelper helper = new ApiLoginHelper(apiLoginProxy);
+ helper.login(appid, secret);
+ DoorQRCodeParam param = new DoorQRCodeParam();
+ param.setQrcode("6M4I1KGUO_UOVJVYCW52FQ");
+
+ DoorQrcodeResponse response = consumePropxy.qrcodequery(param);
+ assertThat("qrcode query " + response.getRetmsg() + response.getException(),
+ response.getRetcode(), equalTo(0));
+ }
@Test
public void thirdpay(){
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/util/MD5.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/util/MD5.java
index 91abab4..bcf4445 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/util/MD5.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/framework/util/MD5.java
@@ -1,5 +1,7 @@
package com.supwisdom.dlpay.framework.util;
+import org.apache.commons.codec.binary.Base64;
+
import java.security.MessageDigest;
public class MD5 {
@@ -83,5 +85,21 @@
}
return null;
}
-
+ /** 对字符串进行MD5加密 */
+ public static String encodeByMD5ToURLSafeBase64(String originString) {
+ if (originString != null) {
+ try {
+ // 创建具有指定算法名称的信息摘要
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ // 使用指定的字节数组对摘要进行最后更新,然后完成摘要计算
+ byte[] results = md.digest(originString.getBytes("utf-8"));
+ // 将得到的字节数组变成字符串返回
+ String resultString = Base64.encodeBase64URLSafeString(results);
+ return resultString.toUpperCase();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ return null;
+ }
}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/util/RandomUtils.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/util/RandomUtils.java
index 0afac38..74a0534 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/util/RandomUtils.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/framework/util/RandomUtils.java
@@ -64,8 +64,15 @@
String result = s + "_"+savesecret;
return result;
}
-
-
+
+ public static String getSecureRandomHex() {
+ SecureRandom a = new SecureRandom();
+ byte[] phonesecret = new byte[10];
+ a.nextBytes(phonesecret);
+
+ String savesecret = Hex.encodeHexString(phonesecret);
+ return savesecret;
+ }
/**
*
* @param length
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/util/AesUtil.java b/payapi/src/main/java/com/supwisdom/dlpay/util/AesUtil.java
new file mode 100644
index 0000000..ddd8f91
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/util/AesUtil.java
@@ -0,0 +1,162 @@
+package com.supwisdom.dlpay.util;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.lang.StringUtils;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+public class AesUtil {
+
+ public static String encrypt(String rawdata, String secret) {
+ Cipher cipher;
+ try {
+ cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+ SecretKeySpec key = new SecretKeySpec(secret.getBytes("UTF-8"), "AES");
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+ byte[] encdata = cipher.doFinal(rawdata.getBytes("UTF-8"));
+ String hexdata = Base64.encodeBase64String(encdata);
+
+ return hexdata;
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchPaddingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvalidKeyException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalBlockSizeException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (BadPaddingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return null;
+
+ }
+ public static String encryptCFB(String rawdata, String secret ,String ivstr,String KEY_ALG_MODE) {
+ Cipher cipher;
+ try {
+ //byte[] iv = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
+ byte[] iv = Hex.decodeHex(ivstr.toCharArray());
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+ cipher = Cipher.getInstance(KEY_ALG_MODE);
+ SecretKeySpec key = new SecretKeySpec(Base64.decodeBase64(secret), "AES");
+ cipher.init(Cipher.ENCRYPT_MODE, key,ivSpec);
+ byte[] encdata = cipher.doFinal(rawdata.getBytes("UTF-8"));
+ String hexdata = Base64.encodeBase64String(encdata);
+
+ return hexdata;
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchPaddingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvalidKeyException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalBlockSizeException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (BadPaddingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvalidAlgorithmParameterException e) {
+ e.printStackTrace();
+ } catch (DecoderException e) {
+ e.printStackTrace();
+ }
+ return null;
+
+ }
+
+ public static String decrypt(String encdata, String secret){
+ Cipher cipher;
+ try {
+ cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+ SecretKeySpec key = new SecretKeySpec(secret.getBytes("UTF-8"), "AES");
+ cipher.init(Cipher.DECRYPT_MODE, key);
+ byte[] decordedValue = Base64.decodeBase64(encdata);
+ byte[] decdata = cipher.doFinal(decordedValue);
+ String rawdata = new String(decdata);
+
+ return rawdata;
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchPaddingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvalidKeyException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalBlockSizeException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (BadPaddingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static String decryptCFB(String encdata, String secret,String ivstr,String KEY_ALG_MODE){
+ Cipher cipher;
+ try {
+// byte[] iv = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
+ byte[] iv = Hex.decodeHex(ivstr.toCharArray());
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+ cipher = Cipher.getInstance(KEY_ALG_MODE);
+ SecretKeySpec key = new SecretKeySpec(Base64.decodeBase64(secret), "AES");
+ cipher.init(Cipher.DECRYPT_MODE, key,ivSpec);
+ byte[] decordedValue = Base64.decodeBase64(encdata);
+ byte[] decdata = cipher.doFinal(decordedValue);
+ String rawdata = new String(decdata);
+
+ return rawdata;
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchPaddingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvalidKeyException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalBlockSizeException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (BadPaddingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvalidAlgorithmParameterException e) {
+ e.printStackTrace();
+ } catch (DecoderException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/util/QrCodeTotpUtil.java b/payapi/src/main/java/com/supwisdom/dlpay/util/QrCodeTotpUtil.java
new file mode 100644
index 0000000..fa51bb3
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/util/QrCodeTotpUtil.java
@@ -0,0 +1,59 @@
+package com.supwisdom.dlpay.util;
+
+public class QrCodeTotpUtil {
+ public static String generateTOTP(String seed){
+ long X = 30;
+ long T0 = 0;
+ String steps = "0";
+ long time = System.currentTimeMillis() / 1000;
+ long T = (time - T0) / X;
+ steps = Long.toHexString(T).toUpperCase();
+ while (steps.length() < 16) {
+ steps = "0" + steps;
+ }
+ String totp = TOTP.generateTOTP(seed, steps, "8", "HmacSHA256");
+ return totp;
+ }
+
+ public static String generateTOTP(String seed,String returnDigits){
+ long X = 30;
+ long T0 = 0;
+ String steps = "0";
+ long time = System.currentTimeMillis() / 1000;
+ long T = (time - T0) / X;
+ steps = Long.toHexString(T).toUpperCase();
+ while (steps.length() < 16) {
+ steps = "0" + steps;
+ }
+ String totp = TOTP.generateTOTP(seed, steps, returnDigits, "HmacSHA256");
+ return totp;
+ }
+
+ public static boolean verifyCode(String totp, String secret, int offset) {
+
+ String second = "30";
+ long T0 = 0;
+ String[] keys = new String[offset * 2 + 1];
+ long time = System.currentTimeMillis() / 1000;
+ for (int i = 0; i < keys.length; i++) {
+ String steps = "0";
+ try {
+ long T = (time - T0) / Long.parseLong(second);
+ steps = Long.toHexString(T + (i - offset)).toUpperCase();
+ while (steps.length() < 16) {
+ steps = "0" + steps;
+ }
+ String key = TOTP.generateTOTP(secret, steps, "8", "HmacSHA256");
+ keys[i] = key;
+ } catch (final Exception e) {
+ System.out.println("Error : " + e);
+ }
+ }
+ for (String key : keys) {
+ if (key.equals(totp)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/util/QrcodeRawData.java b/payapi/src/main/java/com/supwisdom/dlpay/util/QrcodeRawData.java
new file mode 100644
index 0000000..44ac288
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/util/QrcodeRawData.java
@@ -0,0 +1,22 @@
+package com.supwisdom.dlpay.util;
+
+public class QrcodeRawData {
+ private String totp;
+ private String userid;
+
+ public String getTotp() {
+ return totp;
+ }
+
+ public void setTotp(String totp) {
+ this.totp = totp;
+ }
+
+ public String getUserid() {
+ return userid;
+ }
+
+ public void setUserid(String userid) {
+ this.userid = userid;
+ }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/util/RSAKeysGenerate.java b/payapi/src/main/java/com/supwisdom/dlpay/util/RSAKeysGenerate.java
new file mode 100644
index 0000000..4af2148
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/util/RSAKeysGenerate.java
@@ -0,0 +1,179 @@
+package com.supwisdom.dlpay.util;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.StringUtils;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import javax.crypto.Cipher;
+import java.io.ByteArrayOutputStream;
+import java.security.*;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.HashMap;
+import java.util.Map;
+
+public class RSAKeysGenerate {
+
+ public static final String KEY_ALGORITHM = "RSA";
+ private static final String PUBLIC_KEY = "RSAPublicKey";
+ private static final String PRIVATE_KEY = "RSAPrivateKey";
+ private static final String KEY_ALG_MODE = "RSA/ECB/PKCS1Padding";
+ /** 密钥大小 */
+ private static final int KEY_SIZE = 1024;
+ /** *//**
+ * RSA最大加密明文大小
+ */
+ private static final int MAX_ENCRYPT_BLOCK = 117;
+
+ /** *//**
+ * RSA最大解密密文大小
+ */
+ private static final int MAX_DECRYPT_BLOCK = 128;
+ /** 默认的安全服务提供者 */
+ private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider();
+ public static void main(String[] args) {
+ Map<String, Object> keyMap;
+ try {
+ keyMap = initKey();
+ String publicKey = getPublicKey(keyMap);
+ System.out.println(publicKey);
+ String privateKey = getPrivateKey(keyMap);
+ System.out.println(privateKey);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
+ Key key = (Key) keyMap.get(PUBLIC_KEY);
+ byte[] publicKey = key.getEncoded();
+ return encryptBASE64(key.getEncoded());
+ }
+
+ public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
+ Key key = (Key) keyMap.get(PRIVATE_KEY);
+ byte[] privateKey = key.getEncoded();
+ return encryptBASE64(key.getEncoded());
+ }
+
+
+ public static String encryptBASE64(byte[] key) throws Exception {
+ return Base64.encodeBase64String(key);
+ }
+
+ public static Map<String, Object> initKey() throws Exception {
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
+ keyPairGen.initialize(1024);
+ KeyPair keyPair = keyPairGen.generateKeyPair();
+ RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
+ RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
+ Map<String, Object> keyMap = new HashMap<String, Object>(2);
+ keyMap.put(PUBLIC_KEY, publicKey);
+ keyMap.put(PRIVATE_KEY, privateKey);
+ return keyMap;
+ }
+
+ public static PrivateKey getPrivateKey(final String key) throws Exception {
+ byte[] keyBytes = null;
+ keyBytes = com.sun.jersey.core.util.Base64.decode(key);
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
+ KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
+ PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
+ return privateKey;
+ }
+ public static PublicKey getPublicKey(final String key) throws Exception {
+ byte[] keyBytes;
+ keyBytes = com.sun.jersey.core.util.Base64.decode(key);
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
+ KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
+ PublicKey publicKey = keyFactory.generatePublic(keySpec);
+ return publicKey;
+ }
+
+ public static byte[] decrypt(PrivateKey privateKey, byte[] data) throws Exception {
+ Cipher cipher = Cipher.getInstance(KEY_ALG_MODE, DEFAULT_PROVIDER);
+ cipher.init(Cipher.DECRYPT_MODE, privateKey);
+
+ int inputLen = data.length;
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int offSet = 0;
+ byte[] cache;
+ int i = 0;
+ // 对数据分段解密
+ while (inputLen - offSet > 0) {
+ if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
+ cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
+ } else {
+ cache = cipher.doFinal(data, offSet, inputLen - offSet);
+ }
+ out.write(cache, 0, cache.length);
+ i++;
+ offSet = i * MAX_DECRYPT_BLOCK;
+ }
+ byte[] decryptedData = out.toByteArray();
+ out.close();
+ return decryptedData;
+
+ }
+ public static String decryptbyte(PrivateKey privateKey, String encrypttext) {
+ if (privateKey == null || StringUtils.isBlank(encrypttext)) {
+ return null;
+ }
+ try {
+ byte[] en_data = org.apache.commons.codec.binary.Base64.decodeBase64(encrypttext);
+
+
+ byte[] data = decrypt(privateKey, en_data);
+ return new String(data);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return null;
+ }
+ /**
+ * 使用指定的公钥加密数据。
+ *
+ * @param publicKey 给定的公钥。
+ * @param data 要加密的数据。
+ * @return 加密后的数据。
+ */
+ public static byte[] encrypt(PublicKey publicKey, byte[] data) throws Exception {
+ Cipher cipher = Cipher.getInstance(KEY_ALG_MODE, DEFAULT_PROVIDER);
+ cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+
+ int inputLen = data.length;
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int offSet = 0;
+ byte[] cache;
+ int i = 0;
+ // 对数据分段加密
+ while (inputLen - offSet > 0) {
+ if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
+ cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
+ } else {
+ cache = cipher.doFinal(data, offSet, inputLen - offSet);
+ }
+ out.write(cache, 0, cache.length);
+ i++;
+ offSet = i * MAX_ENCRYPT_BLOCK;
+ }
+ byte[] encryptedData = out.toByteArray();
+ out.close();
+ return encryptedData;
+ }
+ public static byte[] encryptbyte(PublicKey publicKey, String plaintext) {
+ if (publicKey == null || plaintext == null) {
+ return null;
+ }
+ byte[] data = plaintext.getBytes();
+ try {
+ byte[] en_data = encrypt(publicKey, data);
+ return en_data;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/util/TOTP.java b/payapi/src/main/java/com/supwisdom/dlpay/util/TOTP.java
new file mode 100644
index 0000000..bcab4d3
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/util/TOTP.java
@@ -0,0 +1,264 @@
+package com.supwisdom.dlpay.util;
+
+/**
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, is permitted pursuant to, and subject to the license
+ terms contained in, the Simplified BSD License set forth in Section
+ 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+ */
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+
+/**
+ * This is an example implementation of the OATH TOTP algorithm. Visit
+ * www.openauthentication.org for more information.
+ *
+ * @author Johan Rydell, PortWise, Inc.
+ */
+public class TOTP {
+ private static final int[] DIGITS_POWER
+ // 0 1 2 3 4 5 6 7 8
+ = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
+
+ private TOTP() {
+ }
+
+ /**
+ * This method uses the JCE to provide the crypto algorithm. HMAC computes a
+ * Hashed Message Authentication Code with the crypto hash algorithm as a
+ * parameter.
+ *
+ * @param crypto
+ * : the crypto algorithm (HmacSHA1, HmacSHA256, HmacSHA512)
+ * @param keyBytes
+ * : the bytes to use for the HMAC key
+ * @param text
+ * : the message or text to be authenticated
+ */
+ private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) {
+ try {
+ Mac hmac;
+ hmac = Mac.getInstance(crypto);
+
+ SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
+ hmac.init(macKey);
+
+ return hmac.doFinal(text);
+ } catch (GeneralSecurityException gse) {
+ throw new UndeclaredThrowableException(gse);
+ }
+ }
+
+ /**
+ * This method converts a HEX string to Byte[]
+ *
+ * @param hex
+ * : the HEX string
+ *
+ * @return: a byte array
+ */
+ private static byte[] hexStr2Bytes(String hex) {
+ // Adding one byte to get the right conversion
+ // Values starting with "0" can be converted
+ byte[] bArray = new BigInteger("10" + hex, 16).toByteArray();
+
+ // Copy all the REAL bytes, not the "first"
+ byte[] ret = new byte[bArray.length - 1];
+
+ for (int i = 0; i < ret.length; i++)
+ ret[i] = bArray[i + 1];
+
+ return ret;
+ }
+
+ /**
+ * This method generates a TOTP value for the given set of parameters.
+ *
+ * @param key
+ * : the shared secret, HEX encoded
+ * @param time
+ * : a value that reflects a time
+ * @param returnDigits
+ * : number of digits to return
+ *
+ * @return: a numeric String in base 10 that includes
+ * {@link truncationDigits} digits
+ */
+ public static String generateTOTP(String key, String time,
+ String returnDigits) {
+ return generateTOTP(key, time, returnDigits, "HmacSHA1");
+ }
+
+ /**
+ * This method generates a TOTP value for the given set of parameters.
+ *
+ * @param key
+ * : the shared secret, HEX encoded
+ * @param time
+ * : a value that reflects a time
+ * @param returnDigits
+ * : number of digits to return
+ *
+ * @return: a numeric String in base 10 that includes
+ * {@link truncationDigits} digits
+ */
+ public static String generateTOTP256(String key, String time,
+ String returnDigits) {
+ return generateTOTP(key, time, returnDigits, "HmacSHA256");
+ }
+
+ /**
+ * This method generates a TOTP value for the given set of parameters.
+ *
+ * @param key
+ * : the shared secret, HEX encoded
+ * @param time
+ * : a value that reflects a time
+ * @param returnDigits
+ * : number of digits to return
+ *
+ * @return: a numeric String in base 10 that includes
+ * {@link truncationDigits} digits
+ */
+ public static String generateTOTP512(String key, String time,
+ String returnDigits) {
+ return generateTOTP(key, time, returnDigits, "HmacSHA512");
+ }
+
+ /**
+ * This method generates a TOTP value for the given set of parameters.
+ *
+ * @param key
+ * : the shared secret, HEX encoded
+ * @param time
+ * : a value that reflects a time
+ * @param returnDigits
+ * : number of digits to return
+ * @param crypto
+ * : the crypto function to use
+ *
+ * @return: a numeric String in base 10 that includes
+ * {@link truncationDigits} digits
+ */
+ public static String generateTOTP(String key, String time,
+ String returnDigits, String crypto) {
+ int codeDigits = Integer.decode(returnDigits).intValue();
+ String result = null;
+
+ // Using the counter
+ // First 8 bytes are for the movingFactor
+ // Compliant with base RFC 4226 (HOTP)
+ while (time.length() < 16)
+ time = "0" + time;
+
+ // Get the HEX in a Byte[]
+ byte[] msg = hexStr2Bytes(time);
+ byte[] k = hexStr2Bytes(key);
+ byte[] hash = hmac_sha(crypto, k, msg);
+
+ // put selected bytes into result int
+ int offset = hash[hash.length - 1] & 0xf;
+
+ int binary = ((hash[offset] & 0x7f) << 24)
+ | ((hash[offset + 1] & 0xff) << 16)
+ | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);
+
+ int otp = binary % DIGITS_POWER[codeDigits];
+
+ result = Integer.toString(otp);
+
+ while (result.length() < codeDigits) {
+ result = "0" + result;
+ }
+
+ return result;
+ }
+
+ public static void main(String[] args) {
+ long X = 30;
+ long T0 = 0;
+ String steps = "0";
+ long time = System.currentTimeMillis() / 1000;
+ System.out.println(time);
+ long T = (time - T0) / X;
+ steps = Long.toHexString(T).toUpperCase();
+ while (steps.length() < 16) {
+ steps = "0" + steps;
+ }
+ String seed = "9e8bb3d2df73741c041aef39e37ee015fc98233720a1350fcd67d5c3027896ac";
+ String totp = TOTP.generateTOTP(seed, steps, "8", "HmacSHA256");
+ System.out.println(totp);
+
+ /*
+
+
+ SecureRandom a = new SecureRandom();
+ byte[] b = a.generateSeed(8);
+ // Seed for HMAC-SHA1 - 20 bytes
+ String seed = "3132333435363738393031323334353637383930";
+
+ // Seed for HMAC-SHA256 - 32 bytes
+ //String seed32 = "3132333435363738393031323334353637383930313233343536373839303132";
+ String seed32 = "e5b72370f61687c8075b8383266f4fbcbb3b55da178eed76329666600c134093";
+
+ // Seed for HMAC-SHA512 - 64 bytes
+ String seed64 = "3132333435363738393031323334353637383930"
+ + "3132333435363738393031323334353637383930"
+ + "3132333435363738393031323334353637383930" + "31323334";
+ long T0 = 0;
+ long X = 30;
+ long[] testTime = { 59L, 1111111109L, 1111111111L, 1234567890L,
+ 2000000000L, 20000000000L };
+
+ String steps = "0";
+ DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ df.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ try {
+ System.out.println("+---------------+-----------------------+"
+ + "------------------+--------+--------+");
+ System.out.println("| Time(sec) | Time (UTC format) "
+ + "| Value of T(Hex) | TOTP | Mode |");
+ System.out.println("+---------------+-----------------------+"
+ + "------------------+--------+--------+");
+
+ for (int i = 0; i < testTime.length; i++) {
+ long T = (testTime[i] - T0) / X;
+ steps = Long.toHexString(T).toUpperCase();
+
+ while (steps.length() < 16)
+ steps = "0" + steps;
+
+ String fmtTime = String.format("%1$-11s", testTime[i]);
+ String utcTime = df.format(new Date(testTime[i] * 1000));
+ System.out.print("| " + fmtTime + " | " + utcTime + " | "
+ + steps + " |");
+ System.out.println(generateTOTP(seed, steps, "8", "HmacSHA1")
+ + "| SHA1 |");
+ System.out.print("| " + fmtTime + " | " + utcTime + " | "
+ + steps + " |");
+ System.out.println(generateTOTP(seed32, steps, "8",
+ "HmacSHA256") + "| SHA256 |");
+ System.out.print("| " + fmtTime + " | " + utcTime + " | "
+ + steps + " |");
+ System.out.println(generateTOTP(seed64, steps, "8",
+ "HmacSHA512") + "| SHA512 |");
+
+ System.out.println("+---------------+-----------------------+"
+ + "------------------+--------+--------+");
+ }
+ } catch (final Exception e) {
+ System.out.println("Error : " + e);
+ }
+
+ */
+ }
+}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
index 8b7b796..d993aea 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
@@ -88,8 +88,8 @@
fun registerMobileFilter(): FilterRegistrationBean<MobileSecurityFilter> {
val registrationBean = FilterRegistrationBean<MobileSecurityFilter>()
registrationBean.filter = mobileSecurityFilter
- registrationBean.order = 2
registrationBean.addUrlPatterns("/mobile/*")
+ registrationBean.order = 2
return registrationBean
}
}
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
index 7c35683..cfdd7ec 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
@@ -18,6 +18,7 @@
import org.apache.commons.lang3.StringUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
+import org.springframework.data.redis.core.RedisTemplate
import org.springframework.http.ResponseEntity
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*
@@ -50,6 +51,12 @@
@Autowired
private lateinit var agentServiceProxy: AgentServiceProxy
+ @Autowired
+ lateinit var redisTemplate: RedisTemplate<String, String>
+
+ @Autowired
+ private lateinit var qrCodeService: QRCodeService
+
/**
* ============================================================================
* 消费流水结果查询统一接口
@@ -754,4 +761,26 @@
}
}
}
+
+ /**
+ * ============================================================================
+ * 门禁码查询接口
+ * ============================================================================
+ * */
+ @PostMapping("/qrcodequery")
+ fun qrcodeQuery(@RequestBody param: DoorQRCodeParam): ResponseEntity<Any> {
+ var token = redisTemplate.opsForValue().get(param.qrcode)
+ if(token.isNullOrEmpty()){
+ return ResponseEntity.ok(ResponseBodyBuilder.create()
+ .fail(1, "二维码不存在或已失效"))
+ }
+ val ret = qrCodeService.decodeCode(token)
+ return if(ret.retcode==0){
+ ResponseEntity.ok(ResponseBodyBuilder.create()
+ .success(ret, "成功"))
+ }else{
+ ResponseEntity.ok(ResponseBodyBuilder.create()
+ .fail(ret.retcode, ret.retmsg))
+ }
+ }
}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt
new file mode 100644
index 0000000..95a1de0
--- /dev/null
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt
@@ -0,0 +1,155 @@
+package com.supwisdom.dlpay.api.service.impl
+
+import com.google.gson.Gson
+import com.supwisdom.dlpay.api.bean.ApiResponse
+import com.supwisdom.dlpay.api.bean.DoorQrcodeResponse
+import com.supwisdom.dlpay.api.service.QRCodeService
+import com.supwisdom.dlpay.api.service.UserService
+import com.supwisdom.dlpay.framework.service.SystemUtilService
+import com.supwisdom.dlpay.framework.util.MD5
+import com.supwisdom.dlpay.framework.util.TradeDict
+import com.supwisdom.dlpay.mobile.service.MobileApiService
+import com.supwisdom.dlpay.util.AesUtil
+import com.supwisdom.dlpay.util.QrCodeTotpUtil
+import com.supwisdom.dlpay.util.QrcodeRawData
+import com.supwisdom.dlpay.util.RSAKeysGenerate
+import mu.KotlinLogging
+import org.apache.commons.lang.math.NumberUtils
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.data.redis.core.RedisTemplate
+import org.springframework.stereotype.Service
+import java.time.Duration
+
+@Service
+class QRCodeServiceImpl:QRCodeService{
+ @Autowired
+ private lateinit var systemUtilService: SystemUtilService
+ @Autowired
+ private lateinit var mobileApiService: MobileApiService
+ @Autowired
+ lateinit var userService: UserService
+ @Autowired
+ lateinit var redisTemplate: RedisTemplate<String, String>
+
+ val logger = KotlinLogging.logger { }
+
+ override fun encodeCode(uid:String): ApiResponse {
+ var resp = ApiResponse()
+ val rootkey = systemUtilService.getBusinessValue("aes.cfb.rootkey")
+ val iv = systemUtilService.getBusinessValue("aes.cfb.iv")
+ if (rootkey.isNullOrEmpty()||iv.isNullOrEmpty()) {
+ resp.retcode = 1
+ resp.retmsg = "秘钥未配置"
+ return resp
+ }
+ val muser = mobileApiService.findUserById(uid)
+ if(muser==null||TradeDict.STATUS_NORMAL!=muser.status){
+ resp.retcode = 1
+ resp.retmsg = "用户不存在或状态异常"
+ return resp
+ }
+ if(muser.userid.isNullOrEmpty()){
+ resp.retcode = 1
+ resp.retmsg = "用户未绑定身份"
+ return resp
+ }
+ val totp = QrCodeTotpUtil.generateTOTP(muser.secertkey)
+ val rowdata = QrcodeRawData()
+ rowdata.userid = muser.userid
+ rowdata.setTotp(totp)
+ val orgData = Gson().toJson(rowdata)
+ val publicKey = RSAKeysGenerate.getPublicKey(muser.rsapublic)
+ val encdata = org.apache.commons.codec.binary.Base64.encodeBase64String(RSAKeysGenerate.encryptbyte(publicKey, orgData))
+ val qrcode = AesUtil.encryptCFB("$uid:$encdata", rootkey, iv, "AES/CFB/NoPadding")
+
+ val key = MD5.encodeByMD5ToURLSafeBase64(qrcode)
+ redisTemplate.opsForValue().set(key,qrcode, Duration.ofMinutes(3))
+ resp.retcode = 0
+ resp.retmsg = key
+ return resp
+ }
+
+ override fun decodeCode(qrcode :String): DoorQrcodeResponse {
+
+ //解密
+ var resp = DoorQrcodeResponse()
+ try {
+ val rootkey = systemUtilService.getBusinessValue("aes.cfb.rootkey")
+ val iv = systemUtilService.getBusinessValue("aes.cfb.iv")
+ val totpoffset = systemUtilService.getBusinessValue("aes.cfb.totp.offset")
+ var offset = 20
+ if (NumberUtils.isDigits(totpoffset)) {
+ offset = Integer.valueOf(totpoffset).toInt()
+ }
+ if (rootkey.isNullOrEmpty()||iv.isNullOrEmpty()) {
+ resp.retcode = 1
+ resp.retmsg = "秘钥未配置"
+ return resp
+ }
+
+ println("vtoken=[" + qrcode + "],length=[" + qrcode.length + "]")
+ val encdataBack = AesUtil.decryptCFB(qrcode, rootkey, iv, "AES/CFB/NoPadding")
+ if (encdataBack == null) {
+ resp.retcode = 1
+ resp.retmsg = "二维码解析失败"
+ return resp
+ }
+ val uid = encdataBack.substring(0, encdataBack.indexOf(":"))
+ val muser = mobileApiService.findUserById(uid)
+ if(muser==null||TradeDict.STATUS_NORMAL!=muser.status){
+ resp.retcode = 1
+ resp.retmsg = "用户不存在或状态异常"
+ return resp
+ }
+ val seed32 = muser.secertkey
+ val rawEncdata = encdataBack.substring(encdataBack.indexOf(":") + 1)
+
+ val privateKey = RSAKeysGenerate.getPrivateKey(muser.rsaprivate)
+ val rawdataBack = RSAKeysGenerate.decryptbyte(privateKey, rawEncdata)
+ logger.info("rawdata_back=$rawdataBack")
+
+ val rawData = Gson().fromJson(rawdataBack, QrcodeRawData::class.java)
+ logger.info("userid=" + rawData.userid)
+ if (null == rawData || rawData.userid.isNullOrEmpty()) {
+ resp.retcode = 1
+ resp.retmsg = "用户不存在"
+ return resp
+ }
+ val person = userService.findOnePersonByUserid(rawData.userid)
+
+ /*if(TradeDict.STATUS_NORMAL!=person.status){
+ resp.retcode = 1
+ resp.retmsg = "用户状态异常"
+ return resp
+ }*/
+ val verifyBarcode = QrCodeTotpUtil.verifyCode(rawData.totp, seed32, offset)
+ return if (verifyBarcode) {
+ resp.retcode = 0
+ resp.retmsg = "OK"
+ resp.userid = person.userid
+ resp.status = person.status
+ val bankcard = mobileApiService.findCardByUserid(person.userid)
+ if(bankcard!=null){
+ resp.bankcardno = bankcard.cardno
+ }
+ val citycard = mobileApiService.findCityCardByUserid(person.userid)
+ if(citycard!=null){
+ resp.citycardno = citycard.cardno
+ }
+ resp
+ } else {
+ resp.retcode = 1
+ resp.retmsg = "二维码校验失败"
+ resp
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ resp.retcode = 1
+ resp.retmsg = "二维码识别异常"
+ return resp
+ }
+
+ }
+
+}
+
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/qrcode_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/qrcode_service.kt
new file mode 100644
index 0000000..029f6a8
--- /dev/null
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/qrcode_service.kt
@@ -0,0 +1,9 @@
+package com.supwisdom.dlpay.api.service
+
+import com.supwisdom.dlpay.api.bean.ApiResponse
+import com.supwisdom.dlpay.api.bean.DoorQrcodeResponse
+
+interface QRCodeService {
+ fun encodeCode(uid: String): ApiResponse
+ fun decodeCode(qrcode: String): DoorQrcodeResponse
+}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
index 3e75d86..b929bea 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
@@ -1,9 +1,9 @@
package com.supwisdom.dlpay.mobile
-import com.mascloud.sdkclient.Client
import com.supwisdom.dlpay.agent.citizencard.YnrccUtil
import com.supwisdom.dlpay.agent.service.CitizencardPayService
import com.supwisdom.dlpay.api.bean.JsonResult
+import com.supwisdom.dlpay.api.service.QRCodeService
import com.supwisdom.dlpay.api.service.UserService
import com.supwisdom.dlpay.api.util.MobileNumberCheck
import com.supwisdom.dlpay.framework.core.JwtConfig
@@ -17,9 +17,9 @@
import com.supwisdom.dlpay.mobile.service.MobileApiService
import com.supwisdom.dlpay.system.service.DictionaryProxy
import com.supwisdom.dlpay.util.ConstantUtil
+import com.supwisdom.dlpay.util.RSAKeysGenerate
import org.apache.commons.lang.StringUtils
import org.jose4j.jwt.ReservedClaimNames
-import org.jose4j.lang.JoseException
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.http.HttpStatus
@@ -95,11 +95,12 @@
val temp = redisTemplate.opsForValue().get(phone)
if (temp.isNullOrEmpty()) {
val code = RandomUtils.randomNumber(6)
+ System.out.println(code)
+ redisTemplate.opsForValue().set(phone, code, Duration.ofMinutes(5))
val rs = mobileApiService.sendSms(phone, code)
if ("0" != rs.retcode) {
return JsonResult.error(rs.retmsg)
}
- redisTemplate.opsForValue().set(phone, code, Duration.ofMinutes(5))
}
return JsonResult.ok("验证码已发送")
}
@@ -196,6 +197,12 @@
}
user.lastlogin = DateUtil.getNow()
user.jti = jwt.jti
+ val keyMap = RSAKeysGenerate.initKey()
+ val publicKey = RSAKeysGenerate.getPublicKey(keyMap)
+ val privateKey = RSAKeysGenerate.getPrivateKey(keyMap)
+ user.rsaprivate = privateKey
+ user.rsapublic = publicKey
+ user.secertkey = RandomUtils.getSecureRandomHex()
mobileApiService.saveUser(user)
redisTemplate.delete(user.uid)
var payseted = false
@@ -234,11 +241,12 @@
lateinit var dictionaryProxy: DictionaryProxy
@Autowired
lateinit var citizencardPayService: CitizencardPayService
-
@Autowired
lateinit var apiJwtRepository: ApiJwtRepository
@Autowired
lateinit var jwtConfig: JwtConfig
+ @Autowired
+ lateinit var qrcodeService:QRCodeService
@RequestMapping("/idtypes")
fun idtypes(): JsonResult {
@@ -693,11 +701,13 @@
@RequestMapping("/qrcode")
fun qrcode(): JsonResult {
val p = SecurityContextHolder.getContext().authentication
- mobileApiService.findUserById(p.name)
+ val user = mobileApiService.findUserById(p.name)
?: return JsonResult.error("用户不存在,请注册")
- //TODO qrcode
- return JsonResult.ok("ok").put("qrcode", p.name)!!
+ val resp = qrcodeService.encodeCode(user.uid)
+ return if(resp.retcode==0){
+ JsonResult.ok("ok").put("qrcode", resp.retmsg)!!
+ }else{
+ JsonResult.error(resp.retmsg)
+ }
}
-
-
}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/domain/TBMobileUser.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/domain/TBMobileUser.kt
index 68b9155..276d8d0 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/domain/TBMobileUser.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/domain/TBMobileUser.kt
@@ -160,6 +160,12 @@
* */
@Column(name = "ulogo", length = 100)
var ulogo: String? = null
+ @Column(name = "rsaprivate", length = 1000)
+ var rsaprivate: String? = null
+ @Column(name = "rsapublic", length = 1000)
+ var rsapublic: String? = null
+ @Column(name = "secertkey", length = 64)
+ var secertkey: String? = null
fun checkLoginpwdtime():Int{
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileApiService.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileApiService.kt
index 5e1ad77..0311352 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileApiService.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileApiService.kt
@@ -18,6 +18,8 @@
fun findCardByUserid(userid :String) :TCard?
+ fun findCityCardByUserid(userid :String) :TCard?
+
fun saveCard(card:TCard):TCard
fun sendSms(phone:String,code:String):BaseResp
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileApiServiceImpl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileApiServiceImpl.kt
index 87ef47b..cc1bc47 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileApiServiceImpl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileApiServiceImpl.kt
@@ -60,6 +60,10 @@
return cardDao.findCardByUseridAndCardtype(userid,ConstantUtil.CARDTYPE_BANKCARD)
}
+ override fun findCityCardByUserid(userid: String): TCard? {
+ return cardDao.findCardByUseridAndCardtype(userid,ConstantUtil.CARDTYPE_CITIZENCARD)
+ }
+
override fun saveCard(card: TCard): TCard {
return cardDao.save(card)
}
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/security.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/security.kt
index 5dc7de1..154dee4 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/security.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/security.kt
@@ -41,6 +41,7 @@
import org.springframework.web.cors.CorsConfigurationSource
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
import org.springframework.web.filter.OncePerRequestFilter
+import org.springframework.web.servlet.config.annotation.CorsRegistry
import java.security.SecureRandom
import java.util.*
import javax.servlet.FilterChain
@@ -131,15 +132,84 @@
}
@Component
-class MobileSecurityFilter : HttpFilter() {
- override fun doFilter(req: HttpServletRequest, res: HttpServletResponse, chain: FilterChain) {
-// val url = req.requestURI
-// if (url.contains("/userinfor")) {
-// SecurityContextHolder.getContext().authentication = RedisTokenStore(redisConnectionFactory).readAuthentication(jwt)
-// chain.doFilter(req, res)
-// return
-// }
- chain.doFilter(req, res)
+class MobileSecurityFilter : OncePerRequestFilter() {
+ @Autowired
+ lateinit var jwtConfig: JwtConfig
+
+ @Autowired
+ lateinit var apiJwtRepository: ApiJwtRepository
+
+ private var jwtUtil: JwtTokenUtil? = null
+
+ private fun getUtil(): JwtTokenUtil {
+ if (jwtUtil == null) {
+ jwtUtil = JwtTokenUtil((jwtConfig))
+ }
+ return jwtUtil as JwtTokenUtil
+ }
+
+ override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
+
+ request.getHeader(jwtConfig.header)?.let { authHeader ->
+ try {
+ val jwt = if (authHeader.startsWith(jwtConfig.tokenHeader)) {
+ authHeader.substring(jwtConfig.tokenHeader.length)
+ } else {
+ throw JoseException("JWT Header error")
+ }
+ val claims = getUtil().verifyToken(jwt)
+ apiJwtRepository.findById(claims[ReservedClaimNames.JWT_ID].toString()).let {
+ if (!it.isPresent) {
+ throw JoseException("JWT has not been register")
+ }
+ // token 已被设为黑名单
+ if (it.get().status != TradeDict.JWT_STATUS_NORMAL) {
+ throw JoseException("JWT status error : ${it.get().status}")
+ }
+ }
+ if (jwtConfig.multiTenant) {
+ val tenantId = request.getHeader(Constants.HEADER_TETANTID)
+ if (tenantId == null) {
+ response.status = HttpStatus.UNAUTHORIZED.value()
+ return
+ }
+ if (claims[Constants.JWT_CLAIM_TENANTID] != tenantId) {
+ response.status = HttpStatus.UNAUTHORIZED.value()
+ return
+ }
+ TenantContext.setTenantSchema(tenantId)
+ }
+ val auth = UsernamePasswordAuthenticationToken(claims[Constants.JWT_CLAIM_UID], null,
+ (claims[Constants.JWT_CLAIM_AUTHORITIES] as ArrayList<*>)
+ .map { SimpleGrantedAuthority(it as String) })
+ SecurityContextHolder.getContext().authentication = auth
+ } catch (e: InvalidJwtException) {
+ SecurityContextHolder.clearContext()
+ if (e.hasExpired()) {
+ // jwt 过期后返回 401
+ apiJwtRepository.deleteById(e.jwtContext.jwtClaims.jwtId)
+ }
+ response.status = HttpStatus.UNAUTHORIZED.value()
+ return
+ } catch (e: JoseException) {
+ SecurityContextHolder.clearContext()
+ // jwt 失效后返回 401
+ response.status = HttpStatus.UNAUTHORIZED.value()
+ response.contentType = "application/json;charset=UTF-8"
+ return
+ } catch (e: Exception) {
+ SecurityContextHolder.clearContext()
+ // jwt 失效后返回 401
+ response.status = HttpStatus.UNAUTHORIZED.value()
+ response.contentType = "application/json;charset=UTF-8"
+ return
+ }
+ }
+ response.setHeader("Access-Control-Allow-Origin", "*");
+ response.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
+ response.setHeader("Access-Control-Allow-Headers", "*");
+ response.setHeader("Access-Control-Allow-Credentials", "true")
+ filterChain.doFilter(request, response)
}
}
@@ -151,12 +221,15 @@
@Configuration
@Order(1)
class ApiWebSecurityConfigurationAdapter : WebSecurityConfigurerAdapter() {
-
+ @Autowired
+ lateinit var apiJwtAuthenticationFilter: ApiJwtAuthenticationFilter
override fun configure(http: HttpSecurity) {
// 设置 API 访问权限管理
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.antMatcher("/api/**")
+ .addFilterAfter(apiJwtAuthenticationFilter,
+ UsernamePasswordAuthenticationFilter::class.java)
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/notify/**").permitAll()
@@ -188,9 +261,9 @@
@Autowired
lateinit var userDetailsService: MobileUserService
-
@Autowired
- lateinit var apiFilter: ApiJwtAuthenticationFilter
+ lateinit var mobileSecurityFilter: MobileSecurityFilter
+
override fun configure(auth: AuthenticationManagerBuilder) {
auth.authenticationProvider(userProvider())
@@ -220,7 +293,7 @@
.cors()
.and()
.antMatcher("/mobileapi/**")
- .addFilterAfter(apiFilter,
+ .addFilterAfter(mobileSecurityFilter,
UsernamePasswordAuthenticationFilter::class.java)
.authorizeRequests().antMatchers("/mobileapi/i/**", "/mobileapi/login")
.permitAll().anyRequest().authenticated()
@@ -230,8 +303,6 @@
.failureHandler(failureHandler)
.successHandler(successHandler)
.and().csrf().disable()
- .sessionManagement().maximumSessions(1)
- .expiredUrl("/mobileapi/sessionexpired")
}
@Bean
@@ -242,7 +313,7 @@
configuration.allowedMethods = listOf("GET", "POST")
configuration.allowedHeaders = listOf("*")
val source = UrlBasedCorsConfigurationSource()
- source.registerCorsConfiguration("/mobileapi/**", configuration);
+ source.registerCorsConfiguration("/mobileapi/**", configuration)
return source
}
}