From: Tang Cheng Date: Tue, 23 Apr 2019 06:12:35 +0000 (+0800) Subject: 重构了 jwt , 支持黑名单机制 X-Git-Tag: 1.0.0^2~259 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=9c202fcfaca71294ea1213e34e9e1bf7ab9b8722;p=epayment%2Ffood_payapi.git 重构了 jwt , 支持黑名单机制 --- diff --git a/config/application-devel-pg-local.properties b/config/application-devel-pg-local.properties index 175bb6b0..ad1ccb2a 100644 --- a/config/application-devel-pg-local.properties +++ b/config/application-devel-pg-local.properties @@ -7,6 +7,7 @@ spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false spring.datasource.url=jdbc:postgresql://localhost:5432/payapi spring.datasource.username=payapi spring.datasource.password=123456 +database.dbtype=postgresql # Redis settings redis.server=localhost redis.port=6379 diff --git a/config/application-devel-pg.properties b/config/application-devel-pg.properties index 92b7ecfb..d21bd66d 100644 --- a/config/application-devel-pg.properties +++ b/config/application-devel-pg.properties @@ -3,6 +3,7 @@ spring.main.banner-mode=off 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 +database.dbtype=postgresql # Postgresql settings spring.datasource.url=jdbc:postgresql://172.28.201.70:15432/payapi spring.datasource.username=payapi diff --git a/src/main/java/com/supwisdom/dlpay/framework/core/DatabaseConfig.java b/src/main/java/com/supwisdom/dlpay/framework/core/DatabaseConfig.java new file mode 100644 index 00000000..16f454ad --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/core/DatabaseConfig.java @@ -0,0 +1,18 @@ +package com.supwisdom.dlpay.framework.core; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class DatabaseConfig { + @Value("${database.dbtype:oracle}") + private String dbType; + + public String getDbType() { + return dbType; + } + + public void setDbType(String dbType) { + this.dbType = dbType; + } +} diff --git a/src/main/java/com/supwisdom/dlpay/framework/core/JwtToken.java b/src/main/java/com/supwisdom/dlpay/framework/core/JwtToken.java new file mode 100644 index 00000000..072ea5d8 --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/core/JwtToken.java @@ -0,0 +1,39 @@ +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/src/main/java/com/supwisdom/dlpay/framework/core/JwtTokenUtil.java b/src/main/java/com/supwisdom/dlpay/framework/core/JwtTokenUtil.java index 20344ab7..9c0a35b9 100644 --- a/src/main/java/com/supwisdom/dlpay/framework/core/JwtTokenUtil.java +++ b/src/main/java/com/supwisdom/dlpay/framework/core/JwtTokenUtil.java @@ -5,6 +5,7 @@ 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; @@ -26,7 +27,7 @@ public class JwtTokenUtil { return jwtConfig.getHeader(); } - public String generateToken(Map params) throws JoseException { + public JwtToken generateToken(Map 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) { @@ -57,16 +58,16 @@ public class JwtTokenUtil { jws.setKey(key.getKey()); jws.setKeyIdHeaderValue(key.getKeyId()); jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256); - return jws.getCompactSerialization(); + return new JwtToken(claims.getJwtId(), jws.getCompactSerialization(), claims.getExpirationTime()); } - public String generateToken(UserDetails userDetails) throws JoseException { + public JwtToken generateToken(UserDetails userDetails) throws JoseException, MalformedClaimException { Map claims = new HashMap<>(); claims.put("uid", userDetails.getUsername()); return generateToken(claims); } - public Map> verifyToken(String token) throws JoseException, InvalidJwtException { + public Map verifyToken(String token) throws JoseException, InvalidJwtException { Map keySpec = new HashMap<>(); keySpec.put("kty", "oct"); keySpec.put("k", jwtConfig.getSecret()); @@ -74,9 +75,8 @@ public class JwtTokenUtil { 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 - .setRequireSubject() // the JWT must have a subject claim - .setExpectedIssuer("supwisdom") // whom the JWT needs to have been issued by .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)) @@ -84,24 +84,6 @@ public class JwtTokenUtil { // Validate the JWT and process it to the Claims JwtClaims jwtClaims = jwtConsumer.processToClaims(token); - System.out.println("JWT validation succeeded! " + jwtClaims); - return jwtClaims.flattenClaims(); - // InvalidJwtException will be thrown, if the JWT failed processing or validation in anyway. - // Hopefully with meaningful explanations(s) about what went wrong. - -// // Programmatic access to (some) specific reasons for JWT invalidity is also possible -// // should you want different error handling behavior for certain conditions. -// -// // Whether or not the JWT has expired being one common reason for invalidity -// if (e.hasExpired()) -// { -// System.out.println("JWT expired at " + e.getJwtContext().getJwtClaims().getExpirationTime()); -// } -// -// // Or maybe the audience was invalid -// if (e.hasErrorCode(ErrorCodes.AUDIENCE_INVALID)) -// { -// System.out.println("JWT had wrong audience: " + e.getJwtContext().getJwtClaims().getAudience()); -// } + return jwtClaims.getClaimsMap(); } } diff --git a/src/main/java/com/supwisdom/dlpay/framework/dao/TaskLockDao.java b/src/main/java/com/supwisdom/dlpay/framework/dao/TaskLockDao.java index 6c36c5d7..576c089a 100644 --- a/src/main/java/com/supwisdom/dlpay/framework/dao/TaskLockDao.java +++ b/src/main/java/com/supwisdom/dlpay/framework/dao/TaskLockDao.java @@ -21,12 +21,14 @@ public interface TaskLockDao extends JpaRepository { @Query(value = "select to_char(sysdate,'yyyymmdd') as hostdate,to_char(sysdate,'hh24miss') as hosttime,to_char(sysdate,'yyyymmddhh24miss') as hostdatetime, sysdate from dual", nativeQuery = true) SystemDateTime getOracleDatetime(); + @Query(value = "select to_char(CURRENT_TIMESTAMP,'yyyymmdd') as hostdate," + "" + + "to_char(CURRENT_TIMESTAMP,'hh24miss') as hosttime,to_char(CURRENT_TIMESTAMP,'yyyymmddhh24miss') as hostdatetime," + + " CURRENT_TIMESTAMP as sysdate", nativeQuery = true) + SystemDateTime getPGDatetime(); + @Query(value = " select to_char(sysdate,'yyyyMMddhh24miss')||to_char(SEQ_REFNO.nextval,'FM000000') as billno from dual ", nativeQuery = true) String getOracleRefno(); - - - //================= database=PG =================// @Query(value = " select to_char(CURRENT_TIMESTAMP,'yyyyMMddhh24miss')||to_char(nextval('SEQ_REFNO'),'FM000000') as billno ", nativeQuery = true) String getPgRefno(); diff --git a/src/main/java/com/supwisdom/dlpay/framework/domain/ApiClientRedis.java b/src/main/java/com/supwisdom/dlpay/framework/domain/ApiClientRedis.java index 2cb43ebc..d7bb0168 100644 --- a/src/main/java/com/supwisdom/dlpay/framework/domain/ApiClientRedis.java +++ b/src/main/java/com/supwisdom/dlpay/framework/domain/ApiClientRedis.java @@ -1,8 +1,8 @@ package com.supwisdom.dlpay.framework.domain; +import org.springframework.data.annotation.Id; import org.springframework.data.redis.core.RedisHash; -import javax.persistence.Id; @RedisHash("app_client") public class ApiClientRedis { diff --git a/src/main/java/com/supwisdom/dlpay/framework/domain/DateItem.java b/src/main/java/com/supwisdom/dlpay/framework/domain/DateItem.java deleted file mode 100644 index f4aa56ec..00000000 --- a/src/main/java/com/supwisdom/dlpay/framework/domain/DateItem.java +++ /dev/null @@ -1,26 +0,0 @@ -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/JwtRedis.java b/src/main/java/com/supwisdom/dlpay/framework/domain/JwtRedis.java new file mode 100644 index 00000000..60647488 --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/domain/JwtRedis.java @@ -0,0 +1,39 @@ +package com.supwisdom.dlpay.framework.domain; + +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; + + +@RedisHash("api_jwt") +public class JwtRedis { + @Id + String jti; + + String status; + + 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; + } +} diff --git a/src/main/java/com/supwisdom/dlpay/framework/domain/PersonRedis.java b/src/main/java/com/supwisdom/dlpay/framework/domain/PersonRedis.java index 2c258640..623e55ae 100644 --- a/src/main/java/com/supwisdom/dlpay/framework/domain/PersonRedis.java +++ b/src/main/java/com/supwisdom/dlpay/framework/domain/PersonRedis.java @@ -1,9 +1,8 @@ package com.supwisdom.dlpay.framework.domain; +import org.springframework.data.annotation.Id; import org.springframework.data.redis.core.RedisHash; -import javax.persistence.Id; - @RedisHash("person") public class PersonRedis { @Id diff --git a/src/main/java/com/supwisdom/dlpay/framework/redisrepo/ApiJwtRepository.java b/src/main/java/com/supwisdom/dlpay/framework/redisrepo/ApiJwtRepository.java new file mode 100644 index 00000000..38a3fbe6 --- /dev/null +++ b/src/main/java/com/supwisdom/dlpay/framework/redisrepo/ApiJwtRepository.java @@ -0,0 +1,7 @@ +package com.supwisdom.dlpay.framework.redisrepo; + +import com.supwisdom.dlpay.framework.domain.JwtRedis; +import org.springframework.data.repository.CrudRepository; + +public interface ApiJwtRepository 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 0cf7bcf2..07d5e63e 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 @@ -1,10 +1,10 @@ package com.supwisdom.dlpay.framework.service.impl; +import com.supwisdom.dlpay.framework.core.DatabaseConfig; 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; @@ -15,18 +15,13 @@ 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 DatabaseConfig databaseConfig; @Autowired private TaskLockDao taskLockDao; @@ -70,17 +65,21 @@ public class SystemUtilServiceImpl implements SystemUtilService { * 获取oracle数据库时间 */ private SystemDateTime 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; + return taskLockDao.getOracleDatetime(); + } + + private SystemDateTime getPGDatetime() { + return taskLockDao.getPGDatetime(); } @Override public SystemDateTime getSysdatetime() { - return getOracleDatetime(); + switch (databaseConfig.getDbType()) { + case "postgresql": + return getPGDatetime(); + default: + return getOracleDatetime(); + } } @Override diff --git a/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java b/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java index 5596b244..6b458362 100644 --- a/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java +++ b/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java @@ -11,6 +11,12 @@ public class TradeDict { public static final String STATUS_CLOSED = "closed"; public static final String STATUS_LOCKED = "locked"; + /** + * JWT 状态 + */ + public static final String JWT_STATUS_NORMAL = "normal"; + public static final String JWT_STATUS_BLACKLIST = "blacklist"; + /** * 流水状态 * temp -- 临时流水 @@ -35,20 +41,20 @@ public class TradeDict { /** * 支付方式 - * */ - public static final String PAYTYPE_CASH="cash"; - public static final String PAYTYPE_BALANCE="balance"; + */ + public static final String PAYTYPE_CASH = "cash"; + public static final String PAYTYPE_BALANCE = "balance"; /** * feetype * - 消费:折扣、搭伙费(管理费) * - 充值:优惠、服务费(手续费) - * */ + */ - public static final String FEETYPE_CONSUME_MEALER="mealer"; //收管理费 - public static final String FEETYPE_CONSUME_DISCOUNT="discount"; //折扣款抵扣 + public static final String FEETYPE_CONSUME_MEALER = "mealer"; //收管理费 + public static final String FEETYPE_CONSUME_DISCOUNT = "discount"; //折扣款抵扣 - public static final String PAYTYPE_RECHARGE_COUPON="coupon"; // 充值优惠 - public static final String PAYTYPE_RECHARGE_SERVICEFEE="servicefee"; //收服务费 + public static final String PAYTYPE_RECHARGE_COUPON = "coupon"; // 充值优惠 + public static final String PAYTYPE_RECHARGE_SERVICEFEE = "servicefee"; //收服务费 } 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 index 5bcea3a0..521fe7c1 100644 --- a/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt +++ b/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt @@ -7,12 +7,13 @@ import com.supwisdom.dlpay.framework.dao.ApiClientDao import com.supwisdom.dlpay.framework.security.validate.ImageCodeUtil import com.supwisdom.dlpay.framework.security.validate.VerifyCode 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.security.OperUtil -import com.supwisdom.dlpay.framework.service.OperatorDetailService +import com.supwisdom.dlpay.framework.redisrepo.ApiJwtRepository import com.supwisdom.dlpay.framework.service.SystemUtilService import com.supwisdom.dlpay.framework.util.HmacUtil +import com.supwisdom.dlpay.framework.util.TradeDict import com.supwisdom.dlpay.system.service.CommonService import com.supwisdom.dlpay.system.service.FunctionService import mu.KotlinLogging @@ -38,10 +39,13 @@ import javax.servlet.http.HttpServletResponse class ApiAuthController { @Autowired - lateinit var repo: ApiClientRepository + lateinit var apiClientRepository: ApiClientRepository @Autowired - lateinit var apiClient: ApiClientDao + lateinit var apiJwtRepository: ApiJwtRepository + + @Autowired + lateinit var apiClientDao: ApiClientDao @Autowired lateinit var systemUtil: SystemUtilService @@ -49,9 +53,9 @@ class ApiAuthController { @Autowired lateinit var jwtConfig: JwtConfig - @GetMapping("/gettoken") - fun loginInit(appid: String): ResponseEntity { - apiClient.findById(appid).run { + @GetMapping(value = ["/gettoken", "/gettoken/{clientid}"]) + fun loginInit(appid: String, @PathVariable clientid: String?): ResponseEntity { + apiClientDao.findById(appid).run { if (!isPresent) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build() } @@ -64,12 +68,12 @@ class ApiAuthController { val token = generateRandomToken() val now = systemUtil.sysdatetime.hostdatetime ApiClientRedis().apply { - id = appid + id = if (clientid == null) appid else "$appid-$clientid" loginTimestamp = now roles = it.roles this.token = HmacUtil.HMACSHA256(token, it.secret) }.also { - repo.save(it) + apiClientRepository.save(it) } return ResponseEntity.ok(ResponseBodyBuilder.create() .data("token", token) @@ -88,20 +92,25 @@ class ApiAuthController { return (api.token == secret) } - @GetMapping("/authentication") - fun login(appid: String, secret: String): ResponseEntity { - return repo.findById(appid).let { + @GetMapping(value = ["/authentication", "/authentication/{clientid}"]) + fun login(appid: String, secret: String, @PathVariable clientid: String?): ResponseEntity { + val requestId = if (clientid == null) appid else "$appid-$clientid" + return apiClientRepository.findById(requestId).let { if (it.isPresent && checkSecretToken(it.get(), secret)) { - it.get().also { client -> - client.token = "" - repo.save(client) - } + apiClientRepository.delete(it.get()) val token = JwtTokenUtil(jwtConfig).generateToken( - mapOf("uid" to appid, "issuer" to "supwisdom", - "subject" to "payapi", + mapOf("uid" to appid, "issuer" to "payapi", + "audience" to (clientid ?: appid), "authorities" to it.get().roles.split(";"))) + JwtRedis().apply { + jti = token.jti + status = TradeDict.JWT_STATUS_NORMAL + expiration = token.expiration.value + }.apply { + apiJwtRepository.save(this) + } ResponseEntity.ok(ResponseBodyBuilder.create() - .data("jwt", token) + .data("jwt", token.jwtToken) .data("appid", appid) .success()) } else { diff --git a/src/main/kotlin/com/supwisdom/dlpay/security.kt b/src/main/kotlin/com/supwisdom/dlpay/security.kt index 491cc463..abb1dd7d 100644 --- a/src/main/kotlin/com/supwisdom/dlpay/security.kt +++ b/src/main/kotlin/com/supwisdom/dlpay/security.kt @@ -3,9 +3,12 @@ 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.ValidateCodeSecurityConfig import com.supwisdom.dlpay.framework.service.OperatorDetailService +import com.supwisdom.dlpay.framework.util.TradeDict import org.jose4j.jwt.consumer.InvalidJwtException +import org.jose4j.lang.JoseException import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -26,6 +29,7 @@ import org.springframework.security.web.authentication.AuthenticationSuccessHand import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl import org.springframework.security.web.util.matcher.AntPathRequestMatcher +import org.springframework.stereotype.Component import org.springframework.web.filter.OncePerRequestFilter import java.security.SecureRandom import javax.servlet.FilterChain @@ -34,18 +38,50 @@ import javax.servlet.http.HttpServletResponse import javax.sql.DataSource -class ApiJwtAuthenticationFilter(jwt: JwtTokenUtil) : OncePerRequestFilter() { - private val jwtUtil = jwt +@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 + } override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) { - request.getHeader(jwtUtil.header)?.let { jwt -> + request.getHeader(jwtConfig.header)?.let { jwt -> try { - val claims = jwtUtil.verifyToken(jwt) + val claims = getUtil().verifyToken(jwt) + apiJwtRepository.findById(claims["jti"].toString()).let { + if (!it.isPresent) { + throw JoseException("JWT has not been register") + } + if (it.get().status != TradeDict.JWT_STATUS_NORMAL) { + throw JoseException("JWT status error : ${it.get().status}") + } + } val auth = UsernamePasswordAuthenticationToken(claims["uid"], null, - claims["authorities"]?.map { SimpleGrantedAuthority(it as String) }) + (claims["authorities"] as ArrayList<*>) + .map { SimpleGrantedAuthority(it as String) }) SecurityContextHolder.getContext().authentication = auth } catch (e: InvalidJwtException) { - SecurityContextHolder.clearContext(); + SecurityContextHolder.clearContext() + if (e.hasExpired()) { + apiJwtRepository.deleteById(e.jwtContext.jwtClaims.jwtId) + response.sendError(HttpStatus.UNAUTHORIZED.value(), e.message) + } else { + response.sendError(HttpStatus.BAD_REQUEST.value(), e.message) + } + } catch (e: JoseException) { + SecurityContextHolder.clearContext() response.sendError(HttpStatus.BAD_REQUEST.value(), e.message) } } @@ -62,13 +98,13 @@ class WebSecurityConfig { class ApiWebSecurityConfigurationAdapter : WebSecurityConfigurerAdapter() { @Autowired - lateinit var jwtConfig: JwtConfig + lateinit var apiFilter: ApiJwtAuthenticationFilter override fun configure(http: HttpSecurity) { // 设置 API 访问权限管理 http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() - .addFilterAfter(ApiJwtAuthenticationFilter(JwtTokenUtil(jwtConfig)), + .addFilterAfter(apiFilter, UsernamePasswordAuthenticationFilter::class.java) .antMatcher("/api/**") .authorizeRequests()