From 1475eb25737732b1a56f84d86cc1010912db409c Mon Sep 17 00:00:00 2001 From: Tang Cheng Date: Tue, 16 Apr 2019 22:59:02 +0800 Subject: [PATCH] =?utf8?q?=E5=AE=9E=E7=8E=B0=E4=BA=86=20jwt=20authenticati?= =?utf8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 + config/application-devel-oracle.properties | 8 +- config/application-devel-pg-local.properties | 19 ++ config/application-devel-pg.properties | 20 +- sql/init_test.sql | 4 + .../dlpay/framework/core/JwtTokenUtil.java | 59 +++++ .../dlpay/framework/dao/ApiClientDao.java | 7 + .../framework/domain/AppClientRedis.java | 39 ++++ .../dlpay/framework/domain/DateItem.java | 26 +++ .../dlpay/framework/domain/PersonRedis.java | 59 +++++ .../dlpay/framework/domain/TApiClient.java | 44 ++++ .../redisrepo/AppClientRepository.java | 7 + .../framework/redisrepo/PersonRepository.java | 7 + .../service/impl/SystemUtilServiceImpl.java | 56 ++++- .../dlpay/framework/util/HmacUtil.java | 213 ++++++++++-------- .../com/supwisdom/dlpay/DlpayApplication.kt | 58 +++-- .../controller/security_controller.kt | 91 ++++++++ 17 files changed, 594 insertions(+), 126 deletions(-) create mode 100644 config/application-devel-pg-local.properties create mode 100644 sql/init_test.sql create mode 100644 src/main/java/com/supwisdom/dlpay/framework/core/JwtTokenUtil.java create mode 100644 src/main/java/com/supwisdom/dlpay/framework/dao/ApiClientDao.java create mode 100644 src/main/java/com/supwisdom/dlpay/framework/domain/AppClientRedis.java create mode 100644 src/main/java/com/supwisdom/dlpay/framework/domain/DateItem.java create mode 100644 src/main/java/com/supwisdom/dlpay/framework/domain/PersonRedis.java create mode 100644 src/main/java/com/supwisdom/dlpay/framework/domain/TApiClient.java create mode 100644 src/main/java/com/supwisdom/dlpay/framework/redisrepo/AppClientRepository.java create mode 100644 src/main/java/com/supwisdom/dlpay/framework/redisrepo/PersonRepository.java create mode 100644 src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt diff --git a/build.gradle b/build.gradle index c273d3c6..68230629 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,7 @@ war { dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.security:spring-security-oauth2-client' @@ -47,6 +48,8 @@ dependencies { implementation files('libs/ojdbc6.jar') + annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" + providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' testImplementation 'org.springframework.boot:spring-boot-starter-test' } diff --git a/config/application-devel-oracle.properties b/config/application-devel-oracle.properties index fbfa731b..3a3d0a65 100644 --- a/config/application-devel-oracle.properties +++ b/config/application-devel-oracle.properties @@ -7,4 +7,10 @@ spring.jpa.hibernate.ddl-auto=create-drop spring.datasource.url=jdbc:oracle:thin:@172.28.201.101:47922:orcl spring.datasource.username=ecardtest spring.datasource.password=kingstar -spring.datasource.driver-class-oracle.jdbc.driver.OracleDriver \ No newline at end of file +spring.datasource.driver-class-oracle.jdbc.driver.OracleDriver + +# Redis settings +redis.server=172.28.201.101 +redis.port=16379 +redis.password=kingstar +redis.database=0 \ No newline at end of file diff --git a/config/application-devel-pg-local.properties b/config/application-devel-pg-local.properties new file mode 100644 index 00000000..39d4caf9 --- /dev/null +++ b/config/application-devel-pg-local.properties @@ -0,0 +1,19 @@ +spring.main.banner-mode=off +# create and drop tables and sequences, loads import.sql +spring.jpa.hibernate.ddl-auto=update +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false +# Postgresql settings +spring.datasource.url=jdbc:postgresql://localhost:5432/payapi +spring.datasource.username=payapi +spring.datasource.password=123456 +# Redis settings +redis.server=localhost +redis.port=6379 +redis.password= +redis.database=0 +# jwt settings +jwt.secret=Zj5taLomEbrM0lk+NMQZbHfSxaDU1wekjT+kiC3YzDw= +# timeout seconds +jwt.expiration=3600 +jwt.header=payapi diff --git a/config/application-devel-pg.properties b/config/application-devel-pg.properties index 64f9736a..92b7ecfb 100644 --- a/config/application-devel-pg.properties +++ b/config/application-devel-pg.properties @@ -1,11 +1,19 @@ spring.main.banner-mode=off - # create and drop tables and sequences, loads import.sql -spring.jpa.hibernate.ddl-auto=create-drop -spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect -spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false - -# Oracle settings +spring.jpa.hibernate.ddl-auto=update +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false +# Postgresql settings spring.datasource.url=jdbc:postgresql://172.28.201.70:15432/payapi spring.datasource.username=payapi spring.datasource.password=123456 +# Redis settings +redis.server=172.28.201.101 +redis.port=16379 +redis.password=kingstar +redis.database=0 +# jwt settings +jwt.secret=Zj5taLomEbrM0lk+NMQZbHfSxaDU1wekjT+kiC3YzDw= +# timeout seconds +jwt.expiration=3600 +jwt.header=payapi diff --git a/sql/init_test.sql b/sql/init_test.sql new file mode 100644 index 00000000..d4396a66 --- /dev/null +++ b/sql/init_test.sql @@ -0,0 +1,4 @@ +insert into tt_apiclient(appid, secret, status) +values ('100001', 'oUw2NmA09ficiVWD4TUQLDOkPyzQa3VzbjjsW0B2qTk=', 'normal'); + +commit; \ No newline at end of file diff --git a/src/main/java/com/supwisdom/dlpay/framework/core/JwtTokenUtil.java b/src/main/java/com/supwisdom/dlpay/framework/core/JwtTokenUtil.java new file mode 100644 index 00000000..e43e4117 --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/core/JwtTokenUtil.java @@ -0,0 +1,59 @@ +package com.supwisdom.dlpay.framework.core; + +import org.jose4j.jwk.JsonWebKey; +import org.jose4j.jws.AlgorithmIdentifiers; +import org.jose4j.jws.JsonWebSignature; +import org.jose4j.jwt.JwtClaims; +import org.jose4j.lang.JoseException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +@EnableAutoConfiguration +@Component +public class JwtTokenUtil { + @Value("${jwt.secret}") + private String secret; + @Value("${jwt.expiration}") + private Long expiration = 3600L; + @Value("${jwt.header}") + private String header = "supwisdom"; + + public String generateToken(Map params) throws JoseException { + JwtClaims claims = new JwtClaims(); + claims.setIssuer(params.get("issuer").toString()); // who creates the token and signs it + claims.setExpirationTimeMinutesInTheFuture(expiration / 60); // time when the token will expire (10 minutes from now) + claims.setGeneratedJwtId(); // a unique identifier for the token + claims.setIssuedAtToNow(); // when the token was issued/created (now) + claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago) + claims.setSubject(params.get("subject").toString()); // the subject/principal is whom the token is about + /* + claims.setClaim("email", "mail@example.com"); // additional claims/attributes about the subject can be added + List groups = Arrays.asList("group-one", "other-group", "group-three"); + claims.setStringListClaim("groups", groups); // multi-valued claims work too and will end up as a JSON array + */ + + Map keySpec = new HashMap<>(); + keySpec.put("kty", "oct"); + keySpec.put("k", secret); + JsonWebKey key = JsonWebKey.Factory.newJwk(keySpec); + JsonWebSignature jws = new JsonWebSignature(); + jws.setPayload(claims.toJson()); + jws.setKey(key.getKey()); + jws.setKeyIdHeaderValue(key.getKeyId()); + jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256); + return jws.getCompactSerialization(); + } + + public String generateToken(UserDetails userDetails) throws JoseException { + Map claims = new HashMap<>(); + claims.put("uid", userDetails.getUsername()); + return generateToken(claims); + } +} diff --git a/src/main/java/com/supwisdom/dlpay/framework/dao/ApiClientDao.java b/src/main/java/com/supwisdom/dlpay/framework/dao/ApiClientDao.java new file mode 100644 index 00000000..43903684 --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/dao/ApiClientDao.java @@ -0,0 +1,7 @@ +package com.supwisdom.dlpay.framework.dao; + +import com.supwisdom.dlpay.framework.domain.TApiClient; +import org.springframework.data.repository.CrudRepository; + +public interface ApiClientDao extends CrudRepository { +} diff --git a/src/main/java/com/supwisdom/dlpay/framework/domain/AppClientRedis.java b/src/main/java/com/supwisdom/dlpay/framework/domain/AppClientRedis.java new file mode 100644 index 00000000..a45f9f38 --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/domain/AppClientRedis.java @@ -0,0 +1,39 @@ +package com.supwisdom.dlpay.framework.domain; + +import org.springframework.data.redis.core.RedisHash; + +import javax.persistence.Id; + +@RedisHash("app_client") +public class AppClientRedis { + private @Id + String id; + + String token; + + String loginTimestamp; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getLoginTimestamp() { + return loginTimestamp; + } + + public void setLoginTimestamp(String loginTimestamp) { + this.loginTimestamp = loginTimestamp; + } +} diff --git a/src/main/java/com/supwisdom/dlpay/framework/domain/DateItem.java b/src/main/java/com/supwisdom/dlpay/framework/domain/DateItem.java new file mode 100644 index 00000000..f4aa56ec --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/domain/DateItem.java @@ -0,0 +1,26 @@ +package com.supwisdom.dlpay.framework.domain; + +import javax.persistence.*; +import java.sql.Date; + +@Entity +public class DateItem { + private Date date; + + /** + * @return the date + */ + @Id + @Column(name = "DATE_VALUE") + public Date getDate() { + return date; + } + + /** + * @param date + * the date to set + */ + public void setDate(Date date) { + this.date = date; + } +} diff --git a/src/main/java/com/supwisdom/dlpay/framework/domain/PersonRedis.java b/src/main/java/com/supwisdom/dlpay/framework/domain/PersonRedis.java new file mode 100644 index 00000000..2c258640 --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/domain/PersonRedis.java @@ -0,0 +1,59 @@ +package com.supwisdom.dlpay.framework.domain; + +import org.springframework.data.redis.core.RedisHash; + +import javax.persistence.Id; + +@RedisHash("person") +public class PersonRedis { + @Id + String id; + + String loginTimestamp; + + String roles; + + Boolean isAdmin; + + String accessToken; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLoginTimestamp() { + return loginTimestamp; + } + + public void setLoginTimestamp(String loginTimestamp) { + this.loginTimestamp = loginTimestamp; + } + + public String getRoles() { + return roles; + } + + public void setRoles(String roles) { + this.roles = roles; + } + + public Boolean getAdmin() { + return isAdmin; + } + + public void setAdmin(Boolean admin) { + isAdmin = admin; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } +} diff --git a/src/main/java/com/supwisdom/dlpay/framework/domain/TApiClient.java b/src/main/java/com/supwisdom/dlpay/framework/domain/TApiClient.java new file mode 100644 index 00000000..5f194652 --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/domain/TApiClient.java @@ -0,0 +1,44 @@ +package com.supwisdom.dlpay.framework.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "TT_APICLIENT") +public class TApiClient { + @Id + @Column(name = "appid", nullable = false, length = 20) + private String appid; + + @Column(name = "secret", nullable = false, length = 64) + private String secret; + + @Column(name = "status", nullable = false, length = 10) + private String status; + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/src/main/java/com/supwisdom/dlpay/framework/redisrepo/AppClientRepository.java b/src/main/java/com/supwisdom/dlpay/framework/redisrepo/AppClientRepository.java new file mode 100644 index 00000000..495a122d --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/redisrepo/AppClientRepository.java @@ -0,0 +1,7 @@ +package com.supwisdom.dlpay.framework.redisrepo; + +import com.supwisdom.dlpay.framework.domain.AppClientRedis; +import org.springframework.data.repository.CrudRepository; + +public interface AppClientRepository extends CrudRepository { +} diff --git a/src/main/java/com/supwisdom/dlpay/framework/redisrepo/PersonRepository.java b/src/main/java/com/supwisdom/dlpay/framework/redisrepo/PersonRepository.java new file mode 100644 index 00000000..a4925cdc --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/redisrepo/PersonRepository.java @@ -0,0 +1,7 @@ +package com.supwisdom.dlpay.framework.redisrepo; + +import com.supwisdom.dlpay.framework.domain.PersonRedis; +import org.springframework.data.repository.CrudRepository; + +public interface PersonRepository extends CrudRepository { +} diff --git a/src/main/java/com/supwisdom/dlpay/framework/service/impl/SystemUtilServiceImpl.java b/src/main/java/com/supwisdom/dlpay/framework/service/impl/SystemUtilServiceImpl.java index b4b8b48f..0cf7bcf2 100644 --- a/src/main/java/com/supwisdom/dlpay/framework/service/impl/SystemUtilServiceImpl.java +++ b/src/main/java/com/supwisdom/dlpay/framework/service/impl/SystemUtilServiceImpl.java @@ -4,6 +4,7 @@ import com.supwisdom.dlpay.framework.dao.SettleCtlDao; import com.supwisdom.dlpay.framework.dao.TaskLockDao; import com.supwisdom.dlpay.framework.dao.TranscodeDao; import com.supwisdom.dlpay.framework.data.SystemDateTime; +import com.supwisdom.dlpay.framework.domain.DateItem; import com.supwisdom.dlpay.framework.domain.TSettlectl; import com.supwisdom.dlpay.framework.domain.TTaskLock; import com.supwisdom.dlpay.framework.domain.TTranscode; @@ -14,8 +15,19 @@ import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.persistence.criteria.CriteriaBuilder; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.Date; + @Service public class SystemUtilServiceImpl implements SystemUtilService { + @PersistenceContext + EntityManager em; + @Autowired private TaskLockDao taskLockDao; @Autowired @@ -25,11 +37,45 @@ public class SystemUtilServiceImpl implements SystemUtilService { private static final Logger logger = Logger.getLogger(SystemUtilServiceImpl.class); + public static class SystemDateTimeImpl implements SystemDateTime { + private Date now; + private static final SimpleDateFormat sdft = new SimpleDateFormat("yyyyMMddHHmmss"); + + public SystemDateTimeImpl(Date now) { + this.now = now; + } + + @Override + public String getHostdate() { + return sdft.format(this.now).substring(0, 8); + } + + @Override + public String getHosttime() { + return sdft.format(this.now).substring(8, 14); + } + + @Override + public String getHostdatetime() { + return sdft.format(this.now); + } + + @Override + public Date getSysdate() { + return this.now; + } + } + /** * 获取oracle数据库时间 */ private SystemDateTime getOracleDatetime() { - return taskLockDao.getOracleDatetime(); +// return taskLockDao.getOracleDatetime(); + String sql = "SELECT CURRENT_TIMESTAMP AS DATE_VALUE"; + Query query = em.createNativeQuery(sql, DateItem.class); + DateItem dateItem = (DateItem) query.getSingleResult(); + SystemDateTime now = new SystemDateTimeImpl(dateItem.getDate()); + return now; } @Override @@ -38,7 +84,7 @@ public class SystemUtilServiceImpl implements SystemUtilService { } @Override - public TTaskLock updateTaskLock(TTaskLock lock){ + public TTaskLock updateTaskLock(TTaskLock lock) { return taskLockDao.save(lock); } @@ -80,12 +126,12 @@ public class SystemUtilServiceImpl implements SystemUtilService { return hostdate; } - private String getOracleRefno(){ - return taskLockDao.getOracleRefno(); + private String getOracleRefno() { + return taskLockDao.getOracleRefno(); } @Override - public String getRefno(){ + public String getRefno() { return getOracleRefno(); } diff --git a/src/main/java/com/supwisdom/dlpay/framework/util/HmacUtil.java b/src/main/java/com/supwisdom/dlpay/framework/util/HmacUtil.java index 0978e28b..8e4e55d1 100644 --- a/src/main/java/com/supwisdom/dlpay/framework/util/HmacUtil.java +++ b/src/main/java/com/supwisdom/dlpay/framework/util/HmacUtil.java @@ -3,126 +3,141 @@ package com.supwisdom.dlpay.framework.util; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.*; public class HmacUtil { - /** - * 除去数组中的空值和签名参数 - * - * @param sArray 签名参数组 - * @return 去掉空值与签名参数后的新签名参数组 - */ - public static Map paraFilter(Map sArray) { - - Map result = new HashMap(); - - if (sArray == null || sArray.size() <= 0) { - return result; - } - - for (String key : sArray.keySet()) { - String value = sArray.get(key); - if (value == null || value.equals("") || "null".equals(value) || key.equalsIgnoreCase("sign")) { - continue; - } - result.put(key, value); - } - - return result; + /** + * 除去数组中的空值和签名参数 + * + * @param sArray 签名参数组 + * @return 去掉空值与签名参数后的新签名参数组 + */ + public static Map paraFilter(Map sArray) { + + Map result = new HashMap(); + + if (sArray == null || sArray.size() <= 0) { + return result; } - /** - * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串 - * - * @param params 需要排序并参与字符拼接的参数组 - * @return 拼接后字符串 - */ - public static String createLinkString(Map params) { + for (String key : sArray.keySet()) { + String value = sArray.get(key); + if (value == null || value.equals("") || "null".equals(value) || key.equalsIgnoreCase("sign")) { + continue; + } + result.put(key, value); + } - List keys = new ArrayList(params.keySet()); - Collections.sort(keys); + return result; + } - String prestr = ""; + /** + * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串 + * + * @param params 需要排序并参与字符拼接的参数组 + * @return 拼接后字符串 + */ + public static String createLinkString(Map params) { - for (int i = 0; i < keys.size(); i++) { - String key = keys.get(i); - String value = params.get(key); + List keys = new ArrayList(params.keySet()); + Collections.sort(keys); - if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符 - prestr = prestr + key + "=" + value; - } else { - prestr = prestr + key + "=" + value + "&"; - } - } + String prestr = ""; - return prestr; - } + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + String value = params.get(key); - public static String HMACSHA1(String data, String key) { - byte[] byteHMAC = null; - try { - Mac mac = Mac.getInstance("HmacSHA1"); - SecretKeySpec spec = new SecretKeySpec(key.getBytes(), "HmacSHA1"); - mac.init(spec); - byteHMAC = mac.doFinal(data.getBytes("UTF-8")); - } catch (InvalidKeyException e) { - e.printStackTrace(); - } catch (NoSuchAlgorithmException ignore) { - ignore.printStackTrace(); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - - if (byteHMAC != null) { - try { - String hexMac = getHexString(byteHMAC); - return hexMac; - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return null; - } - return null; + if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符 + prestr = prestr + key + "=" + value; + } else { + prestr = prestr + key + "=" + value + "&"; + } + } + return prestr; + } + + private static String HMACSHAX(String data, String key, String alg) { + String algorithm = ""; + if ("SHA1".equals(alg)) { + algorithm = "HmacSHA1"; + } else if ("SHA256".equals(alg)) { + algorithm = "HmacSHA256"; + } else if ("SHA512".equals(alg)) { + algorithm = "HmacSHA512"; + } + byte[] byteHMAC = null; + try { + Mac mac = Mac.getInstance(algorithm); + SecretKeySpec spec = new SecretKeySpec(key.getBytes(), algorithm); + mac.init(spec); + byteHMAC = mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); + } catch (InvalidKeyException | NoSuchAlgorithmException e) { + e.printStackTrace(); } - public static String getHexString(byte[] b) throws Exception { - String result = ""; - for (int i = 0; i < b.length; i++) { - result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1); - } - return result; + if (byteHMAC != null) { + try { + String hexMac = getHexString(byteHMAC); + return hexMac; + } catch (Exception e) { + e.printStackTrace(); + } + return null; } + return null; + } - public static boolean checkHmacSign(String data, String key, String sign) { - String asign = HMACSHA1(data, key); - if (asign == null) { - return false; - } - if (asign.equalsIgnoreCase(sign)) { - return true; - } + public static String HMACSHA256(String data, String key) { + return HMACSHAX(data, key, "SHA256"); + } - return false; + public static String HMACSHA512(String data, String key) { + return HMACSHAX(data, key, "SHA512"); + } + + public static String HMACSHA1(String data, String key) { + return HMACSHAX(data, key, "SHA1"); + } + + public static String getHexString(byte[] b) throws Exception { + String result = ""; + for (int i = 0; i < b.length; i++) { + result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1); } + return result; + } - public static void main(String[] args) { - String refno = DateUtil.getNow(); - Map mapValue = new HashMap(); - mapValue.put("tradeaccount", "10000097"); - mapValue.put("tradename", "在线点餐"); - mapValue.put("tradeamount", "1"); - mapValue.put("paidamount", "1"); - mapValue.put("outtradeno", refno); - mapValue.put("yktshopid", "2"); - mapValue.put("shopid", "68512084"); - String signstr = createLinkString(HmacUtil.paraFilter(mapValue)); - String sign = HmacUtil.HMACSHA1(signstr, "adc4ac6822fd462780f878b86cb94688"); - System.out.println("{\"tradeaccount\":\"10000097\",\"tradename\":\"在线点餐\",\"tradeamount\":\"1\"," + - "\"paidamount\": \"1\",\"outtradeno\":\""+refno+"\",\"yktshopid\": \"2\",\"shopid\":\"68512084\",\"sign\": \""+sign+"\"}"); + public static boolean checkHmacSign(String data, String key, String sign) { + String asign = HMACSHA1(data, key); + if (asign == null) { + return false; } + if (asign.equalsIgnoreCase(sign)) { + return true; + } + + return false; + } + + public static void main(String[] args) { + String refno = DateUtil.getNow(); + Map mapValue = new HashMap(); + mapValue.put("tradeaccount", "10000097"); + mapValue.put("tradename", "在线点餐"); + mapValue.put("tradeamount", "1"); + mapValue.put("paidamount", "1"); + mapValue.put("outtradeno", refno); + mapValue.put("yktshopid", "2"); + mapValue.put("shopid", "68512084"); + String signstr = createLinkString(HmacUtil.paraFilter(mapValue)); + String sign = HmacUtil.HMACSHA1(signstr, "adc4ac6822fd462780f878b86cb94688"); + System.out.println("{\"tradeaccount\":\"10000097\",\"tradename\":\"在线点餐\",\"tradeamount\":\"1\"," + + "\"paidamount\": \"1\",\"outtradeno\":\"" + refno + "\",\"yktshopid\": \"2\",\"shopid\":\"68512084\",\"sign\": \"" + sign + "\"}"); + } } diff --git a/src/main/kotlin/com/supwisdom/dlpay/DlpayApplication.kt b/src/main/kotlin/com/supwisdom/dlpay/DlpayApplication.kt index baa9e6c0..68a0e613 100644 --- a/src/main/kotlin/com/supwisdom/dlpay/DlpayApplication.kt +++ b/src/main/kotlin/com/supwisdom/dlpay/DlpayApplication.kt @@ -1,35 +1,63 @@ package com.supwisdom.dlpay -import org.springframework.beans.factory.annotation.Autowired +import io.lettuce.core.ReadFrom +import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration -import org.springframework.context.annotation.PropertySource import org.springframework.core.annotation.Order -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder +import org.springframework.data.redis.connection.RedisConnectionFactory +import org.springframework.data.redis.connection.RedisPassword +import org.springframework.data.redis.connection.RedisStandaloneConfiguration +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory +import org.springframework.data.redis.core.RedisKeyValueTemplate +import org.springframework.data.redis.core.RedisTemplate +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter import org.springframework.security.config.http.SessionCreationPolicy -import org.springframework.security.core.Authentication import org.springframework.security.core.userdetails.User import org.springframework.security.core.userdetails.UserDetailsService -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository import org.springframework.security.provisioning.InMemoryUserDetailsManager import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler -import org.springframework.security.web.authentication.logout.LogoutSuccessHandler -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +@Configuration +@EnableRedisRepositories +class AppConfig { + + @Value("\${redis.server}") + lateinit var server: String + + @Value("\${redis.port}") + var port: Int = 0 + + @Value("\${redis.database}") + var database: Int = 0 + + @Value("\${redis.password}") + lateinit var password: String + + @Bean + fun redisConnectionFactory(): RedisConnectionFactory { + val clientConfig = LettuceClientConfiguration.builder() + .readFrom(ReadFrom.SLAVE_PREFERRED) + .build() + val serverConfig = RedisStandaloneConfiguration(server, port) + if (!password.isNullOrEmpty()) { + serverConfig.password = RedisPassword.of(password) + } + serverConfig.database = database + + return LettuceConnectionFactory(serverConfig, clientConfig) + } +} + @EnableWebSecurity class WebSecurityConfig { - @Autowired - private lateinit var clientRegistrationRepository: ClientRegistrationRepository @Bean fun userDetailsService(): UserDetailsService { @@ -45,8 +73,8 @@ class WebSecurityConfig { @Configuration @Order(1) class ApiWebSecurityConfigurationAdapter : WebSecurityConfigurerAdapter() { - @Autowired - private lateinit var clientRegistrationRepository: ClientRegistrationRepository +// @Autowired +// private lateinit var clientRegistrationRepository: ClientRegistrationRepository override fun configure(http: HttpSecurity) { http.authorizeRequests() diff --git a/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt b/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt new file mode 100644 index 00000000..465e74f9 --- /dev/null +++ b/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt @@ -0,0 +1,91 @@ +package com.supwisdom.dlpay.framework.controller + +import com.supwisdom.dlpay.framework.ResponseBodyBuilder +import com.supwisdom.dlpay.framework.core.JwtTokenUtil +import com.supwisdom.dlpay.framework.dao.ApiClientDao +import com.supwisdom.dlpay.framework.domain.AppClientRedis +import com.supwisdom.dlpay.framework.redisrepo.AppClientRepository +import com.supwisdom.dlpay.framework.service.SystemUtilService +import com.supwisdom.dlpay.framework.util.HmacUtil +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import java.util.* + +@RestController +@RequestMapping("/auth") +class ApiAuthController { + + @Autowired + lateinit var repo: AppClientRepository + + @Autowired + lateinit var apiClient: ApiClientDao + + @Autowired + lateinit var systemUtil: SystemUtilService + + @Autowired + lateinit var jwtUtil: JwtTokenUtil + + @GetMapping("/gettoken") + fun loginInit(appid: String): ResponseEntity { + apiClient.findById(appid).run { + if (!isPresent) { + return ResponseEntity.status(401).build() + } + if (get().status != "normal") { + return ResponseEntity.ok(ResponseBodyBuilder.create() + .fail(1, "API状态错误")) + } + get() + }.let { + val token = generateRandomToken() + val now = systemUtil.sysdatetime.hostdatetime + AppClientRedis().apply { + id = appid + loginTimestamp = now + this.token = HmacUtil.HMACSHA256(token, it.secret) + }.also { + repo.save(it) + } + return ResponseEntity.ok(ResponseBodyBuilder.create() + .data("token", token) + .data("timestamp", now) + .success()) + } + } + + private fun generateRandomToken(): String { + val random = ByteArray(12) { 0x00 } + Random(System.currentTimeMillis()).nextBytes(random) + return Base64.getEncoder().encode(random).toString(Charsets.UTF_8) + } + + private fun checkSecretToken(app: AppClientRedis, secret: String): Boolean { + return (app.token == secret) + } + + @GetMapping("/authentication") + fun login(appid: String, secret: String): ResponseEntity { + return repo.findById(appid).let { + if (it.isPresent && checkSecretToken(it.get(), secret)) { + it.get().also { client -> + client.token = "" + repo.save(client) + } + val token = jwtUtil.generateToken( + mapOf("uid" to appid, "issuer" to "supwisdom", + "subject" to "payapi")) + ResponseEntity.ok(ResponseBodyBuilder.create() + .data("jwt", token) + .data("appid", appid) + .success()) + } else { + ResponseEntity.status(401).build() + } + } + } +} \ No newline at end of file -- 2.17.1