实现了 jwt authentication
diff --git a/build.gradle b/build.gradle
index c273d3c..6823062 100644
--- a/build.gradle
+++ b/build.gradle
@@ -26,6 +26,7 @@
 
 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 @@
     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 fbfa731..3a3d0a6 100644
--- a/config/application-devel-oracle.properties
+++ b/config/application-devel-oracle.properties
@@ -7,4 +7,10 @@
 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 0000000..39d4caf
--- /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 64f9736..92b7ecf 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 0000000..d4396a6
--- /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 0000000..e43e411
--- /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<String, Object> 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<String> 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<String, Object> 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<String, Object> 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 0000000..4390368
--- /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<TApiClient, String> {
+}
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 0000000..a45f9f3
--- /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 0000000..f4aa56e
--- /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 0000000..2c25864
--- /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 0000000..5f19465
--- /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 0000000..495a122
--- /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<AppClientRedis, String> {
+}
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 0000000..a4925cd
--- /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<PersonRedis, String> {
+}
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 b4b8b48..0cf7bcf 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.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.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 @@
 
   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 @@
   }
 
   @Override
-  public TTaskLock updateTaskLock(TTaskLock lock){
+  public TTaskLock updateTaskLock(TTaskLock lock) {
     return taskLockDao.save(lock);
   }
 
@@ -80,12 +126,12 @@
     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 0978e28..8e4e55d 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 @@
 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<String, String> paraFilter(Map<String, String> sArray) {
+  /**
+   * 除去数组中的空值和签名参数
+   *
+   * @param sArray 签名参数组
+   * @return 去掉空值与签名参数后的新签名参数组
+   */
+  public static Map<String, String> paraFilter(Map<String, String> sArray) {
 
-        Map<String, String> result = new HashMap<String, String>();
+    Map<String, String> result = new HashMap<String, String>();
 
-        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;
+    if (sArray == null || sArray.size() <= 0) {
+      return result;
     }
 
-    /**
-     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
-     *
-     * @param params 需要排序并参与字符拼接的参数组
-     * @return 拼接后字符串
-     */
-    public static String createLinkString(Map<String, String> params) {
-
-        List<String> keys = new ArrayList<String>(params.keySet());
-        Collections.sort(keys);
-
-        String prestr = "";
-
-        for (int i = 0; i < keys.size(); i++) {
-            String key = keys.get(i);
-            String value = params.get(key);
-
-            if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
-                prestr = prestr + key + "=" + value;
-            } else {
-                prestr = prestr + key + "=" + value + "&";
-            }
-        }
-
-        return prestr;
+    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);
     }
 
-    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();
-        }
+    return result;
+  }
 
-        if (byteHMAC != null) {
-            try {
-                String hexMac = getHexString(byteHMAC);
-                return hexMac;
-            } catch (Exception e) {
-                // TODO Auto-generated catch block
-                e.printStackTrace();
-            }
-            return null;
-        }
-        return null;
+  /**
+   * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
+   *
+   * @param params 需要排序并参与字符拼接的参数组
+   * @return 拼接后字符串
+   */
+  public static String createLinkString(Map<String, String> params) {
 
+    List<String> keys = new ArrayList<String>(params.keySet());
+    Collections.sort(keys);
+
+    String prestr = "";
+
+    for (int i = 0; i < keys.size(); i++) {
+      String key = keys.get(i);
+      String value = params.get(key);
+
+      if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
+        prestr = prestr + key + "=" + value;
+      } else {
+        prestr = prestr + key + "=" + value + "&";
+      }
     }
 
-    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;
+    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 boolean checkHmacSign(String data, String key, String sign) {
-        String asign = HMACSHA1(data, key);
-        if (asign == null) {
-            return false;
-        }
-        if (asign.equalsIgnoreCase(sign)) {
-            return true;
-        }
+    if (byteHMAC != null) {
+      try {
+        String hexMac = getHexString(byteHMAC);
+        return hexMac;
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+      return null;
+    }
+    return null;
+  }
 
-        return false;
+  public static String HMACSHA256(String data, String key) {
+    return HMACSHAX(data, key, "SHA256");
+  }
+
+  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 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 void main(String[] args) {
-        String refno = DateUtil.getNow();
-        Map<String, String> mapValue = new HashMap<String, String>();
-        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+"\"}");
-    }
+    return false;
+  }
+
+  public static void main(String[] args) {
+    String refno = DateUtil.getNow();
+    Map<String, String> mapValue = new HashMap<String, String>();
+    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 baa9e6c..68a0e61 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 @@
         @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 0000000..465e74f
--- /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<Any> {
+        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<Any> {
+        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