diff --git a/mall-ebtp-cloud-jpa-starter/pom.xml b/mall-ebtp-cloud-jpa-starter/pom.xml index cfc3598..7868595 100644 --- a/mall-ebtp-cloud-jpa-starter/pom.xml +++ b/mall-ebtp-cloud-jpa-starter/pom.xml @@ -7,13 +7,13 @@ com.chinaunicom.ebtp mall-ebtp-cloud-parent - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT ../mall-ebtp-cloud-parent com.chinaunicom.ebtp mall-ebtp-cloud-jpa-starter - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT mall-ebtp-cloud-jpa-starter diff --git a/mall-ebtp-cloud-kafka-starter/pom.xml b/mall-ebtp-cloud-kafka-starter/pom.xml index 7c6ce52..c627b69 100644 --- a/mall-ebtp-cloud-kafka-starter/pom.xml +++ b/mall-ebtp-cloud-kafka-starter/pom.xml @@ -8,13 +8,13 @@ com.chinaunicom.ebtp mall-ebtp-cloud-parent - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT ../mall-ebtp-cloud-parent com.chinaunicom.ebtp mall-ebtp-cloud-kafka-starter - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT mall-ebtp-cloud-kafka-starter diff --git a/mall-ebtp-cloud-parent/pom.xml b/mall-ebtp-cloud-parent/pom.xml index b54d53d..96b6da7 100644 --- a/mall-ebtp-cloud-parent/pom.xml +++ b/mall-ebtp-cloud-parent/pom.xml @@ -7,12 +7,12 @@ com.chinaunicom.ebtp mall-ebtp-cloud - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT com.chinaunicom.ebtp mall-ebtp-cloud-parent - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT pom mall-ebtp-cloud-parent @@ -24,22 +24,22 @@ com.chinaunicom.ebtp mall-ebtp-cloud-jpa-starter - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT com.chinaunicom.ebtp mall-ebtp-cloud-kafka-starter - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT com.chinaunicom.mall.ebtp uboot-common - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT com.chinaunicom.mall.ebtp uboot-core - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT @@ -48,6 +48,26 @@ org.springframework.boot spring-boot-starter-web + + + org.apache.tomcat.embed + tomcat-embed-core + + + org.apache.tomcat.embed + tomcat-embed-websocket + + + + + org.apache.tomcat.embed + tomcat-embed-core + 9.0.80 + + + org.apache.tomcat.embed + tomcat-embed-websocket + 9.0.80 org.springframework.boot diff --git a/pom.xml b/pom.xml index 3291304..4d07f81 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ com.chinaunicom.ebtp mall-ebtp-cloud - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT pom mall-ebtp-cloud diff --git a/uboot-common/pom.xml b/uboot-common/pom.xml index 9dd41f7..536ff02 100644 --- a/uboot-common/pom.xml +++ b/uboot-common/pom.xml @@ -6,13 +6,13 @@ com.chinaunicom.ebtp mall-ebtp-cloud-parent - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT ../mall-ebtp-cloud-parent com.chinaunicom.mall.ebtp uboot-common - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT uboot-common diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/jasypt/starter/JasyptStarterConfiguration.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/jasypt/starter/JasyptStarterConfiguration.java index 4175c16..bc5500b 100644 --- a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/jasypt/starter/JasyptStarterConfiguration.java +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/jasypt/starter/JasyptStarterConfiguration.java @@ -28,5 +28,38 @@ public class JasyptStarterConfiguration { return encryptor; } + public static String encryptStr(String encryptStr, String password) { + PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); + SimpleStringPBEConfig config = new SimpleStringPBEConfig(); + config.setPassword(password); + config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256"); + config.setKeyObtentionIterations("1000"); + config.setPoolSize("1"); + config.setProviderName("SunJCE"); + config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); + config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator"); + config.setStringOutputType("base64"); + encryptor.setConfig(config); + String enPw = encryptor.encrypt(encryptStr); + System.out.println("加密后:" + enPw); + String decrypt = encryptor.decrypt(enPw); + System.out.println("解密的字符串:" + decrypt); + return enPw; + } + public static void main(String[] args) { +// PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); +// SimpleStringPBEConfig config = new SimpleStringPBEConfig(); +// config.setPassword("uniom-ebtp"); +// config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256"); +// config.setKeyObtentionIterations("1000"); +// config.setPoolSize("1"); +// config.setProviderName("SunJCE"); +// config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); +// config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator"); +// config.setStringOutputType("base64"); +// encryptor.setConfig(config); +// String decrypt = encryptor.decrypt("VwMRvAmu9rP0TGa1REZL5khiOOZtI47GoRJeHBN9LRDkJ+heZ4vXQ82/scobMGKW"); + JasyptStarterConfiguration.encryptStr("ProdMall3_0531portal","uniom-ebtp"); + } } diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/UserAuthenticationEntryPoint.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/UserAuthenticationEntryPoint.java index 78bf737..57766dc 100644 --- a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/UserAuthenticationEntryPoint.java +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/UserAuthenticationEntryPoint.java @@ -15,8 +15,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -import static com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants.REMOTE_ACCESS_FAILURE; -import static com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants.TOKEN_EXPIRED; +import static com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants.*; /** * 通过实现EntryPoint接口,自定义spring security异常返回 @@ -42,6 +41,7 @@ public class UserAuthenticationEntryPoint implements AuthenticationEntryPoint { response.setContentType(RESPONSE_CONTENT_TYPE); String code = (String) request.getSession().getAttribute("code");// security filter 返回的自定义状态码 + log.info("commence :"+code); //未获取到token 且不在白名单 if (authException instanceof InsufficientAuthenticationException) { code = REMOTE_ACCESS_FAILURE; @@ -67,6 +67,8 @@ public class UserAuthenticationEntryPoint implements AuthenticationEntryPoint { return accessDenidedException(code, response); case REMOTE_ACCESS_FAILURE: return remoteTimeoutException(code, response); + case REMOTE_ACCESS_CHECK: + return remoteCheckException(code, response); default: return globalException(code, response); } @@ -103,7 +105,21 @@ public class UserAuthenticationEntryPoint implements AuthenticationEntryPoint { return map; } + /** + * Token 远程认证服务超时 + * + * @param code + * @return + */ + private Map remoteCheckException(String code, HttpServletResponse response) { + response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); + Map map = new HashMap<>(); + map.put("code", code); + map.put("message", "无效请求"); + + return map; + } /** * @return */ diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/Constants.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/Constants.java index 401623b..d97cd5b 100644 --- a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/Constants.java +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/Constants.java @@ -9,6 +9,7 @@ public interface Constants { public static final String TOKEN_PREFIX = "Bearer "; public static final String CURRENT_ROLE_CODE = "currentRoleCode"; public static final String COOKIE_TOKEN_CODE = "mall3_token"; + public static final String HEADER_CHECK_TOKEN = "Mall3Check"; public static final String REDIS_USER_KEY = "ebtp:user_cache:"; public static final String REDIS_USER_KEY_EXTEND = "ebtp:user_cache_extend"; public static final String USERS = "users"; @@ -16,6 +17,7 @@ public interface Constants { public static final String TOKEN_EXPIRED = "90401"; public static final String REMOTE_ACCESS_FAILURE = "90500"; + public static final String REMOTE_ACCESS_CHECK = "90501"; String ACTUATOR_HEALTH = "actuator/health"; String ACTUATOR_PROMETHEUS = "actuator/prometheus"; diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/ParameterRequestWrapper.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/ParameterRequestWrapper.java new file mode 100644 index 0000000..3fcffd3 --- /dev/null +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/ParameterRequestWrapper.java @@ -0,0 +1,243 @@ +package com.chinaunicom.mall.ebtp.cloud.security.starter.common; + +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.*; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +public class ParameterRequestWrapper extends HttpServletRequestWrapper { + + + private Map params = new HashMap(); + private byte[] body; + + @SuppressWarnings("unchecked") + public ParameterRequestWrapper(HttpServletRequest request,String privateKey) { + // 将request交给父类,以便于调用对应方法的时候,将其输出, + // 其实父亲类的实现方式和第一种new的方式类似 + super(request); + String value = ""; + try { + String method = request.getMethod(); + String queryString = ""; + if("GET".equals(method)){ + queryString = request.getQueryString(); + }else if("POST".equals(method)) { + InputStream inputStream = request.getInputStream(); + if (inputStream != null) { + StringBuilder stringBuilder = new StringBuilder(); + BufferedReader bufferedReader = null; + bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + char[] charBuffer = new char[128]; + int bytesRead = -1; + while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { + stringBuilder.append(charBuffer, 0, bytesRead); + } + queryString = stringBuilder.toString(); + } + } + log.info("----请求 queryString: "+queryString); + if(queryString!=null&&!"".equals(queryString)) { + //String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJOGd3FrZ3nB3VeBnD86hEfKqqEQqfZWPx6wg1w8dEMrP94PGsdPzviyxh9F2RA9Th3XfIuHLC7fQPQn8i6wHUBrLQqXt+zNjO0/ViDtjwaaLDKePhsWiggVmTmXTAl3FyJcDgWCMr6gPhSf8uE/bSTk5JD5AswMEcElMP+6T1zFAgMBAAECgYAk2fsuTukbRi03db4FIWX31Q2IjHQYf/TmgqtduBBG0x/yJY3H6gzGnUnWeAlAAEBqPfDzncGQt94u32ek+ANmN161r22eBqoihHJQsCD3n3uFw8fdCRzpP9fiJVUFf3Yf/Di2G6cHKJ1Bs8H2GtmajeUUgjNNvJdQXLsTU2DBoQJBAPV9Luzcb62BO9GsL/C6nrxOEPnfTOvpQpBDjb4JnnS8G4CRoEERB8y0p/npD7wrhwG1aEpOj/6XUUz8yAqrA50CQQCZ139/lX0W6TGxZF5RQu9PlKW5/K74Ysd6tvuom93bwLG4zpCgfIoH3mSkWtKENvob7CV07T/irYppa/bIpRlJAkARnsqfdbMOYRhKFHHcdYivO2s8hCqhRDzi3fZujYIyHs5ajBlUkkRdDRqBdZkLiJRIKx0xODJds77CJ+Kz4VKBAkAUf14Bhc1w7Aku9YyLjzuLgubB7STy+1ZQx2iognwA95+2W+9xMqbBfaQzMdayvxB7/+7NsX69mRnEChqLiVPhAkEA6W9YOoZQlL6yCYE4GE3W38g0m7lq68XuVFgCpULEfR3s5bsPcqxC4xM29Ry80cEv6fAE+jht7JUq0ar/tRqfNA=="; + try { + value = RSAcheck.decryptDatafd(queryString, privateKey); + }catch (Exception e){ + log.error("解密失败,按照未加密参数使用"); + value = ""; + } + log.info("----请求 value: "+value); + if(value!=null&&!"".equals(value)) { + if("GET".equals(method)) { + String[] params = value.split("&"); + for (String param : params) { + String[] pvalue = param.split("="); + this.addParameter(pvalue[0], pvalue[1]); + //this.addParameterToBody(pvalue[0], pvalue[1]); + } + }else if("POST".equals(method)) { + this.body = getData(value).getBytes(); + } + }else{ + String json = queryString; + if (!StringUtils.isEmpty(json)) { + // body 赋值 + this.body = getData(json).getBytes(); + } + // 请求参数赋值 + this.params.putAll(request.getParameterMap()); + } + } + }catch (Exception e){ + log.error("异常",e); + } + } + + + + /** + * 重载一个构造方法-- 扩展参数 + * + * @param request + * @param extendParams + */ + public ParameterRequestWrapper(HttpServletRequest request, Map extendParams,String privateKey) { + this(request,privateKey); + //这里将扩展参数写入参数表 + addAllParameters(extendParams); + } + + /** + * 增加多个参数 + * + * @param otherParams + */ + public void addAllParameters(Map otherParams) { + for (Map.Entry entry : otherParams.entrySet()) { + addParameter(entry.getKey(), entry.getValue()); + } + } + + /** + * 增加参数 + */ + public void addParameter(String name, Object value) { + if (value != null) { + if (value instanceof String[]) { + params.put(name, (String[]) value); + } else if (value instanceof String) { + params.put(name, new String[]{(String) value}); + } else { + params.put(name, new String[]{String.valueOf(value)}); + } + } + } + + /** + * 增加body 参数 + * + * @param name + * @param value + */ + public void addParameterToBody(String name, Object value) { + byte[] json = this.body; + if (null == json) { + json = new byte[0]; + } + String jsonStr = new String(json); + try { + Map mapData = new HashMap<>(); + if(jsonStr!=null&&!"".equals(jsonStr)){ + mapData = JSONObject.parseObject(jsonStr, Map.class); + } + mapData.put(name, value); + this.body = JSONObject.toJSONString(mapData).getBytes(); + } catch (Exception ex) { + log.error("body体赋值错误",ex); + // 转换异常 + } + + } + + + /** + * body中参数解密 + * + * @param json + * @return + */ + private String getData(String json) { + //加密,如果传过来的是加密数据,先解密,未加密直接返回原json + + //不加密 + return json; + + } + + /** + * 获取body 参数 + * + * @param request + * @return + */ + public static String getPostData(HttpServletRequest request) { + StringBuilder data = new StringBuilder(); + String line; + BufferedReader reader; + try { + reader = request.getReader(); + while (null != (line = reader.readLine())) { + data.append(line); + } + } catch (IOException e) { + return null; + } + return data.toString(); + } + + @Override + public String getParameter(String name) {//重写getParameter,代表参数从当前类中的map获取 + String[] values = params.get(name); + if (values == null || values.length == 0) { + return null; + } + return values[0]; + } + + @Override + public String[] getParameterValues(String name) {//同上 + return params.get(name); + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + /** + * 在使用@RequestBody注解的时候,其实框架是调用了getInputStream()方法,所以我们要重写这个方法 + * + * @return + * @throws IOException + */ + @Override + public ServletInputStream getInputStream() throws IOException { + if (body == null) { + body = new byte[0]; + } + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() { + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + + @Override + public int read() throws IOException { + return bais.read(); + } + }; + } + public String getBody() { + return Base64.getEncoder().encodeToString(this.body); + } + +} diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/RSAcheck.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/RSAcheck.java new file mode 100644 index 0000000..1c405bf --- /dev/null +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/RSAcheck.java @@ -0,0 +1,456 @@ +package com.chinaunicom.mall.ebtp.cloud.security.starter.common; + +import javax.crypto.Cipher; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +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.text.SimpleDateFormat; +import java.util.*; + +public class RSAcheck { + + public static final String KEY_ALGORITHM = "RSA"; + public static final String SIGNATURE_ALGORITHM = "MD5withRSA"; + + private static final String PUBLIC_KEY = "RSAPublicKey"; + private static final String PRIVATE_KEY = "RSAPrivateKey"; + + /** + * 初始化密钥 + * + * @return + * @throws Exception + */ + public static Map initKey() throws Exception { + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); + keyPairGen.initialize(512); + KeyPair keyPair = keyPairGen.generateKeyPair(); + + // 公钥 + RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); +// logger.info("------" + publicKey); + // 私钥 + RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); +// logger.info("------" + publicKey); + + Map keyMap = new HashMap (2); + + keyMap.put(PUBLIC_KEY, publicKey); + keyMap.put(PRIVATE_KEY, privateKey); + return keyMap; + } + + /** + * 取得私钥 + * + * @param keyMap + * @return + * @throws Exception + */ + public static String getPrivateKey(Map keyMap) + throws Exception { + Key key = (Key) keyMap.get(PRIVATE_KEY); + return encryptBASE64(key.getEncoded()); + } + + /** + * 取得公钥 + * + * @param keyMap + * @return + * @throws Exception + */ + public static String getPublicKey(Map keyMap) + throws Exception { + Key key = (Key) keyMap.get(PUBLIC_KEY); + return encryptBASE64(key.getEncoded()); + } + + + /** + * 解密
+ * 用私钥解密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] decryptByPrivateKey(byte[] data, String key) + throws Exception { + // 对密钥解密 + byte[] keyBytes = decryptBASE64(key); + + // 取得私钥 + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); + + // 对数据解密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + return cipher.doFinal(data); + } + /** + * 解密
+ * 用私钥解密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static String decryptByPrivateKeyString(byte[] data, String key) + throws Exception { + return new String(decryptByPrivateKey(data, key)); + } + + + /** + * 解密
+ * 用公钥解密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] decryptByPublicKey(byte[] data, String key) + throws Exception { + // 对密钥解密 + byte[] keyBytes = decryptBASE64(key); + + // 取得公钥 + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key publicKey = keyFactory.generatePublic(x509KeySpec); + + // 对数据解密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + + return cipher.doFinal(data); + } + /** + * 加密
+ * 用公钥加密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static String encrypt(String data, String key) + throws Exception { + byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); + byte[] value = encryptByPublicKey(dataBytes,key); + return encryptBASE64(value); + } + /** + * 加密
+ * 用公钥加密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] encryptByPublicKey(byte[] data, String key) + throws Exception { + // 对公钥解密 + byte[] keyBytes = decryptBASE64(key); + + // 取得公钥 + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key publicKey = keyFactory.generatePublic(x509KeySpec); + + // 对数据加密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + + return cipher.doFinal(data); + } + + /** + * 加密
+ * 用私钥加密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] encryptByPrivateKey(byte[] data, String key) + throws Exception { + // 对密钥解密 + byte[] keyBytes = decryptBASE64(key); + + // 取得私钥 + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); + + // 对数据加密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + + return cipher.doFinal(data); + } + + /** + * 用私钥对信息生成数字签名 + * + * @param data + * 加密数据 + * @param privateKey + * 私钥 + * + * @return + * @throws Exception + */ + public static String sign(byte[] data, String privateKey) throws Exception { + // 解密由base64编码的私钥 + byte[] keyBytes = decryptBASE64(privateKey); + + // 构造PKCS8EncodedKeySpec对象 + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + + // KEY_ALGORITHM 指定的加密算法 + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + + // 取私钥匙对象 + PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); + + // 用私钥对信息生成数字签名 + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initSign(priKey); + signature.update(data); + + return encryptBASE64(signature.sign()); + } + + /** + * 校验数字签名 + * + * @param data + * 加密数据 + * @param publicKey + * 公钥 + * @param sign + * 数字签名 + * + * @return 校验成功返回true 失败返回false + * @throws Exception + * + */ + public static boolean verify(byte[] data, String publicKey, String sign) + throws Exception { + + // 解密由base64编码的公钥 + byte[] keyBytes = decryptBASE64(publicKey); + + // 构造X509EncodedKeySpec对象 + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + + // KEY_ALGORITHM 指定的加密算法 + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + + // 取公钥匙对象 + PublicKey pubKey = keyFactory.generatePublic(keySpec); + + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initVerify(pubKey); + signature.update(data); + + // 验证签名是否正常 + return signature.verify(decryptBASE64(sign)); + } + + public static byte[] decryptBASE64(String key) throws Exception { + return Base64.getDecoder().decode(key); + } + + private static String encryptBASE64(byte[] key) throws Exception { + return Base64.getEncoder().encodeToString(key); + } + + public static void main(String[] args) { + Map keyMap; + try { + //keyMap = initKey(); + //Map map = initKey(); + //System.out.println(map); + //System.out.println(keyMap); + //取得公钥和么私钥 + //keyMap = initKey(); + String publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJOzdznkEvViWv7+XV/7PfwwiqSfJDo0dWa4dV/kxffLnahmxgVposlZNkqhjwOEPZFAlsOOgqHIxNOO4MeQBa0CAwEAAQ=="; + //getPublicKey(keyMap); + // // + System.out.println("字符类型公钥:" + publicKey); + + String privateKey = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAk7N3OeQS9WJa/v5dX/s9/DCKpJ8kOjR1Zrh1X+TF98udqGbGBWmiyVk2SqGPA4Q9kUCWw46CocjE047gx5AFrQIDAQABAkAIHG/stvCvlxImNLPOBI8X3VaPycmEhML5vCF9/aM9g1SuFa298Q5W8FqAmm8SE5lRpw2yyToWtLbufJtAa7wFAiEAxViJBkLU4wfPCwiPiAn17owXbocC9rj3fAzEH9DYDdcCIQC/mZp4ujO035Qqw2QQeFWpDc/vITx1OTWaxq6/LvvwGwIgXTZLSmzItw9aKOD7QotJ4UnES41zxetp4er5u/leA3MCIGcRw2ZEjII1b+hdOdweT75kfsId9/77apm7Xc/c/4yXAiEAnBrCiVXRNN+slO0MYaxynr4eIiPG/EjYBYxXlwBpeOc="; + //getPrivateKey(keyMap);// + System.out.println("字符类型私钥:" + privateKey); + + System.out.println("公钥加密——私钥解密---------------"); + SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); + String source = "831357ef-3a80-40b3-9563-6451a7a8d605_"+(format.format(new Date())); +// System.out.println("\r加密前文字:\r\n" + source); +// byte[] data = source.getBytes(); +// byte[] encodedData = encryptByPublicKey(data, publicKey); +// +// System.out.println("加密后文字:\r\n" + encryptBASE64(encodedData)); +// byte[] decodedData = decryptByPrivateKey(encodedData, privateKey); +// String target = new String(decodedData); +// System.out.println("解密后文字: \r\n" + target); + + System.out.println("\r加密前文字:\r\n" + source); + String ciphertext = encryptDatafd(source, publicKey); + System.out.println(ciphertext); + + String data = decryptDatafd(ciphertext, privateKey); + System.out.println(data); + String data2 = decryptDatafd("IaDxV6UytgKPh2g/4fh2jvuiKJV7bRrbaSG7jqZ+94oOsCsxvL1wMl09tAF8B2N2Eex4vKtt/LQvouLJXdlBVg==", privateKey); + System.out.println(data2); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * String转私钥PrivateKey + * + * @param key + * @return + * @throws Exception + */ + public static PrivateKey getPrivateKey(String key) throws Exception { + byte[] keyBytes; + keyBytes = org.apache.commons.codec.binary.Base64.decodeBase64(key); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(keySpec); + return privateKey; + } + // 最大明文加密长度(单位:字节) + private final static int MAX_ENCRYPT_BLOCK = 245; + + // 最大密文解密长度(单位:字节) + private final static int MAX_DECRYPT_BLOCK = 256; + /** + * 分段加密数据 + * + * @param data 待加密的数据 + * @param publicKeyStr 公钥字符串 + * @return 经过Base64编码的加密好的数据 + * @throws Exception + */ + public static String encryptDatafd(String data, String publicKeyStr) throws Exception { + // 1.将公钥字符串转换为字节数组 + byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr); + + // 2.根据公钥字符串生成publicKey + X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyBytes); + KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM); + PublicKey publicKey = factory.generatePublic(spec); + + // 3.将待加密数据字符串转换为字节数组 + byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); + + // 4.分段加密 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + int dataLength = dataBytes.length; + int offSet = 0; + byte[] cache; + + while (dataLength - offSet > 0) { + if (dataLength - offSet > MAX_ENCRYPT_BLOCK) { + cache = cipher.doFinal(dataBytes, offSet, MAX_ENCRYPT_BLOCK); + } else { + cache = cipher.doFinal(dataBytes, offSet, dataLength - offSet); + } + output.write(cache, 0, cache.length); + + offSet += MAX_ENCRYPT_BLOCK; + } + output.close(); + + // 5.返回Base64编码后的字符串 + return Base64.getEncoder().encodeToString(output.toByteArray()); + } + /** + * 用私钥 解密 + * + * @param encryptionBase64Str RSA加密后又base64编码后的字符串 + * @return 解密结果 + * @throws Exception + */ + public static String decrypt(String encryptionBase64Str,String priKey) throws Exception { + //String priKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANJ1Z5PNYU6HTG7nSFcnoPzGJaOQQYqCnQVnsF1GeFZIxdGNx1yweGVyweOljcJijRIWjFWRV23DiLpS2KkHzx72fMRKaMn/NqXuDxqnLquBmNDCutprmJ+v1RVkkd3GHPLiRAizfKoMcQpqK0Ax9hY4oIX7bLi1u/t4dSaNH40PAgMBAAECgYBNMJx1vF1VNRCWmKOSu8kzDOXfMNipGbDHu7kW7PLUCsrrSgn1+A+gfc+ZVC5DPmwpVzXPaIhdjNsII7ytely/fw6BZjoToIk31QZDOAz4CpImzSiwnrVpLiDLZH7fykqUOlXomjc6qVpKfjvOUQHNF46EmCNlb6QX/ZWJwFPPsQJBAPKsuaT14JL98kLUXhuv/yV/FRMqgSoskEQD9JRjWbXuSlekx13f/ODTfBHMwX/hTofjAOtQyPs/bI5JtfrllLcCQQDeA9F3y/315h2rDtraJ9HM37pZ6MppAj2VkO7/uLiZX/Pswz45JUyTu5dkbsM7YgZ1iixfDzw32FBmtULRtOJpAkEAsx5khGrr0PNZ/pPrxnn4VL/hWV01K2DrNxjITUKrs8pn2xsIALVUXMocuLKuXGp74XbfddESF8jvonKrvL36LwJBAM2a8aElDLNugelJ34oLOHOoejcLrHAlQA1EZOH0GxkHUKnigrx/e3SRVPoQzcMv29V8uezq+hSpwoPrt87n2okCQFsULqFWm+COhWTkCuye27ipR6w3F7JSmj96Y2773F2LAb//vlsRftz517AcbHX74G4Icy02OV2AToyUi9lL9lo=";//getPrivateKey(keyMap); + // base64 解码 + byte[] base64 = org.apache.commons.codec.binary.Base64.decodeBase64(encryptionBase64Str); + // String 转 私钥decryptByPrivateKey + PrivateKey rsaPrivateKey = getPrivateKey(priKey); + //私钥解密 + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] result = cipher.doFinal(base64); + return new String(result); + } + /** + * 分段解密数据 + * + * @param ciphertext 密文 + * @param privateKeyStr 私钥字符串 + * @return 解密后的数据 + * @throws Exception + */ + public static String decryptDatafd(String ciphertext, String privateKeyStr) throws Exception { + // 1.将私钥字符串转换为字节数组 + byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr); + + // 2.根据私钥字符串生成privateKey + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyBytes); + KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM); + PrivateKey privateKey = factory.generatePrivate(spec); + + // 3.解码Base64字符串,转换为字节数组 + byte[] dataBytes = Base64.getDecoder().decode(ciphertext); + + // 4.分段解密 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + int dataLength = dataBytes.length; + int offSet = 0; + byte[] cache; + + while (dataLength - offSet > 0) { + if (dataLength - offSet > MAX_DECRYPT_BLOCK) { + cache = cipher.doFinal(dataBytes, offSet, MAX_DECRYPT_BLOCK); + } else { + cache = cipher.doFinal(dataBytes, offSet, dataLength - offSet); + } + output.write(cache, 0, cache.length); + + offSet += MAX_DECRYPT_BLOCK; + } + output.close(); + // 返回解密好的原始数据 + return output.toString(); + } +} diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/RsaTest.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/RsaTest.java new file mode 100644 index 0000000..7be4840 --- /dev/null +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/RsaTest.java @@ -0,0 +1,127 @@ +package com.chinaunicom.mall.ebtp.cloud.security.starter.common; + +import javax.crypto.Cipher; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +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.Base64; +import java.util.HashMap; +import java.util.Map; + +public class RsaTest { + + // 算法类型 + private final static String KEY_ALGORITHM = "RSA"; + + // 最大明文加密长度(单位:字节) + private final static int MAX_ENCRYPT_BLOCK = 245; + + // 最大密文解密长度(单位:字节) + private final static int MAX_DECRYPT_BLOCK = 256; + + /** + * 分段加密数据 + * + * @param data 待加密的数据 + * @param publicKeyStr 公钥字符串 + * @return 经过Base64编码的加密好的数据 + * @throws Exception + */ + public static String encryptData(String data, String publicKeyStr) throws Exception { + // 1.将公钥字符串转换为字节数组 + byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr); + + // 2.根据公钥字符串生成publicKey + X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyBytes); + KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM); + PublicKey publicKey = factory.generatePublic(spec); + + // 3.将待加密数据字符串转换为字节数组 + byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); + + // 4.分段加密 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + int dataLength = dataBytes.length; + int offSet = 0; + byte[] cache; + + while (dataLength - offSet > 0) { + if (dataLength - offSet > MAX_ENCRYPT_BLOCK) { + cache = cipher.doFinal(dataBytes, offSet, MAX_ENCRYPT_BLOCK); + } else { + cache = cipher.doFinal(dataBytes, offSet, dataLength - offSet); + } + output.write(cache, 0, cache.length); + + offSet += MAX_ENCRYPT_BLOCK; + } + output.close(); + + // 5.返回Base64编码后的字符串 + return Base64.getEncoder().encodeToString(output.toByteArray()); + } + + /** + * 分段解密数据 + * + * @param ciphertext 密文 + * @param privateKeyStr 私钥字符串 + * @return 解密后的数据 + * @throws Exception + */ + public static String decryptData(String ciphertext, String privateKeyStr) throws Exception { + // 1.将私钥字符串转换为字节数组 + byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr); + + // 2.根据私钥字符串生成privateKey + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyBytes); + KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM); + PrivateKey privateKey = factory.generatePrivate(spec); + + // 3.解码Base64字符串,转换为字节数组 + byte[] dataBytes = Base64.getDecoder().decode(ciphertext); + + // 4.分段解密 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + int dataLength = dataBytes.length; + int offSet = 0; + byte[] cache; + + while (dataLength - offSet > 0) { + if (dataLength - offSet > MAX_DECRYPT_BLOCK) { + cache = cipher.doFinal(dataBytes, offSet, MAX_DECRYPT_BLOCK); + } else { + cache = cipher.doFinal(dataBytes, offSet, dataLength - offSet); + } + output.write(cache, 0, cache.length); + + offSet += MAX_DECRYPT_BLOCK; + } + output.close(); + // 返回解密好的原始数据 + return output.toString(); + } + + + public static void main(String[] args) throws Exception { + + String publicKeyStr = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArF9CK/qMzej1vifFCINwp+7V4otEtVpCP0SIflQMvXjFqDyMuWZS4epwbZ2D6Ko5n3GPtHXs9vcVvw2kaGrmuKDbbASQyVlVCY1y91AsTJCTaF6DRa7cY/rYwvqHOc3XZZT86mes0MZmulGDt0V/0p7LdglZpBJrAXg9ANvIcBW8eQERleQPhszYPzQP3YUD6ogI3u6QRhR8uVSWqVrs5naVuNROTuZk/6ZG6rpXrzjniYLYT8TfT41fcL5JClDM51I96s9fJw3mfHFe9qlkWj9pdB76G44+mR+UHAvP96RQ35c6HmsLOu6I8PV0BM5+OzU0l5qVltmgs3Yr9RM5EwIDAQAB"; + String privateKeyStr = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCsX0Ir+ozN6PW+J8UIg3Cn7tXii0S1WkI/RIh+VAy9eMWoPIy5ZlLh6nBtnYPoqjmfcY+0dez29xW/DaRoaua4oNtsBJDJWVUJjXL3UCxMkJNoXoNFrtxj+tjC+oc5zddllPzqZ6zQxma6UYO3RX/Snst2CVmkEmsBeD0A28hwFbx5ARGV5A+GzNg/NA/dhQPqiAje7pBGFHy5VJapWuzmdpW41E5O5mT/pkbqulevOOeJgthPxN9PjV9wvkkKUMznUj3qz18nDeZ8cV72qWRaP2l0Hvobjj6ZH5QcC8/3pFDflzoeaws67ojw9XQEzn47NTSXmpWW2aCzdiv1EzkTAgMBAAECggEAdWAsfCwRw4lWBZWpOwHeLcyaArkZIXED2Xc+ht+PCVp7JfONVBZUbBgrVMlE8KMxt9wpohYHNajNOxr8EEpzL9gBco2tVh6ppGaYmcYTVFPCvPhW5ZWL590By8uzV25OtZJ9otTUPhpMC6XEToFZ6D9PhuIZE3ujOA37ZGFFHOTfOaumAO/JOAywib+PzyqVOeqbLn9fxeMwiV7FQv4tfykmRjdidyXF51T1GYhKS4U15Rj9UEx5PUJlc36SWY/9bWOGSoEZd+JhzlXP88kK2kttiWj/exX8FYWFbeaRhPkK4YQ6+i3MbNxqz0NRJaEKQmCFtw1fSRC6NzlvuYZ7AQKBgQDybNJZf0M0x9NltOIzycu6MyOTIeO3tCUapQlxa5e7KzZS7j320f+juTNw4O3pt5Cklsb5CwGZPn/09RXC67ULRCtPHPHz69n19dNa1tVofu4jUT/UYTfe54xnGBtx1XMBEhWVJkucd6aaUr5yiO50uavPRHZmoUUA92aM0wr4gQKBgQC2BjkamkTxwvNFior8VDAHq376mXewBf6ybSS5lMamiOvqYpCONeCPMJOK84QYFSJI3c1iFKcKpm5rt41kNfE6uEkPjC4zFUZAhafdKi03V3VG3ZzSxFytWzt4wztf/TNq96JZynZHY837qh4D+UuouWNOKdmXMY+IvM2Sk3sHkwKBgQC0WAQ8FBJ5B1baSLAmeq6WPEjwwbtYBCm+Ipxdxf7AfKsTEq0CGsMklzgPdyVGQwrVhl1LE8cCq54hKtofgZ3TXckiN5Q/M2uYMGIlJ8Dm1dZua5kic5hOuM6YOzTfgDznxP5NUInbQp+sGnYXWoqaRy3rKTEztDFTQLkHdlCpAQKBgQCxUFSWLotUuuTB7FjBvrze69eRBKiL5vsaEqoAwgXVOnId65AHiEJNGjPP2rHlx8iTFMW6coXaIRBVjAOHB+kKm3RIWfSzPFkoB0rjbe+IBoEu3DilNDVXhhTj6cLQGdXZsIsNTuVzW6zPKAN/OXzTSmyLOsdEujmpKMpUq6fgUwKBgGHOzITAmMZXn3D9Yh4foPy2kNc9ME/Q2z0A5AKdY4HtZ1hmsmsGjUTPbtGXIJRxYeQHwoDI1xQKbvRJXaG33ulNnvuMl0XSr4XA7uYoAgSL1I+3xrTxTShAsDb42uE5KCsd969cyNfeNdwxF7S8KH0yKcUy5BqOdX4pUlrEQHnI"; + + String ciphertext = encryptData("{\"maxAmount\":{\"amount\":1,\"projectId\":\"1744965355372617728\"},\"roomType\":\"2\",\"voList\":[{\"annoStructuredType\":\"1\",\"docEndTime\":null,\"docStartTime\":null,\"expenses1Amount\":\"0\",\"expenses1Type\":\"0\",\"expenses4Amount\":null,\"expenses4Type\":\"\",\"expenses5Amount\":\"0\",\"expenses5Type\":null,\"key\":0,\"orderState\":null,\"payment4Method\":\"\",\"projectId\":\"1744965355372617728\",\"sectionId\":\"1744965355389394944\",\"sectionName\":\"结构化202401-1\",\"sendOut\":0},{\"annoStructuredType\":\"1\",\"docEndTime\":null,\"docStartTime\":null,\"expenses1Amount\":\"0\",\"expenses1Type\":\"0\",\"expenses4Amount\":null,\"expenses4Type\":\"\",\"expenses5Amount\":\"0\",\"expenses5Type\":null,\"key\":1,\"orderState\":null,\"payment4Method\":\"\",\"projectId\":\"1744965355372617728\",\"sectionId\":\"1744965355393589251\",\"sectionName\":\"结构化202401-2\",\"sendOut\":0}]}", publicKeyStr); + System.out.println(ciphertext); + + String data = decryptData(ciphertext, privateKeyStr); + System.out.println(data); + } +} diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/filter/TokenAuthenticationFilter.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/filter/TokenAuthenticationFilter.java index b43e66b..053ab11 100644 --- a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/filter/TokenAuthenticationFilter.java +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/filter/TokenAuthenticationFilter.java @@ -3,19 +3,26 @@ package com.chinaunicom.mall.ebtp.cloud.security.starter.filter; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.exceptions.ExceptionUtil; import com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants; +import com.chinaunicom.mall.ebtp.cloud.security.starter.common.RSAcheck; import com.chinaunicom.mall.ebtp.cloud.security.starter.entity.AuthAllows; import com.chinaunicom.mall.ebtp.cloud.security.starter.entity.RoleCodeAuthority; import com.chinaunicom.mall.ebtp.cloud.security.starter.entity.SecurityUser; import com.chinaunicom.mall.ebtp.cloud.userinfo.starter.service.UserInfoService; import com.chinaunicom.mall.ebtp.common.base.entity.BaseCacheUser; +import com.chinaunicom.mall.ebtp.common.exception.common.CommonExceptionEnum; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RegExUtils; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.remoting.RemoteTimeoutException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; @@ -25,6 +32,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -37,14 +45,30 @@ import static com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants. * @author Ajaxfan */ @Slf4j +@Component public class TokenAuthenticationFilter extends OncePerRequestFilter { @Autowired private UserInfoService client; + //@Value("${http.rsa.privateKey}") + private String privateKey="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCIbZigdtFhreIKKBesIrgPhZoRgbMALpTjnNtAibPjmJJMSuWDnHgvSLpCaqNuCEm2GG5dRcZHTc2HWqGz00AJBxmMvrY8H2OqSAF0DuHGlMFZ4k2rpwY1VK9EJbN1/dTm9ZHxEvCRrUgZAQ1In3pUwflNgriou0/2MMoCL9i22TrAd/x835MCA0H0SxSGr02GhZRVmQMYo7axprr4/7RBUpynwh2ERKyZ6kzPF2qk4NW6lWXQSitOytVHagfxDfIEGjti1t4cnbD8n1DfjmBBpaUrJ2x9JX3Vxp6Gc0OCuqKJSxP73roibop1OXlXFJNn3Ansa0tiEkWuyPIqDAcnAgMBAAECggEAJeFwcJWtO5cBXVqWgBg/zSeGiPffUNyeQLjr8/aIOVjCvmZJZgrLIC3sccaUTFgGx4XvFvzpuiCzQbL7lSfB7v/Gq1rE1NrOXJiTtrrvG6lwKln4wQGmaqZx59UkCE4LkHl9JVfh7kET9MVi/9gwKlqVs1zpkwg3gp83Z/YLEXHFwlgMadDe7vxf/gTTdgH2mPYxI304hUj/lKlomFLiNx1Tn1KGphdklYa+Vx8fjfzmxp+aoxbLrLqnV90udDCQPHy7WzjW2+cYR8rbZmzR/zIbJxz1iIvG0GDVgJti9aJi3qujCAJ84fhG5yHpImIP6VjyHLwv61duPD3zla0bKQKBgQDEa3H2STgfwecK5T4gY72tNf1ZAtv7QajAkRcVoYEuUVSMtSvGZA3XLOjZKF1B8AOjdnxrIs2xh2ig+36EfpMTEwzkB/DBEIbnzCCdCedj+THysUImpdiHrhXpYO6GzvZMUgBtQvPLx2H3hosz0VS0/RiUYCMhIC7gWkp2lSLF6wKBgQCxz6GNW33MMSMjD/CAOwI333f0DtNtWWK+rMeQRXO2EKMg+aIk3Yez3C62EcQYgysm4J2Y0r84BDXKL795sVKWZBMY67qSMntfD2PxI9rHYttKd2sCttrZUB0Btr8j+hp0nd7EGhDVHFvWFqMgMfv+DQAoP1ah0Oa1Mzvto2RItQKBgCc1M2hwMS3VrOL147LfdgtPTaUo9vRupNrbm3oL5Rdz0KbNu1E8w66CHnDKp+LzoCZPDoLsTZb0aqRHz3dvNLiIAvi5xQsk22kFzDTMt02zfo7tZ0EDtfdPfrw5RyPNyEhvzfoBdVzmDd41EzBixr/iDbgAgMKqA/Fw1JBbMLfFAoGBAIFaBrCK6kztQh5zrgXa55Y131GQp4ZZDFih4Nu3aqH8IHAQqkAfWGUjvGd/LSSX54B0zrEqPylFBsx7R9QDh40YaDws4KESUGWrEI2QA8wgwl692P80fqiweymQBCTsAOrtHe2S+MPPyY9JEIqPUj5SvtaInUFCc1NGXkZWItIxAoGACKFRZOwxSUgGiK3YkQILO7Jy7jS0EMG8Bo0tuxjgBRCB2QgUe5TzFpkjSOjgqID5NRaFmthsfpOWmLgANne59tCozjmbGhLKSb+py/j3r5w2uWnDDVrZ0hcPqIFR+8y6c1sus5VnaKfDd/Sm4Tc4DFPNld+CKnJPtMxZUE51m78="; + @Value("${check.tokentime.timeLimit}") + private String kswTimeLimit;// = "20"; + @Value("${check.tokentime.onof}") + private String onof;// = "0"; + @Value("${check.tokentime.checkprivateKey}") + private String checkprivateKey;//="MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAk7N3OeQS9WJa/v5dX/s9/DCKpJ8kOjR1Zrh1X+TF98udqGbGBWmiyVk2SqGPA4Q9kUCWw46CocjE047gx5AFrQIDAQABAkAIHG/stvCvlxImNLPOBI8X3VaPycmEhML5vCF9/aM9g1SuFa298Q5W8FqAmm8SE5lRpw2yyToWtLbufJtAa7wFAiEAxViJBkLU4wfPCwiPiAn17owXbocC9rj3fAzEH9DYDdcCIQC/mZp4ujO035Qqw2QQeFWpDc/vITx1OTWaxq6/LvvwGwIgXTZLSmzItw9aKOD7QotJ4UnES41zxetp4er5u/leA3MCIGcRw2ZEjII1b+hdOdweT75kfsId9/77apm7Xc/c/4yXAiEAnBrCiVXRNN+slO0MYaxynr4eIiPG/EjYBYxXlwBpeOc="; + @Value("${check.tokentime.checkpublicKey}") + private String checkpublicKey;//"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJOzdznkEvViWv7+XV/7PfwwiqSfJDo0dWa4dV/kxffLnahmxgVposlZNkqhjwOEPZFAlsOOgqHIxNOO4MeQBa0CAwEAAQ=="; @Autowired private AuthAllows allows; + //private String checkprivateKey="MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAk7N3OeQS9WJa/v5dX/s9/DCKpJ8kOjR1Zrh1X+TF98udqGbGBWmiyVk2SqGPA4Q9kUCWw46CocjE047gx5AFrQIDAQABAkAIHG/stvCvlxImNLPOBI8X3VaPycmEhML5vCF9/aM9g1SuFa298Q5W8FqAmm8SE5lRpw2yyToWtLbufJtAa7wFAiEAxViJBkLU4wfPCwiPiAn17owXbocC9rj3fAzEH9DYDdcCIQC/mZp4ujO035Qqw2QQeFWpDc/vITx1OTWaxq6/LvvwGwIgXTZLSmzItw9aKOD7QotJ4UnES41zxetp4er5u/leA3MCIGcRw2ZEjII1b+hdOdweT75kfsId9/77apm7Xc/c/4yXAiEAnBrCiVXRNN+slO0MYaxynr4eIiPG/EjYBYxXlwBpeOc="; + + @Autowired(required = false) + @Qualifier("userinfoRedisTemplate") + private RedisTemplate redisTemplate; /** * @param request * @param response @@ -55,11 +79,27 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException { + //解密 恢复请求参数 + //ParameterRequestWrapper requestWrapper = new ParameterRequestWrapper((HttpServletRequest) request,privateKey); + String api = request.getRequestURI(); String method = request.getMethod(); if (!StringUtils.contains(api, ACTUATOR_PROMETHEUS)) { log.info("--------" + method + " - " + api + "?" + Optional.ofNullable(request.getQueryString()).orElse("")); } + String check_header = request.getHeader(HEADER_CHECK_TOKEN); + log.info("HEADER_CHECK_TOKEN:"+HEADER_CHECK_TOKEN); + log.info("check_header:"+check_header); + String isFeginKey = request.getHeader("isFegin"); + log.info("isFeginKey: " + isFeginKey); + String isFegin = null; + if(isFeginKey!=null&&!"".equals(isFeginKey)) { + try { + isFegin = RSAcheck.decrypt(isFeginKey, checkprivateKey); + } catch (Exception e) { + log.error("isFegin 解密异常", e); + } + } // 清空上下文中的缓存信息, 防止二次请求时数据异常 (如此, 每次有新的请求进入,都会进行token的验证) SecurityContextHolder.getContext().setAuthentication(null); @@ -77,11 +117,13 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { final String header = request.getHeader(AUTHORIZATION_HEADER); final String currentRoleCode = request.getHeader(CURRENT_ROLE_CODE); log.debug("header:{},currentRoleCode:{}", header, currentRoleCode); - + Boolean tokenCheckB = false; try { + // 检查请求头是否包含 Bearer 前缀 if (StringUtils.startsWith(header, Constants.TOKEN_PREFIX)) { setAuthentication(currentRoleCode, RegExUtils.replaceAll(header, Constants.TOKEN_PREFIX, ""), isWhite);// 移除header的前缀,提取出token字串 + tokenCheckB = true; } // 检查cookie else { @@ -102,9 +144,110 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { ExceptionUtil.stacktraceToString(e); log.error(e.getMessage()); } + //校验token 时间戳 + log.info("校验token时间戳"); + if(tokenCheckB) { + if (!api.contains(ACTUATOR_HEALTH) && !api.contains(ACTUATOR_PROMETHEUS) + && (isFegin == null || !"isFegin".equals(isFegin))) { + BaseCacheUser buser = new BaseCacheUser(); + BeanUtils.copyProperties(SecurityContextHolder.getContext().getAuthentication().getPrincipal(), buser); + log.info("获取用户信息:" + buser); + if (buser != null && buser.getUserId() != null && !"".equals(buser.getUserId())) { + if (!checkTokenTime(request, response, filterChain)) { + request.getSession().setAttribute("code", "90501"); + CommonExceptionEnum.FRAME_EXCEPTION_COMMON_DATA_OTHER_ERROR.customValidName("无效请求", true); + } + } + } + } + + // 过滤链调用 filterChain.doFilter(request, response); } + /** + * 校验请求唯一性 + * @param request + */ + public Boolean checkTokenTime(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain){ + Boolean b = true; + String check_header = request.getHeader(HEADER_CHECK_TOKEN); + log.info("request check_header:"+check_header); + if("1".equals(onof)){ + return true; + } +// Optional optionalCookie = Optional.ofNullable(request.getCookies()) +// .flatMap(cookies -> +// Stream.of(cookies) +// .filter(item -> StringUtils.equals(item.getName(), COOKIE_TOKEN_CODE)) +// .findFirst()); + if (check_header!=null&&!"".equals(check_header)) { + String cookieKey = check_header;//optionalCookie.get().getValue(); + log.info("cookieKey:"+cookieKey); + Object o = redisTemplate.opsForValue().get(HEADER_CHECK_TOKEN+":"+cookieKey); + if (o != null) { + String num = String.valueOf(o); + log.info(HEADER_CHECK_TOKEN+":"+cookieKey+"= "+num); + if("2".equals(num)){ + log.error("请求连接已使用过"); + b = false; + }else{ + redisTemplate.opsForValue().set(HEADER_CHECK_TOKEN+":"+cookieKey, 2, 20, TimeUnit.SECONDS); + } + //request.getSession().setAttribute("code", "90501"); + //throw new RemoteTimeoutException(REMOTE_ACCESS_CHECK);//REMOTE_ACCESS_CHECK + //throw new BusinessException(CommonExceptionEnum.LOGIN_CHECK_TOKEN_EXPIRATION, "无效请求 h!"); + }else{ + redisTemplate.opsForValue().set(HEADER_CHECK_TOKEN+":"+cookieKey, 1, 20, TimeUnit.SECONDS); + } + + String header = request.getHeader(AUTHORIZATION_HEADER);//请求头token + header = RegExUtils.replaceAll(header, Constants.TOKEN_PREFIX, ""); + String cookieValue = ""; + try { + cookieValue = RSAcheck.decrypt(cookieKey, checkprivateKey); + }catch (Exception e){ + b = false; + //request.getSession().setAttribute("code", "90501"); + //throw new RemoteTimeoutException(REMOTE_ACCESS_CHECK);//REMOTE_ACCESS_CHECK + } + log.info("header :"+header); + log.info("cookieValue :"+cookieValue); + String[] checkValues = String.valueOf(cookieValue).split("_");//0 token 1 token 时间 + if (!header.equals(checkValues[0])) { + log.error("请求连接token不一致"); + b = false; + //request.getSession().setAttribute("code", "90501"); + //throw new RemoteTimeoutException(REMOTE_ACCESS_CHECK);//REMOTE_ACCESS_CHECK + //throw new BusinessException(CommonExceptionEnum.LOGIN_CHECK_TOKEN_EXPIRATION, "无效请求 token!"); + } + //SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); + long newDateLong = System.currentTimeMillis(); + long inDateLong = Long.valueOf(checkValues[1]).longValue(); + + log.info("newDateLong:"+newDateLong); + log.info("inDateLong:"+inDateLong); + log.info("kswTimeLimit:"+kswTimeLimit); + log.info("newDateLong - inDateLong :"+(newDateLong - inDateLong)); + log.info("newDateLong - inDateLong 结果 :"+((newDateLong - inDateLong) > Long.valueOf(kswTimeLimit).longValue())); + if ((newDateLong - inDateLong) > Long.valueOf(kswTimeLimit).longValue()) {// + log.error("请求已超时"); + //request.getSession().setAttribute("code", "90501"); + b = false; + //throw new RemoteTimeoutException(REMOTE_ACCESS_CHECK);//REMOTE_ACCESS_CHECK + //throw new BusinessException(CommonExceptionEnum.LOGIN_CHECK_TOKEN_EXPIRATION, "无效请求!time out"); + } + + } else { + log.error("请求未授权"); + //request.getSession().setAttribute("code", "90501"); + b = false; + //throw new RemoteTimeoutException(REMOTE_ACCESS_CHECK);//REMOTE_ACCESS_CHECK + //throw new BusinessException(CommonExceptionEnum.LOGIN_CHECK_TOKEN_EXPIRATION, "无效请求!"); + } + return b; + } + /** * 白名单验证 * diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/common/config/FeignConfig.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/common/config/FeignConfig.java index f59c0ca..9680629 100644 --- a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/common/config/FeignConfig.java +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/common/config/FeignConfig.java @@ -2,6 +2,7 @@ package com.chinaunicom.mall.ebtp.common.config; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.chinaunicom.mall.ebtp.cloud.security.starter.common.RSAcheck; import com.chinaunicom.mall.ebtp.cloud.security.starter.entity.AuthAllows; import feign.RequestInterceptor; import feign.RequestTemplate; @@ -12,8 +13,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpRange; -import org.springframework.http.HttpRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -45,6 +44,10 @@ public class FeignConfig implements RequestInterceptor { List tokenWhiteList; @Autowired private AuthAllows allows; + @Value("${check.tokentime.checkprivateKey}") + private String checkprivateKey;//="MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAk7N3OeQS9WJa/v5dX/s9/DCKpJ8kOjR1Zrh1X+TF98udqGbGBWmiyVk2SqGPA4Q9kUCWw46CocjE047gx5AFrQIDAQABAkAIHG/stvCvlxImNLPOBI8X3VaPycmEhML5vCF9/aM9g1SuFa298Q5W8FqAmm8SE5lRpw2yyToWtLbufJtAa7wFAiEAxViJBkLU4wfPCwiPiAn17owXbocC9rj3fAzEH9DYDdcCIQC/mZp4ujO035Qqw2QQeFWpDc/vITx1OTWaxq6/LvvwGwIgXTZLSmzItw9aKOD7QotJ4UnES41zxetp4er5u/leA3MCIGcRw2ZEjII1b+hdOdweT75kfsId9/77apm7Xc/c/4yXAiEAnBrCiVXRNN+slO0MYaxynr4eIiPG/EjYBYxXlwBpeOc="; + @Value("${check.tokentime.checkpublicKey}") + private String checkpublicKey;//"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJOzdznkEvViWv7+XV/7PfwwiqSfJDo0dWa4dV/kxffLnahmxgVposlZNkqhjwOEPZFAlsOOgqHIxNOO4MeQBa0CAwEAAQ=="; /** * @param template @@ -54,6 +57,15 @@ public class FeignConfig implements RequestInterceptor { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); log.info("--------feign url- " + template.url()); String method = template.method(); + + try { + //long newDateLong = System.currentTimeMillis(); + String key = RSAcheck.encrypt("isFegin", checkpublicKey); + template.header("isFegin", key); + }catch (Exception e){ + log.error("fegin加密异常"); + } + if (Objects.nonNull(attributes)) { if (isNonExistsWhiteList(template.url())) { injectToken(template, attributes); @@ -70,6 +82,7 @@ public class FeignConfig implements RequestInterceptor { log.info("attributes is null=======accessToken===" + accessToken); template.header(HttpHeaders.AUTHORIZATION, String.format("%s%s", TOKEN_PREFIX, accessToken)); } + } diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/common/exception/common/CommonExceptionEnum.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/common/exception/common/CommonExceptionEnum.java index 761e90d..86c1212 100644 --- a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/common/exception/common/CommonExceptionEnum.java +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/common/exception/common/CommonExceptionEnum.java @@ -49,7 +49,11 @@ public enum CommonExceptionEnum implements BusinessExceptionAssert { /** * 登陆已超期 */ - LOGIN_EXPIRATION(90401, "登陆已超期"); + LOGIN_EXPIRATION(90401, "登陆已超期"), + /** + * 无效请求 + */ + LOGIN_CHECK_TOKEN_EXPIRATION(90501, "无效请求"); /** * 返回码 diff --git a/uboot-common/src/main/resources/application-common.yml b/uboot-common/src/main/resources/application-common.yml index 9a3d025..36cab29 100644 --- a/uboot-common/src/main/resources/application-common.yml +++ b/uboot-common/src/main/resources/application-common.yml @@ -150,3 +150,8 @@ management: cors: allowed-origins: "*" allowed-methods: "*" + +http: + rsa: + publicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNR+qWwx1SFELNYCk3vqqQ9wAgr7il6/yRPnwk/Vq+7UAlUyOrlTb9ZAKAxJE7OjoqQHpPJkXYypqlNkkpYrRHKc3lTeSXFL1AOU6idtidTC1W7STwNNYJ1RtFA7qqVd4C/+pbU0GHZ2OMsMbFr+b40pienLBg0FGGGCoRVbOSxwIDAQAB + privateKey: MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAM1H6pbDHVIUQs1gKTe+qpD3ACCvuKXr/JE+fCT9Wr7tQCVTI6uVNv1kAoDEkTs6OipAek8mRdjKmqU2SSlitEcpzeVN5JcUvUA5TqJ22J1MLVbtJPA01gnVG0UDuqpV3gL/6ltTQYdnY4ywxsWv5vjSmJ6csGDQUYYYKhFVs5LHAgMBAAECgYBJsk/d4B5eoTd6U9N4V9MUSBibo3o+1wHNgwk+nlY9xR8KR1a++srLHWRopikdgkHveUZvs+XPdq0eMucBHJZrcVT9ZT4ehPmCyRZzILjKqZugP5MpTj1CtM5SfQtxsfuJmM5LYvVX2lmYQw2k+MyWNfyrVfH6HVvu19l1sBqpAQJBAOW2LlqTACRPD3+IOFrHlRQvuUrLq38eRFZCRte2UEq0fMv8VKlgwZdMynD40LwAeO0Cz9huQkNFx4WGxOXthnECQQDkxf43WY6MltfTATYQy4h/TKHznKG+9dMNVFf3SkNECGh5IcW21v44s5X6zOl6zbPVtiySq/c2t9/IbbSc2/i3AkAa3q6ZZayUkrLrZhHBfKsRi2uPNje/TNkNhf8naGoH8wjOC5wTm//JJPBhOpmgBCYhAz0wweT6XYUzN0p84sXhAkEAh7ak7nTkSaaadgQ77I6xdMMjN/9tPHlcMIYegQa/DLboMyPDxImZ2k0+5b91qnIpAtjFbGUzjHRb/uyFvqAH1QJBAJsS6/GhMoCDyPdGQAMA+KylB1s6o8sowLx92q6oBs03idtvFxAocIej1m1UuCW+EpT+ZoFwOn+sdCm5VKfVoLQ= \ No newline at end of file diff --git a/uboot-core/pom.xml b/uboot-core/pom.xml index 2e221dd..b2aced9 100644 --- a/uboot-core/pom.xml +++ b/uboot-core/pom.xml @@ -6,13 +6,13 @@ com.chinaunicom.ebtp mall-ebtp-cloud-parent - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT ../mall-ebtp-cloud-parent com.chinaunicom.mall.ebtp uboot-core - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT uboot-core @@ -20,7 +20,7 @@ com.chinaunicom.mall.ebtp uboot-common - 2.2.0-SNAPSHOT + 2.3.0-SNAPSHOT