引入 multi-tenant library 并完成初步测试
diff --git a/payapi/Dockerfile b/payapi/Dockerfile
index 28f3e81..6975293 100644
--- a/payapi/Dockerfile
+++ b/payapi/Dockerfile
@@ -1,9 +1,13 @@
 FROM openjdk:8
 
-COPY payapi-1.jar /opt/payapi/payapi.jar
+ARG BUILD_VERSION
+
+ENV EXEC_JAR=payapi-$BUILD_VERSION.jar
+
+COPY payapi-${BUILD_VERSION}.jar /opt/payapi/
 
 EXPOSE 8080
 
 WORKDIR /opt/payapi
 
-CMD ["java" , "-jar", "payapi.jar"]
+CMD java -jar ${EXEC_JAR}
diff --git a/payapi/build.gradle b/payapi/build.gradle
index 9dcd2e4..d8f169c 100644
--- a/payapi/build.gradle
+++ b/payapi/build.gradle
@@ -14,14 +14,15 @@
 
 println("Build version: $buildVersion")
 
+
 bootJar {
     enabled = true
     mainClassName = payapiStartClass
-    def standalone = ""
-    if (rootProject.hasProperty("no-multi-tenant")) {
-        standalone = "-stangalone-"
-    }
-    archiveFileName = "${project.name}${standalone}-${buildVersion}.${archiveExtension.getOrElse('.jar')}"
+//    def standalone = ""
+//    if (rootProject.hasProperty("no-multi-tenant")) {
+//        standalone = "-stangalone-"
+//
+    archiveFileName = "${project.name}-${buildVersion}.${archiveExtension.getOrElse('.jar')}"
     manifest {
         attributes("Payapi-Version": buildVersion,
                 "Payapi-Buildtime": buildTime)
@@ -51,12 +52,11 @@
     }
     println("Docker image tag : ${imageVersion}")
     name "${dockerRegistry}/payapi:${imageVersion}"
-    println(jar.archiveFile.get())
-    files jar.archiveFile.get()
+    println(bootJar.archiveFile.get())
+    files bootJar.archiveFile.get()
+    buildArgs([BUILD_VERSION: "${buildVersion}"])
 }
 
-docker.dependsOn(jar)
-
 configurations {
     developmentOnly
     runtimeClasspath {
@@ -107,6 +107,7 @@
 
     implementation "com.supwisdom:multi-tenant-core:${multiTenantLibVersion}"
     implementation "com.supwisdom:multi-tenant-datasource:${multiTenantLibVersion}"
+    implementation "com.supwisdom:multi-tenant-jwt:${multiTenantLibVersion}"
 
     implementation "org.bitbucket.b_c:jose4j:${jose4jVersion}"
     implementation files("libs/masmgc.sdk.sms-0.0.1-SNAPSHOT.jar")
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/core/JwtConfig.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/core/JwtConfig.java
deleted file mode 100644
index 85167fd..0000000
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/core/JwtConfig.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.supwisdom.dlpay.framework.core;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class JwtConfig {
-  @Value("${jwt.secret}")
-  private String secret;
-  @Value("${jwt.expiration:3600}")
-  private Long expiration = 3600L;
-  @Value("${jwt.header:Authorization}")
-  private String header = "Authorization";
-  @Value("${jwt.token_header:Bearer }")
-  private String tokenHeader = "Bearer ";
-
-  @Value("${jwt.multitenant:false}")
-  private Boolean multiTenant = false;
-
-  public String getSecret() {
-    return secret;
-  }
-
-  public Long getExpiration() {
-    return expiration;
-  }
-
-  public String getHeader() {
-    return header;
-  }
-
-  public String getTokenHeader() {
-    return tokenHeader;
-  }
-
-  public void setExpiration(Long expiration) {
-    this.expiration = expiration;
-  }
-
-  public Boolean getMultiTenant() {
-    return multiTenant;
-  }
-
-  public void setMultiTenant(Boolean multiTenant) {
-    this.multiTenant = multiTenant;
-  }
-}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/core/JwtToken.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/core/JwtToken.java
deleted file mode 100644
index 072ea5d..0000000
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/core/JwtToken.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.supwisdom.dlpay.framework.core;
-
-import org.jose4j.jwt.NumericDate;
-
-public class JwtToken {
-  private String jti;
-  private NumericDate expiration;
-  private String jwtToken;
-
-  public JwtToken(String jti, String jwtToken, NumericDate exp) {
-    this.jti = jti;
-    this.jwtToken = jwtToken;
-    this.expiration = exp;
-  }
-
-  public String getJti() {
-    return jti;
-  }
-
-  public void setJti(String jti) {
-    this.jti = jti;
-  }
-
-  public String getJwtToken() {
-    return jwtToken;
-  }
-
-  public void setJwtToken(String jwtToken) {
-    this.jwtToken = jwtToken;
-  }
-
-  public NumericDate getExpiration() {
-    return expiration;
-  }
-
-  public void setExpiration(NumericDate expiration) {
-    this.expiration = expiration;
-  }
-}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/core/JwtTokenUtil.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/core/JwtTokenUtil.java
deleted file mode 100644
index 71f65bf..0000000
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/core/JwtTokenUtil.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.supwisdom.dlpay.framework.core;
-
-import com.supwisdom.dlpay.framework.util.Constants;
-import org.jose4j.jwa.AlgorithmConstraints;
-import org.jose4j.jwk.JsonWebKey;
-import org.jose4j.jws.AlgorithmIdentifiers;
-import org.jose4j.jws.JsonWebSignature;
-import org.jose4j.jwt.JwtClaims;
-import org.jose4j.jwt.MalformedClaimException;
-import org.jose4j.jwt.consumer.InvalidJwtException;
-import org.jose4j.jwt.consumer.JwtConsumer;
-import org.jose4j.jwt.consumer.JwtConsumerBuilder;
-import org.jose4j.lang.JoseException;
-import org.springframework.security.core.userdetails.UserDetails;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class JwtTokenUtil {
-  private JwtConfig jwtConfig;
-
-  public JwtTokenUtil(JwtConfig config) {
-    this.jwtConfig = config;
-  }
-
-  public String getHeader() {
-    return jwtConfig.getHeader();
-  }
-
-  public JwtToken generateToken(Map<String, Object> params) throws JoseException, MalformedClaimException {
-    JwtClaims claims = new JwtClaims();
-    claims.setIssuer(params.get("issuer").toString());  // who creates the token and signs it
-    if (params.get("audience") != null) {
-      claims.setAudience(params.get("audience").toString());
-    }
-    claims.setExpirationTimeMinutesInTheFuture(jwtConfig.getExpiration() / 60); // time when the token will expire (10 minutes from now)
-    claims.setGeneratedJwtId();
-    claims.setIssuedAtToNow();  // when the token was issued/created (now)
-    claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago)
-    if (params.get("subject") != null) {
-      claims.setSubject(params.get("subject").toString()); // the subject/principal is whom the token is about
-    }
-    if (params.get(Constants.JWT_CLAIM_AUTHORITIES) != null) {
-      claims.setClaim(Constants.JWT_CLAIM_AUTHORITIES, params.get(Constants.JWT_CLAIM_AUTHORITIES));
-    }
-    if (params.get(Constants.JWT_CLAIM_UID) != null) {
-      claims.setClaim(Constants.JWT_CLAIM_UID, params.get(Constants.JWT_CLAIM_UID));
-    }
-    if (params.get(Constants.JWT_CLAIM_TENANTID) != null) {
-      claims.setClaim(Constants.JWT_CLAIM_TENANTID, params.get(Constants.JWT_CLAIM_TENANTID));
-    }
-    /*
-    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", jwtConfig.getSecret());
-    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 new JwtToken(claims.getJwtId(), jws.getCompactSerialization(), claims.getExpirationTime());
-  }
-
-  public JwtToken generateToken(UserDetails userDetails) throws JoseException, MalformedClaimException {
-    Map<String, Object> claims = new HashMap<>();
-    claims.put("uid", userDetails.getUsername());
-    return generateToken(claims);
-  }
-
-  public Map<String, Object> verifyToken(String token) throws JoseException, InvalidJwtException {
-    Map<String, Object> keySpec = new HashMap<>();
-    keySpec.put("kty", "oct");
-    keySpec.put("k", jwtConfig.getSecret());
-    JsonWebKey key = JsonWebKey.Factory.newJwk(keySpec);
-    JwtConsumer jwtConsumer = new JwtConsumerBuilder()
-        .setRequireExpirationTime() // the JWT must have an expiration time
-        .setAllowedClockSkewInSeconds(30) // allow some leeway in validating time based claims to account for clock skew
-        .setVerificationKey(key.getKey()) // verify the signature with the public key
-        .setSkipDefaultAudienceValidation()
-        .setJwsAlgorithmConstraints( // only allow the expected signature algorithm(s) in the given context
-            new AlgorithmConstraints(org.jose4j.jwa.AlgorithmConstraints.ConstraintType.WHITELIST, // which is only RS256 here
-                AlgorithmIdentifiers.HMAC_SHA256))
-        .build(); // create the JwtConsumer instance
-
-    //  Validate the JWT and process it to the Claims
-    JwtClaims jwtClaims = jwtConsumer.processToClaims(token);
-    return jwtClaims.getClaimsMap();
-  }
-}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/JwtRedis.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/JwtRedis.java
deleted file mode 100644
index d32ff8e..0000000
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/JwtRedis.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.supwisdom.dlpay.framework.domain;
-
-import org.springframework.data.annotation.Id;
-import org.springframework.data.redis.core.RedisHash;
-import org.springframework.data.redis.core.TimeToLive;
-
-
-@RedisHash(value = "api_jwt")
-public class JwtRedis {
-  @Id
-  String jti;
-
-  String status;
-
-  String uid;
-
-  @TimeToLive
-  Long expiration;
-
-  public String getJti() {
-    return jti;
-  }
-
-  public void setJti(String jti) {
-    this.jti = jti;
-  }
-
-  public String getStatus() {
-    return status;
-  }
-
-  public void setStatus(String status) {
-    this.status = status;
-  }
-
-  public Long getExpiration() {
-    return expiration;
-  }
-
-  public void setExpiration(Long expiration) {
-    this.expiration = expiration;
-  }
-
-  public String getUid() {
-    return uid;
-  }
-
-  public void setUid(String uid) {
-    this.uid = uid;
-  }
-}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/TTenantUserDB.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/TTenantUserDB.java
index 7df05a7..6f3258f 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/TTenantUserDB.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/framework/domain/TTenantUserDB.java
@@ -2,13 +2,14 @@
 
 import javax.persistence.*;
 import javax.validation.constraints.NotNull;
+import java.io.Serializable;
 
 @Entity
 @Table(name = "t_tenant_user_db", schema = "public",
     indexes = {@Index(name = "tenant_db_dbid_idx", columnList = "dbid"),
         @Index(name = "tenant_db_schema_idx", columnList = "schema"),
         @Index(name = "tenant_db_idx2", columnList = "dbid, schema", unique = true)})
-public class TTenantUserDB {
+public class TTenantUserDB implements Serializable {
   @Id
   @Column(name = "id", length = 32)
   private String id;
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/redisrepo/ApiJwtRepository.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/redisrepo/ApiJwtRepository.java
deleted file mode 100644
index 3371b31..0000000
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/redisrepo/ApiJwtRepository.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.supwisdom.dlpay.framework.redisrepo;
-
-import com.supwisdom.dlpay.framework.domain.JwtRedis;
-import org.springframework.data.repository.CrudRepository;
-import org.springframework.stereotype.Repository;
-
-public interface ApiJwtRepository extends CrudRepository<JwtRedis, String> {
-}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/tenant/MultiTenantUserAdapter.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/tenant/MultiTenantUserAdapter.java
index fddcfd7..1a0a92b 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/tenant/MultiTenantUserAdapter.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/framework/tenant/MultiTenantUserAdapter.java
@@ -5,6 +5,7 @@
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.context.annotation.Primary;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.stereotype.Component;
 
@@ -17,10 +18,13 @@
 public class MultiTenantUserAdapter implements AbstractTenantUserOperator {
   private static final String DOMAIN_SEP = "@";
 
-  private TenantSessionHelper tenantSessionHelper;
+  private final TenantSessionHelper tenantSessionHelper;
 
-  public MultiTenantUserAdapter(TenantSessionHelper tenantSessionHelper) {
+  private final RedisTemplate<String, String> redisTemplate;
+
+  public MultiTenantUserAdapter(TenantSessionHelper tenantSessionHelper, RedisTemplate<String, String> redisTemplate) {
     this.tenantSessionHelper = tenantSessionHelper;
+    this.redisTemplate = redisTemplate;
   }
 
   @Override
@@ -30,7 +34,15 @@
     if (StringUtils.isEmpty(domain)) {
       tenantSessionHelper.setSessionTenantById(Constants.DEFAULT_TENANTID);
     } else {
-      tenantSessionHelper.setSessionTenantById(domain);
+      String schema = redisTemplate.opsForValue().get(domain);
+      if (StringUtils.isEmpty(schema)) {
+        schema = "public";
+      }
+      String tenantId = redisTemplate.opsForValue().get(schema);
+      if (StringUtils.isEmpty(schema)) {
+        tenantId = "default";
+      }
+      tenantSessionHelper.setSessionTenantById(tenantId);
     }
     if (StringUtils.isEmpty(realname)) {
       throw new UsernameNotFoundException("管理员不存在");
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
index 4b88ed0..4cd2abe 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
@@ -3,15 +3,21 @@
 import com.supwisdom.dlpay.framework.service.TenantService
 import com.supwisdom.dlpay.framework.tenant.TenantCacheKeyGen
 import com.supwisdom.dlpay.framework.util.Constants
+import com.supwisdom.multitenant.TenantContextHolder
 import com.supwisdom.multitenant.TenantDetails
 import com.supwisdom.multitenant.TenantDetailsProvider
-import com.supwisdom.multitenant.TenantSessionData
 import com.supwisdom.multitenant.annotations.EnableHttpHeaderTenantInterceptor
 import com.supwisdom.multitenant.annotations.EnableSessionTenantInterceptor
+import com.supwisdom.multitenant.exceptions.TenantNotDefException
+import com.supwisdom.multitenant.jwt.JwtTenantConfigAdapter
+import com.supwisdom.multitenant.jwt.annotations.EnableJwtTenantInterceptor
+import com.supwisdom.multitenant.jwt.config.JwtTenantConfig
+import com.supwisdom.multitenant.jwt.config.JwtToken
 import io.lettuce.core.ReadFrom
 import mu.KotlinLogging
 import net.javacrumbs.shedlock.core.LockProvider
 import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider
+import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.beans.factory.annotation.Value
 import org.springframework.boot.SpringApplication
@@ -37,6 +43,7 @@
 import org.springframework.data.redis.core.RedisTemplate
 import org.springframework.data.redis.repository.configuration.EnableRedisRepositories
 import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer
 import org.springframework.data.redis.serializer.StringRedisSerializer
 import org.springframework.http.client.SimpleClientHttpRequestFactory
 import org.springframework.scheduling.annotation.EnableScheduling
@@ -72,6 +79,15 @@
     }
 
     @Bean
+    fun redisTempalte(factor: RedisConnectionFactory): RedisTemplate<String, JwtToken> {
+        val template = RedisTemplate<String, JwtToken>();
+        template.setConnectionFactory(factor)
+        template.keySerializer = StringRedisSerializer()
+        template.valueSerializer = Jackson2JsonRedisSerializer(JwtToken::class.java)
+        return template
+    }
+
+    @Bean
     fun lockProvider(connectionFactory: RedisConnectionFactory): LockProvider {
         return RedisLockProvider(connectionFactory, "prod")
     }
@@ -156,6 +172,8 @@
     fun restTemplate(factory: SimpleClientHttpRequestFactory): RestTemplate {
         return RestTemplate(factory)
     }
+
+
 }
 
 
@@ -172,34 +190,58 @@
 class MyTenantDetailsProvider : TenantDetailsProvider {
     @Autowired
     private lateinit var tenantService: TenantService
+    @Autowired
+    private lateinit var redisTemplate: RedisTemplate<String, String>
+
+    private val logger = KotlinLogging.logger { }
 
     private val defaultTenant = TenantDetails().apply {
         id = Constants.DEFAULT_TENANTID
         dbSchema = "public"
         dataCenter = "default"
+        enabled = true
     }
 
     override fun defaultTenant(): TenantDetails {
         return defaultTenant
     }
 
-    override fun createDetailsById(id: String?): TenantDetails {
-        return tenantService.findByTenantId(id)?.let { catalog ->
-            TenantDetails().apply {
-                this.id = catalog.id
-                dbSchema = catalog.schema
-                dataCenter = "default"
-            }
-        } ?: return defaultTenant
+    override fun createDetailsById(id: String): TenantDetails {
+        logger.debug { "find tenant id <$id> ..." }
+        val schema = redisTemplate.opsForValue().get(id) ?: return defaultTenant
+        return TenantDetails().apply {
+            this.id = id
+            dbSchema = schema
+            dataCenter = "default"
+            enabled = true
+        }
+    }
+}
+
+@Component
+class MyTenantJwtConfigAdapter : JwtTenantConfigAdapter {
+    @Value("\${jwt.secret}")
+    private lateinit var jwtSecret: String;
+
+    override fun getConfig(): JwtTenantConfig {
+        if (TenantContextHolder.getContext().tenant == null) {
+            throw TenantNotDefException("未定义Tenant id")
+        }
+        return JwtTenantConfig().apply {
+            this.tenantId = TenantContextHolder.getContext().tenant.id
+            this.secret = jwtSecret
+        }
     }
 }
 
 @SpringBootApplication
 @EnableDiscoveryClient
 @EnableScheduling
+@EnableSchedulerLock(defaultLockAtMostFor = "PT15m")
 @EnableCaching
 @EnableHttpHeaderTenantInterceptor
 @EnableSessionTenantInterceptor
+@EnableJwtTenantInterceptor
 @ServletComponentScan
 class PayApiApplication : SpringBootServletInitializer() {
 
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt
index deabb41..22c1490 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt
@@ -5,21 +5,20 @@
 import com.supwisdom.dlpay.api.bean.ApiLoginResponse
 import com.supwisdom.dlpay.exception.TransactionCheckException
 import com.supwisdom.dlpay.framework.ResponseBodyBuilder
-import com.supwisdom.dlpay.framework.core.JwtConfig
-import com.supwisdom.dlpay.framework.core.JwtTokenUtil
 import com.supwisdom.dlpay.framework.dao.ApiClientDao
 import com.supwisdom.dlpay.framework.dao.TenantConfigDao
 import com.supwisdom.dlpay.framework.domain.ApiClientRedis
-import com.supwisdom.dlpay.framework.domain.JwtRedis
 import com.supwisdom.dlpay.framework.domain.TOperator
 import com.supwisdom.dlpay.framework.redisrepo.ApiClientRepository
-import com.supwisdom.dlpay.framework.redisrepo.ApiJwtRepository
 import com.supwisdom.dlpay.framework.security.validate.ImageCodeUtil
 import com.supwisdom.dlpay.framework.security.validate.VerifyCode
 import com.supwisdom.dlpay.framework.service.CommonService
 import com.supwisdom.dlpay.framework.service.SystemUtilService
 import com.supwisdom.dlpay.framework.util.*
 import com.supwisdom.dlpay.system.service.FunctionService
+import com.supwisdom.multitenant.jwt.JwtRequestData
+import com.supwisdom.multitenant.jwt.JwtTenantService
+import com.supwisdom.multitenant.jwt.JwtTokenBuilder
 import mu.KotlinLogging
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.data.redis.connection.RedisConnectionFactory
@@ -37,7 +36,9 @@
 import org.springframework.web.bind.annotation.*
 import org.springframework.web.context.request.ServletWebRequest
 import java.io.IOException
+import java.time.Instant
 import java.util.*
+import javax.annotation.Resource
 import javax.imageio.ImageIO
 import javax.servlet.http.HttpServletRequest
 import javax.servlet.http.HttpServletResponse
@@ -50,19 +51,19 @@
     lateinit var apiClientRepository: ApiClientRepository
 
     @Autowired
-    lateinit var apiJwtRepository: ApiJwtRepository
-
-    @Autowired
     lateinit var apiClientDao: ApiClientDao
 
     @Autowired
     lateinit var systemUtil: SystemUtilService
 
     @Autowired
-    lateinit var jwtConfig: JwtConfig
+    private lateinit var tetantConfigDao: TenantConfigDao
 
     @Autowired
-    private lateinit var tetantConfigDao: TenantConfigDao
+    private lateinit var jwtTenantService: JwtTenantService
+
+    @Resource(name = "jwtRequestData")
+    private lateinit var jwtRequestData: JwtRequestData
 
     @GetMapping(value = ["/gettoken", "/gettoken/{clientid}"])
     fun loginInit(appid: String, @PathVariable clientid: String?,
@@ -114,24 +115,16 @@
         return apiClientRepository.findById(requestId).let {
             if (it.isPresent && checkSecretToken(it.get(), secret)) {
                 apiClientRepository.deleteById(requestId)
-                val token = JwtTokenUtil(jwtConfig).generateToken(
-                        mapOf(Constants.JWT_CLAIM_UID to appid,
-                                "issuer" to "payapi",
-                                "audience" to (clientid ?: appid),
-                                Constants.JWT_CLAIM_TENANTID to it.get().tenantId,
-                                Constants.JWT_CLAIM_AUTHORITIES to it.get().roles.split(";")))
-                JwtRedis().apply {
-                    jti = token.jti
-                    uid = appid
-                    status = TradeDict.JWT_STATUS_NORMAL
-                    expiration = token.expiration.valueInMillis
-                }.apply {
-                    apiJwtRepository.save(this)
-                }
-
+                val builder = JwtTokenBuilder.create()
+                        .uid(appid)
+                        .issuer("payapi")
+                        .audience(listOf(clientid ?: appid))
+                        .tenantId(it.get().tenantId)
+                        .authorities(it.get().roles.split(";"))
+                val token = jwtTenantService.generate(builder)
                 ResponseEntity.ok(ResponseBodyBuilder.create()
-                        .success(ApiLoginResponse(token.jwtToken,
-                                appid, DateUtil.getUTCTime(token.expiration.valueInMillis))))
+                        .success(ApiLoginResponse(token.get().jwt,
+                                appid, Instant.ofEpochSecond(token.get().expiration).toString())))
             } else {
                 ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
             }
@@ -140,38 +133,29 @@
 
     @GetMapping("/refresh")
     fun refresh(request: HttpServletRequest): ResponseEntity<Any> {
-        val auth = request.getHeader(jwtConfig.header) ?: ""
-        if (!auth.startsWith(jwtConfig.tokenHeader)) {
-            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
-        }
-        val jwt = JwtTokenUtil(jwtConfig).verifyToken(auth.substring(jwtConfig.tokenHeader.length))
-        val appid = jwt["uid"] as String
-        val result = apiClientDao.findByAppid(appid)?.let {
-            if (it.status == TradeDict.STATUS_NORMAL) {
-                // 新证书
-                val token = JwtTokenUtil(jwtConfig).generateToken(
-                        mapOf(Constants.JWT_CLAIM_UID to appid,
-                                "issuer" to "payapi",
-                                "audience" to jwt["audience"],
-                                Constants.JWT_CLAIM_AUTHORITIES to it.roles.split(";")))
-                JwtRedis().apply {
-                    jti = token.jti
-                    uid = appid
-                    status = TradeDict.JWT_STATUS_NORMAL
-                    expiration = token.expiration.valueInMillis
-                }.apply {
-                    apiJwtRepository.save(this)
-                }
+        val jwt = jwtRequestData.jwtToken
+                ?: return ResponseEntity.ok(ResponseBodyBuilder.create().fail(TradeErrorCode.INPUT_DATA_ERROR,
+                        "jwt unauthorized"))
 
-                ResponseEntity.ok(ResponseBodyBuilder.create()
-                        .success(ApiLoginResponse(token.jwtToken,
-                                appid, DateUtil.getUTCTime(token.expiration.valueInMillis))))
-            } else {
-                ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(mapOf("msg" to "appid error"))
-            }
-        } ?: ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(mapOf("msg" to "appid error"))
-        @Suppress("UNCHECKED_CAST")
-        return result as ResponseEntity<Any>
+        val appid = jwt.uid
+        // 新证书
+        val builder = JwtTokenBuilder.create()
+                .uid(appid)
+                .issuer(jwt.issuer)
+                .audience(jwt.audience)
+                .tenantId(jwt.tenantId)
+                .authorities(jwt.authorities)
+        val token = jwtTenantService.generate(builder)
+
+        return if (token.isPresent) {
+            ResponseEntity.ok(ResponseBodyBuilder.create()
+                    .success(ApiLoginResponse(token.get().jwt,
+                            appid, Instant.ofEpochSecond(token.get().expiration).toString())))
+        } else {
+
+            ResponseEntity.ok(ResponseBodyBuilder.create()
+                    .fail(TradeErrorCode.INPUT_DATA_ERROR, "JWT生成错误"))
+        }
     }
 }
 
@@ -208,25 +192,26 @@
 @RestController
 class UserInforController {
     @Autowired
-    lateinit var jwtConfig: JwtConfig
-    @Autowired
     private lateinit var redisConnectionFactory: RedisConnectionFactory
 
+    @Resource(name = "jwtRequestData")
+    private lateinit var jwtRequestData: JwtRequestData
+
     @RequestMapping("/userinfor")
     fun user(@RequestParam("access_token") access_token: String?,
              @RequestHeader(Constants.HEADER_AUTHORIZATION) auth: String?): ResponseEntity<Any> {
         if (access_token.isNullOrEmpty() && auth.isNullOrEmpty()) {
             return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
         }
-        var jwt: String
-        if(!auth.isNullOrEmpty()){
-            jwt = auth.substring(jwtConfig.tokenHeader.length)
-        }else{
-            jwt = access_token!!
+        val jwt = if (!auth.isNullOrEmpty()) {
+            jwtRequestData.jwtToken.jwt
+        } else {
+            access_token!!
         }
-        val obj: OAuth2Authentication? = RedisTokenStore(redisConnectionFactory).readAuthentication(jwt) ?: return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
+        val obj: OAuth2Authentication? = RedisTokenStore(redisConnectionFactory).readAuthentication(jwt)
+                ?: return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
         val user = obj!!.userAuthentication.principal as UserDetails
-        if(user.username.isNullOrEmpty()){
+        if (user.username.isNullOrEmpty()) {
             return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
         }
         return ResponseEntity.status(HttpStatus.OK).body("""{"name":"${user.username}"}""")
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/service/impl/framework_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/service/impl/framework_service_impl.kt
index b997a61..e5d2858 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/service/impl/framework_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/service/impl/framework_service_impl.kt
@@ -2,55 +2,42 @@
 
 import com.jcabi.manifests.Manifests
 import com.supwisdom.dlpay.exception.TransactionProcessException
-import com.supwisdom.dlpay.framework.core.JwtConfig
-import com.supwisdom.dlpay.framework.core.JwtTokenUtil
 import com.supwisdom.dlpay.framework.dao.ApiClientDao
 import com.supwisdom.dlpay.framework.service.CommonService
 import com.supwisdom.dlpay.framework.util.StringUtil
 import com.supwisdom.dlpay.framework.util.TradeErrorCode
+import com.supwisdom.multitenant.jwt.JwtRequestData
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.stereotype.Service
+import javax.annotation.Resource
 import javax.servlet.http.HttpServletRequest
 
 @Service
 class CommonServiceImpl : CommonService {
     @Autowired
-    lateinit var jwtConfig: JwtConfig
-    @Autowired
     lateinit var apiClientDao: ApiClientDao
 
-    private var jwtUtil: JwtTokenUtil? = null
-
-    private fun getUtil(): JwtTokenUtil {
-        if (jwtUtil == null) {
-            jwtUtil = JwtTokenUtil((jwtConfig))
-        }
-        return jwtUtil as JwtTokenUtil
-    }
+    @Resource(name = "jwtRequestData")
+    private lateinit var jwtRequestData: JwtRequestData
 
     override fun getSystemVersion(): String {
         return try {
-            var ver =  Manifests.read("Payapi-Version") ?: "version 1.0"
+            var ver = Manifests.read("Payapi-Version") ?: "version 1.0"
             ver += Manifests.read("Payapi-Buildtime") ?: "no"
             ver
         } catch (ex: Exception) {
-//            ex.printStackTrace()
             "unknown"
         }
     }
 
     override fun getRequestAppid(request: HttpServletRequest): String {
-        request.getHeader(jwtConfig.header).let {
-            if (null != it && it.startsWith(jwtConfig.tokenHeader)) {
-                val claims = getUtil().verifyToken(it.substring(jwtConfig.tokenHeader.length))
-                val uid = claims["uid"]?.toString()
-                if (!StringUtil.isEmpty(uid)) {
-                    return uid as String
-                }
+        jwtRequestData.jwtToken?.also {
+            val uid = it.uid
+            if (!StringUtil.isEmpty(uid)) {
+                return uid as String
             }
-
-            throw TransactionProcessException(TradeErrorCode.BUSINESS_APPID_NOTFOUND, "APPID未找到") //报错
         }
+        throw TransactionProcessException(TradeErrorCode.BUSINESS_APPID_NOTFOUND, "APPID未找到") //报错
     }
 
     override fun getAppidSecretByRequest(request: HttpServletRequest): String {
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/AuthLoginHandler.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/AuthLoginHandler.kt
index c795cbd..6317ab3 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/AuthLoginHandler.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/AuthLoginHandler.kt
@@ -3,16 +3,14 @@
 import com.fasterxml.jackson.databind.ObjectMapper
 import com.supwisdom.dlpay.api.bean.JsonResult
 import com.supwisdom.dlpay.api.service.UserService
-import com.supwisdom.dlpay.framework.core.JwtConfig
-import com.supwisdom.dlpay.framework.core.JwtTokenUtil
-import com.supwisdom.dlpay.framework.domain.JwtRedis
-import com.supwisdom.dlpay.framework.redisrepo.ApiJwtRepository
 import com.supwisdom.dlpay.framework.service.SystemUtilService
 import com.supwisdom.dlpay.framework.util.*
 import com.supwisdom.dlpay.mobile.dao.MobileUserDao
 import com.supwisdom.dlpay.mobile.domain.TBMobileUser
 import com.supwisdom.dlpay.mobile.exception.UserLoginFailException
 import com.supwisdom.dlpay.mobile.service.MobileApiService
+import com.supwisdom.multitenant.jwt.JwtTenantService
+import com.supwisdom.multitenant.jwt.JwtTokenBuilder
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.http.HttpStatus
 import org.springframework.security.authentication.BadCredentialsException
@@ -34,40 +32,30 @@
     @Autowired
     lateinit var objectMapper: ObjectMapper
     @Autowired
-    lateinit var jwtConfig: JwtConfig
-    @Autowired
-    lateinit var apiJwtRepository: ApiJwtRepository
-    @Autowired
     lateinit var systemUtilService: SystemUtilService
     @Autowired
     lateinit var userService: UserService
 
+    @Autowired
+    lateinit var jwtTenantService: JwtTenantService
+
     override fun onAuthenticationSuccess(request: HttpServletRequest, response: HttpServletResponse, authentication: Authentication) {
         val platform = request.getParameter("platform")
         logger.error(platform)
         val temp = authentication.principal as TBMobileUser
         val user = mobileApiService.findUserById(temp.uid)
-        val exp = systemUtilService.getSysparaValueAsInt(SysparaUtil.MOBILE_LOGIN_EXPIRE_IN_SECONDS,60*60*24*3)
-        jwtConfig.expiration = exp.toLong()
+        val exp = systemUtilService.getSysparaValueAsInt(SysparaUtil.MOBILE_LOGIN_EXPIRE_IN_SECONDS, 60 * 60 * 24 * 3)
         if (user != null) {
             //TODO 从数据取jwtConfig.expiration
-            val token = JwtTokenUtil(jwtConfig).generateToken(
-                    mapOf("uid" to user.uid, "issuer" to "payapi",
-                            "audience" to user.loginid,
-                            Constants.JWT_CLAIM_TENANTID to "mobile",
-                            Constants.JWT_CLAIM_AUTHORITIES to temp.authorities))
-            val jwt = JwtRedis().apply {
-                jti = token.jti
-                uid = user.loginid
-                status = TradeDict.JWT_STATUS_NORMAL
-                expiration = token.expiration.valueInMillis
-            }.apply {
-                //删除之前的token
-                if (!user.jti.isNullOrEmpty()) {
-                    apiJwtRepository.deleteById(user.jti!!)
-                }
-                apiJwtRepository.save(this)
-            }
+            val builder = JwtTokenBuilder.create()
+                    .expiration(exp.toLong())
+                    .issuer("payapi")
+                    .uid(user.uid)
+                    .tenantId("mobile")
+                    .authorities(temp.authorities?.toList() ?: listOf<Any>())
+            val token = jwtTenantService.generate(builder)
+
+            val jwt = token.get()
             if (user.loginpwderror > 0) {
                 user.loginpwderror = 0
                 user.loginpwderrortime = null
@@ -77,32 +65,32 @@
             user.jti = jwt.jti
             mobileApiService.saveUser(user)
             var payseted = false
-            if(!user.paypwd.isNullOrEmpty()){
+            if (!user.paypwd.isNullOrEmpty()) {
                 payseted = true
             }
             var name = ""
-            var signed=""
+            var signed = ""
             if (!user.userid.isNullOrEmpty()) {
                 val person = userService.findOnePersonByUserid(user.userid!!)
                 var card = mobileApiService.findCardByUserid(user.userid!!)
                 name = person.name
-                if(card!=null&&card.signed){
+                if (card != null && card.signed) {
                     signed = TradeDict.STATUS_YES
                 }
             }
             response.status = HttpStatus.OK.value()
             response.contentType = "application/json;charset=UTF-8"
             response.writer.write(objectMapper.writeValueAsString(JsonResult.ok()
-                    .put("token", token.jwtToken)
-                    ?.put("expire",token.expiration.valueInMillis)
-                    ?.put("now",System.currentTimeMillis())
+                    .put("token", jwt.jwt)
+                    ?.put("expire", jwt.expiration)
+                    ?.put("now", System.currentTimeMillis())
                     ?.put("tenantid", "mobile")
                     ?.put("name", name)
                     ?.put("uid", user.uid)
                     ?.put("phone", StringUtil.phoneReplace(user.phone))
-                    ?.put("paypwdset",payseted)
+                    ?.put("paypwdset", payseted)
                     ?.put("signed", signed)
-                    ?.put("userid",if(user.userid.isNullOrEmpty()) "" else user.userid)))
+                    ?.put("userid", if (user.userid.isNullOrEmpty()) "" else user.userid)))
         } else {
             throw UserLoginFailException("登录错误")
         }
@@ -128,7 +116,7 @@
             else -> exception.message!!
         }
         val temp = request.getParameter("username")
-        if(!temp.isNullOrEmpty()) {
+        if (!temp.isNullOrEmpty()) {
             mobileUserDao.findByLoginid(temp)?.let {
                 if (it.loginpwderror == 0) {
                     it.loginpwderror = 0
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
index da66271..26289f6 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
@@ -6,10 +6,6 @@
 import com.supwisdom.dlpay.api.service.QRCodeService
 import com.supwisdom.dlpay.api.service.UserService
 import com.supwisdom.dlpay.api.util.MobileNumberCheck
-import com.supwisdom.dlpay.framework.core.JwtConfig
-import com.supwisdom.dlpay.framework.core.JwtTokenUtil
-import com.supwisdom.dlpay.framework.domain.JwtRedis
-import com.supwisdom.dlpay.framework.redisrepo.ApiJwtRepository
 import com.supwisdom.dlpay.framework.service.SystemUtilService
 import com.supwisdom.dlpay.framework.util.*
 import com.supwisdom.dlpay.framework.util.Dictionary
@@ -18,6 +14,9 @@
 import com.supwisdom.dlpay.system.service.DictionaryProxy
 import com.supwisdom.dlpay.util.ConstantUtil
 import com.supwisdom.dlpay.util.RSAKeysGenerate
+import com.supwisdom.multitenant.jwt.JwtRequestData
+import com.supwisdom.multitenant.jwt.JwtTenantService
+import com.supwisdom.multitenant.jwt.JwtTokenBuilder
 import mu.KotlinLogging
 import org.apache.commons.lang.StringUtils
 import org.jose4j.jwt.ReservedClaimNames
@@ -35,6 +34,7 @@
 import org.springframework.web.bind.annotation.RequestParam
 import java.time.Duration
 import java.util.*
+import javax.annotation.Resource
 
 
 @RestController
@@ -45,11 +45,10 @@
     @Autowired
     lateinit var redisTemplate: RedisTemplate<String, String>
     @Autowired
-    lateinit var jwtConfig: JwtConfig
-    @Autowired
-    lateinit var apiJwtRepository: ApiJwtRepository
-    @Autowired
     lateinit var systemUtilService: SystemUtilService
+    @Autowired
+    lateinit var jwtTenantService: JwtTenantService
+
     val logger = KotlinLogging.logger { }
 
     @RequestMapping("/time")
@@ -139,7 +138,7 @@
             }
             user.status = TradeDict.STATUS_NORMAL
             user.registerplatform = platform
-            if(!user.registerplatform.isNullOrEmpty()){
+            if (!user.registerplatform.isNullOrEmpty()) {
                 user.lastloginplatform = user.registerplatform!!.split(",")[1]
             }
             user.devuid = uuid
@@ -181,26 +180,18 @@
         val encoder = BCryptPasswordEncoder()
         user!!.loginpwd = encoder.encode(pwd)
         val exp = systemUtilService.getSysparaValueAsInt(SysparaUtil.MOBILE_LOGIN_EXPIRE_IN_SECONDS, 60 * 60 * 24 * 3)
-        jwtConfig.expiration = exp.toLong()
+//        jwtConfig.expiration = exp.toLong()
         val authorities: Collection<GrantedAuthority> = AuthorityUtils.createAuthorityList("ROLE_USER")
         user.auths = authorities
-        val token = JwtTokenUtil(jwtConfig).generateToken(
-                mapOf("uid" to user.uid, "issuer" to "payapi",
-                        "audience" to user.loginid,
-                        Constants.JWT_CLAIM_TENANTID to "mobile",
-                        Constants.JWT_CLAIM_AUTHORITIES to user.authorities))
-        val jwt = JwtRedis().apply {
-            jti = token.jti
-            uid = user.loginid
-            status = TradeDict.JWT_STATUS_NORMAL
-            expiration = token.expiration.valueInMillis
-        }.apply {
-            //删除之前的token
-            if (!user.jti.isNullOrEmpty()) {
-                apiJwtRepository.deleteById(user.jti!!)
-            }
-            apiJwtRepository.save(this)
-        }
+        val builder = JwtTokenBuilder.create()
+                .expiration(exp.toLong())
+                .uid(user.uid)
+                .issuer("payapi")
+                .tenantId("mobile")
+                .authorities(user.authorities?.toList() ?: listOf<Any>())
+        val token = jwtTenantService.generate(builder)
+        val jwt = token.get()
+
         if (user.loginpwderror > 0) {
             user.loginpwderror = 0
             user.loginpwderrortime = null
@@ -226,9 +217,9 @@
                 signed = TradeDict.STATUS_YES
             }
         }
-        return JsonResult.ok("OK").put("token", token.jwtToken)
+        return JsonResult.ok("OK").put("token", jwt.jwt)
                 ?.put("userid", if (user.userid.isNullOrEmpty()) "" else user.userid)
-                ?.put("expire", token.expiration.valueInMillis)
+                ?.put("expire", jwt.expiration)
                 ?.put("now", System.currentTimeMillis())
                 ?.put("phone", StringUtil.phoneReplace(user.phone))
                 ?.put("paypwdset", payseted)
@@ -252,28 +243,29 @@
     @Autowired
     lateinit var citizencardPayService: CitizencardPayService
     @Autowired
-    lateinit var apiJwtRepository: ApiJwtRepository
+    lateinit var qrcodeService: QRCodeService
     @Autowired
-    lateinit var jwtConfig: JwtConfig
+    lateinit var systemUtilService: SystemUtilService
     @Autowired
-    lateinit var qrcodeService:QRCodeService
+    lateinit var jwtTenantService: JwtTenantService
+
+    @Resource(name = "jwtRequestData")
+    lateinit var jwtRequestData: JwtRequestData
+
     val logger = KotlinLogging.logger { }
 
     @RequestMapping("/idtypes")
     fun idtypes(): JsonResult {
-        var dict = dictionaryProxy.getDictionaryAsMap(Dictionary.IDTYPE)
+        val dict = dictionaryProxy.getDictionaryAsMap(Dictionary.IDTYPE)
         return JsonResult.ok("OK").put("idtypes", dict)!!
     }
 
     @RequestMapping("/logout")
-    fun logout(@RequestHeader("Authorization") auth: String?): ResponseEntity<Any> {
-        if (auth == null) {
-            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
-        }
-        val jwt = auth.substring(jwtConfig.tokenHeader.length)
-        val claims = JwtTokenUtil(jwtConfig).verifyToken(jwt)
+    fun logout(): ResponseEntity<Any> {
         SecurityContextHolder.clearContext()
-        apiJwtRepository.deleteById(claims[ReservedClaimNames.JWT_ID].toString())
+        jwtRequestData.jwtToken?.also {
+            jwtTenantService.revoke(it)
+        }
         return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
     }
 
@@ -283,42 +275,35 @@
     @RequestMapping("/infor")
     fun getUserInfor(): JsonResult {
         val p = SecurityContextHolder.getContext().authentication
-        var user = mobileApiService.findUserById(p.name)
+        val user = mobileApiService.findUserById(p.name)
                 ?: return JsonResult.error("用户不存在,请注册")
-        var tk= ""
-        if (!user.jti.isNullOrEmpty()) {
-            var opt = apiJwtRepository.findById(user.jti!!)
-            if(opt.isPresent){
-                var jwt =  opt.get()
-                val cur = System.currentTimeMillis()
-                //token 小于12个小时,则更新它
-                if(jwt.expiration-cur<1000*60*60*12){
-                    val token = JwtTokenUtil(jwtConfig).generateToken(
-                            mapOf("uid" to user.uid, "issuer" to "payapi",
-                                    "audience" to user.loginid,
-                                    Constants.JWT_CLAIM_TENANTID to "mobile",
-                                    Constants.JWT_CLAIM_AUTHORITIES to p.authorities))
-                    jwt = JwtRedis().apply {
-                        jti = token.jti
-                        uid = user.loginid
-                        status = TradeDict.JWT_STATUS_NORMAL
-                        expiration = token.expiration.valueInMillis
-                    }.apply {
-                        //删除之前的token
-                        if (!user.jti.isNullOrEmpty()) {
-                            apiJwtRepository.deleteById(user.jti!!)
-                        }
-                        apiJwtRepository.save(this)
-                    }
-                    user.jti = jwt.jti
+        var tk = ""
+        jwtRequestData.jwtToken?.also {
+            val currentMillis = System.currentTimeMillis()
+            if (it.expiration - currentMillis < 60 * 60 * 12) {
+                val exp = systemUtilService.getSysparaValueAsInt(
+                        SysparaUtil.MOBILE_LOGIN_EXPIRE_IN_SECONDS, 60 * 60 * 24 * 3)
+                val builder = JwtTokenBuilder.create()
+                        .expiration(exp.toLong())
+                        .uid(user.uid)
+                        .issuer("payapi")
+                        .tenantId("mobile")
+                        .authorities(p.authorities.toList())
+                val token = jwtTenantService.generate(builder)
+                if (token.isPresent) {
+                    user.jti = token.get().jti
                     mobileApiService.saveUser(user)
-                    tk = token.jwtToken
+                    tk = token.get().jwt
+                    jwtTenantService.revoke(jwtRequestData.jwtToken)
+                } else {
+                    tk = jwtRequestData.jwtToken.jwt
                 }
             }
         }
         return JsonResult.ok("OK").put("now", System.currentTimeMillis())
                 ?.put("token", tk)!!
     }
+
     /**
      * 验证码生成,内部校验
      * */
@@ -625,9 +610,9 @@
                 ?.put("name", name)
                 ?.put("needrebind", needrebind)
                 ?.put("signed", signed)
-                ?.put("version","1")
-                ?.put("minversion","1")
-                ?.put("versionmsg","1")
+                ?.put("version", "1")
+                ?.put("minversion", "1")
+                ?.put("versionmsg", "1")
                 ?.put("userid", if (user.userid.isNullOrEmpty()) "" else user.userid)!!.put("t", t)!!
     }
 
@@ -666,7 +651,7 @@
         val pwdtimes = user.checkLoginpwdtime()
         if (pwdtimes == -1) {
             if (!user.jti.isNullOrEmpty()) {
-                apiJwtRepository.deleteById(user.jti!!)
+                jwtTenantService.revoke(jwtRequestData.jwtToken)
             }
             return JsonResult.error(-1, "原密码错误次数过多,将退出系统,请重新登录系统或点击忘记密码功能找回密码")
         } else if (pwdtimes == 1) {
@@ -760,9 +745,9 @@
         val user = mobileApiService.findUserById(p.name)
                 ?: return JsonResult.error("用户不存在,请注册")
         val resp = qrcodeService.encodeCode(user.uid)
-        return if(resp.retcode==0){
+        return if (resp.retcode == 0) {
             JsonResult.ok("ok").put("qrcode", resp.retmsg)!!
-        }else{
+        } else {
             JsonResult.error(resp.retmsg)
         }
     }
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/security.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/security.kt
index 9db3122..8e1892a 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/security.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/security.kt
@@ -1,18 +1,13 @@
 package com.supwisdom.dlpay
 
-import com.supwisdom.dlpay.framework.core.JwtConfig
-import com.supwisdom.dlpay.framework.core.JwtTokenUtil
 import com.supwisdom.dlpay.framework.core.PasswordBCryptConfig
-import com.supwisdom.dlpay.framework.redisrepo.ApiJwtRepository
 import com.supwisdom.dlpay.framework.security.MyAuthenticationFailureHandler
 import com.supwisdom.dlpay.framework.security.ValidateCodeSecurityConfig
 import com.supwisdom.dlpay.framework.service.impl.MultiTenantOperatorDetailService
-import com.supwisdom.dlpay.framework.util.Constants
-import com.supwisdom.dlpay.framework.util.TradeDict
 import com.supwisdom.dlpay.mobile.AuthLoginFailHandler
 import com.supwisdom.dlpay.mobile.AuthLoginSuccessHandler
 import com.supwisdom.dlpay.mobile.service.MobileUserService
-import org.jose4j.jwt.ReservedClaimNames
+import com.supwisdom.multitenant.jwt.JwtRequestData
 import org.jose4j.jwt.consumer.InvalidJwtException
 import org.jose4j.lang.JoseException
 import org.springframework.beans.factory.annotation.Autowired
@@ -42,6 +37,7 @@
 import org.springframework.web.filter.OncePerRequestFilter
 import java.security.SecureRandom
 import java.util.*
+import javax.annotation.Resource
 import javax.servlet.FilterChain
 import javax.servlet.http.HttpServletRequest
 import javax.servlet.http.HttpServletResponse
@@ -50,60 +46,19 @@
 
 @Component
 class ApiJwtAuthenticationFilter : OncePerRequestFilter() {
-    @Autowired
-    lateinit var jwtConfig: JwtConfig
 
-    @Autowired
-    lateinit var apiJwtRepository: ApiJwtRepository
-
-    private var jwtUtil: JwtTokenUtil? = null
-
-    private fun getUtil(): JwtTokenUtil {
-        if (jwtUtil == null) {
-            jwtUtil = JwtTokenUtil((jwtConfig))
-        }
-        return jwtUtil as JwtTokenUtil
-    }
+    @Resource(name = "jwtRequestData")
+    private lateinit var jwtRequestData: JwtRequestData
 
     override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
-        request.getHeader(jwtConfig.header)?.let { authHeader ->
+        jwtRequestData.jwtToken?.let { jwt ->
             try {
-                val jwt = if (authHeader.startsWith(jwtConfig.tokenHeader)) {
-                    authHeader.substring(jwtConfig.tokenHeader.length)
-                } else {
-                    throw JoseException("JWT Header error")
-                }
-                val claims = getUtil().verifyToken(jwt)
-                apiJwtRepository.findById(claims[ReservedClaimNames.JWT_ID].toString()).let {
-                    if (!it.isPresent) {
-                        throw JoseException("JWT has not been register")
-                    }
-                    // token 已被设为黑名单
-                    if (it.get().status != TradeDict.JWT_STATUS_NORMAL) {
-                        throw JoseException("JWT status error : ${it.get().status}")
-                    }
-                }
-                if (jwtConfig.multiTenant) {
-                    val tenantId = request.getHeader(Constants.HEADER_TETANTID)
-                    if (tenantId == null) {
-                        response.status = HttpStatus.UNAUTHORIZED.value()
-                        return
-                    }
-                    if (claims[Constants.JWT_CLAIM_TENANTID] != tenantId) {
-                        response.status = HttpStatus.UNAUTHORIZED.value()
-                        return
-                    }
-                }
-                val auth = UsernamePasswordAuthenticationToken(claims[Constants.JWT_CLAIM_UID], null,
-                        (claims[Constants.JWT_CLAIM_AUTHORITIES] as ArrayList<*>)
+                val auth = UsernamePasswordAuthenticationToken(jwt.uid, null,
+                        (jwt.authorities as ArrayList<*>)
                                 .map { SimpleGrantedAuthority(it as String) })
                 SecurityContextHolder.getContext().authentication = auth
             } catch (e: InvalidJwtException) {
                 SecurityContextHolder.clearContext()
-                if (e.hasExpired()) {
-                    // jwt 过期后返回 401
-                    apiJwtRepository.deleteById(e.jwtContext.jwtClaims.jwtId)
-                }
                 response.status = HttpStatus.UNAUTHORIZED.value()
                 return
             } catch (e: JoseException) {
@@ -126,20 +81,9 @@
 
 @Component
 class MobileSecurityFilter : OncePerRequestFilter() {
-    @Autowired
-    lateinit var jwtConfig: JwtConfig
 
-    @Autowired
-    lateinit var apiJwtRepository: ApiJwtRepository
-
-    private var jwtUtil: JwtTokenUtil? = null
-
-    private fun getUtil(): JwtTokenUtil {
-        if (jwtUtil == null) {
-            jwtUtil = JwtTokenUtil((jwtConfig))
-        }
-        return jwtUtil as JwtTokenUtil
-    }
+    @Resource(name = "jwtRequestData")
+    private lateinit var jwtRequestData: JwtRequestData
 
     override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
         var context: String? = request.contextPath
@@ -159,44 +103,14 @@
             filterChain.doFilter(request, response)
             return
         }
-        request.getHeader(jwtConfig.header)?.let { authHeader ->
+        jwtRequestData.jwtToken?.let { jwt ->
             try {
-                val jwt = if (authHeader.startsWith(jwtConfig.tokenHeader)) {
-                    authHeader.substring(jwtConfig.tokenHeader.length)
-                } else {
-                    throw JoseException("JWT Header error")
-                }
-                val claims = getUtil().verifyToken(jwt)
-                apiJwtRepository.findById(claims[ReservedClaimNames.JWT_ID].toString()).let {
-                    if (!it.isPresent) {
-                        throw JoseException("JWT has not been register")
-                    }
-                    // token 已被设为黑名单
-                    if (it.get().status != TradeDict.JWT_STATUS_NORMAL) {
-                        throw JoseException("JWT status error : ${it.get().status}")
-                    }
-                }
-                if (jwtConfig.multiTenant) {
-                    val tenantId = request.getHeader(Constants.HEADER_TETANTID)
-                    if (tenantId == null) {
-                        response.status = HttpStatus.UNAUTHORIZED.value()
-                        return
-                    }
-                    if (claims[Constants.JWT_CLAIM_TENANTID] != tenantId) {
-                        response.status = HttpStatus.UNAUTHORIZED.value()
-                        return
-                    }
-                }
-                val auth = UsernamePasswordAuthenticationToken(claims[Constants.JWT_CLAIM_UID], null,
-                        (claims[Constants.JWT_CLAIM_AUTHORITIES] as ArrayList<*>)
+                val auth = UsernamePasswordAuthenticationToken(jwt.uid, null,
+                        (jwt.authorities as ArrayList<*>)
                                 .map { SimpleGrantedAuthority(it as String) })
                 SecurityContextHolder.getContext().authentication = auth
             } catch (e: InvalidJwtException) {
                 SecurityContextHolder.clearContext()
-                if (e.hasExpired()) {
-                    // jwt 过期后返回 401
-                    apiJwtRepository.deleteById(e.jwtContext.jwtClaims.jwtId)
-                }
                 response.status = HttpStatus.UNAUTHORIZED.value()
                 return
             } catch (e: JoseException) {
@@ -373,6 +287,11 @@
                 // 设置 Web MVC 应用权限
                 http.apply(validateCodeSecurityConfig)
                         .and()
+                        .headers { headers ->
+                            headers.xssProtection { xssProtection ->
+                                xssProtection.xssProtectionEnabled(true)
+                            }
+                        }
                         .authorizeRequests()
                         .antMatchers("/login", "/login/form", "/mobileapi/**", "/userinfor").permitAll()
                         .antMatchers("/static/**").permitAll()
@@ -395,6 +314,7 @@
                         .deleteCookies("JSESSIONID")
                         .invalidateHttpSession(true)
                         .and().csrf().ignoringAntMatchers("oauth/**")
+
                 // 设置 Web MVC 应用权限
 //                http.apply(validateCodeSecurityConfig)
 //                        .and()
diff --git a/payapi/src/main/resources/application.properties b/payapi/src/main/resources/application.properties
index 906a5be..377a873 100644
--- a/payapi/src/main/resources/application.properties
+++ b/payapi/src/main/resources/application.properties
@@ -32,8 +32,8 @@
 ## quartz task scheduler
 shopbalance.updater.cron=*/10 * * * * ?
 dayend.settletask.cron=0 3/30 2-3 * * ?
-query.third.transdtl.result.cron=7 0/1 * * * ?
-payapi.sourcetype.checker.scheduler=7 3/10 * * * ?
+query.third.transdtl.result.cron=-
+payapi.sourcetype.checker.scheduler=-
 citizencard.dolosstask.cron=-
 ################################################
 # user password
diff --git a/payapi/src/test/kotlin/com/supwisdom/dlpay/controller/security_controller_test.kt b/payapi/src/test/kotlin/com/supwisdom/dlpay/controller/security_controller_test.kt
index 24853db..65bd05b 100644
--- a/payapi/src/test/kotlin/com/supwisdom/dlpay/controller/security_controller_test.kt
+++ b/payapi/src/test/kotlin/com/supwisdom/dlpay/controller/security_controller_test.kt
@@ -1,8 +1,8 @@
 package com.supwisdom.dlpay.controller
 
 import com.supwisdom.dlpay.MvcBaseTest
-import com.supwisdom.dlpay.framework.core.JwtConfig
 import com.supwisdom.dlpay.framework.util.HmacUtil
+import com.supwisdom.multitenant.jwt.config.JwtProperties
 import io.restassured.RestAssured
 import io.restassured.RestAssured.*
 import io.restassured.http.ContentType
@@ -28,7 +28,7 @@
     private var port: Int = 0
 
     @Autowired
-    lateinit var jwtConfig: JwtConfig
+    private lateinit var jwtProperties: JwtProperties
 
     @Before
     fun setUp() {
@@ -96,7 +96,7 @@
     @Test
     fun testJwtRefresh() {
         getJwt(appid, appsecret).also { jwt ->
-            given().header(jwtConfig.header, "${jwtConfig.tokenHeader}$jwt")
+            given().header(jwtProperties.jwtHeader, "${jwtProperties.schema} $jwt")
                     .`when`()
                     .get("/api/auth/refresh")
                     .then()