From 1a0675643b123d49360991e493993e5dd0a6b913 Mon Sep 17 00:00:00 2001 From: zhuyijun Date: Sat, 17 Sep 2022 12:48:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=B5=84=E6=BA=90=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=92=8Ctoken=EF=BC=8C=E9=85=8D=E7=BD=AE=E4=B8=8A?= =?UTF-8?q?=E4=B8=8B=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 24 +- zyjblogs-cloud-dependencies/pom.xml | 55 + zyjblogs-common-spring-boot-starter/pom.xml | 6 + ...seContextHandler.java => BaseContext.java} | 16 +- .../starter/common/entity/dto/ContextDto.java | 4 - .../exception/AbstractBusinessException.java | 34 + .../exception/AbstractFrameworkException.java | 3 + .../common/utils/jwt/JwtParserUtils.java | 234 ---- .../starter/common/utils/rsa/RsaUtils.java | 209 +-- .../common/utils/web/RequestUtils.java | 35 + zyjblogs-gateway/pom.xml | 47 +- .../zyjblogs/gateway/filter/AuthFilter.java | 6 +- zyjblogs-oauth-spring-boot-starter/pom.xml | 68 + ...authFeignInterceptorAutoConfiguration.java | 42 + .../oauth/exception/AuthRuntimeException.java | 17 + .../oauth}/resource/ResourceServerConfig.java | 27 +- .../security/OauthAccessTokenConverter.java | 38 + .../starter/oauth}/security/TokenConfig.java | 22 +- .../main/resources/META-INF/spring.factories | 5 + .../src/main/resources/public.txt | 9 + zyjblogs-oauth/pom.xml | 24 +- .../oauth/config/security/JwtTokenConfig.java | 17 +- zyjblogs-rbac/pom.xml | 43 +- .../security/WebSecurityConfiguration.java | 24 - .../user/controller/UserController.java | 5 + zyjblogs-redis-spring-boot-starter/pom.xml | 71 + .../starter/redis/config/RedisConfig.java | 53 + .../redis/utils/RedisTemplateHandler.java | 1157 +++++++++++++++++ .../utils/lock/AbstractLockExecutor.java | 9 + .../utils/lock/DefaultLockKeyBuilder.java | 15 + .../redis/utils/lock/LockExecutor.java | 40 + .../redis/utils/lock/LockKeyBuilder.java | 14 + .../starter/redis/utils/lock/RedisLock.java | 66 + .../redis/utils/lock/RedisLockTemplate.java | 122 ++ .../utils/lock/RedisTemplateLockExecutor.java | 63 + .../src/main/resources/META-INF/MANIFEST.MF | 5 + .../pom.properties | 5 + .../pom.xml | 77 ++ .../main/resources/META-INF/spring.factories | 3 + zyjblogs-web-spring-boot-starter/pom.xml | 6 +- 40 files changed, 2236 insertions(+), 484 deletions(-) create mode 100644 zyjblogs-cloud-dependencies/pom.xml rename zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/context/{BaseContextHandler.java => BaseContext.java} (75%) create mode 100644 zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/exception/AbstractBusinessException.java delete mode 100644 zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/utils/jwt/JwtParserUtils.java create mode 100644 zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/utils/web/RequestUtils.java create mode 100644 zyjblogs-oauth-spring-boot-starter/pom.xml create mode 100644 zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/config/OauthFeignInterceptorAutoConfiguration.java create mode 100644 zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/exception/AuthRuntimeException.java rename {zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/config => zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth}/resource/ResourceServerConfig.java (68%) create mode 100644 zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/security/OauthAccessTokenConverter.java rename {zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/config => zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth}/security/TokenConfig.java (65%) create mode 100644 zyjblogs-oauth-spring-boot-starter/src/main/resources/META-INF/spring.factories create mode 100644 zyjblogs-oauth-spring-boot-starter/src/main/resources/public.txt delete mode 100644 zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/config/security/WebSecurityConfiguration.java create mode 100644 zyjblogs-redis-spring-boot-starter/pom.xml create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/config/RedisConfig.java create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/RedisTemplateHandler.java create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/AbstractLockExecutor.java create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/DefaultLockKeyBuilder.java create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/LockExecutor.java create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/LockKeyBuilder.java create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/RedisLock.java create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/RedisLockTemplate.java create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/RedisTemplateLockExecutor.java create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/MANIFEST.MF create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/maven.cn.zyjblogs.starter.zyjblogs-web-spring-boot-starter/pom.properties create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/maven.cn.zyjblogs.starter.zyjblogs-web-spring-boot-starter/pom.xml create mode 100644 zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/spring.factories diff --git a/pom.xml b/pom.xml index f237d13..39445fc 100644 --- a/pom.xml +++ b/pom.xml @@ -13,16 +13,10 @@ zyjblogs-parent pom 1.0-SNAPSHOT - - zyjblogs-oauth - zyjblogs-rbac - zyjblogs-gateway - zyjblogs-common-spring-boot-starter - zyjblogs-web-spring-boot-starter - 1.0-SNAPSHOT + 11 11 2.3.12.RELEASE @@ -41,7 +35,7 @@ 3.4.3 8.0.21 - 2.2.6.RELEASE + 2.2.9.RELEASE 2.12.2 1.5.4 @@ -87,6 +81,16 @@ jackson-core ${jackson.version} + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + mysql @@ -220,10 +224,6 @@ - - org.springframework.boot - spring-boot-devtools - org.projectlombok lombok diff --git a/zyjblogs-cloud-dependencies/pom.xml b/zyjblogs-cloud-dependencies/pom.xml new file mode 100644 index 0000000..da26571 --- /dev/null +++ b/zyjblogs-cloud-dependencies/pom.xml @@ -0,0 +1,55 @@ + + + + zyjblogs-parent + cn.zyjblogs + 1.0-SNAPSHOT + + + 4.0.0 + zyjblogs-cloud-dependencies + 1.0-SNAPSHOT + pom + + 11 + 11 + + + + + cn.zyjblogs.starter + zyjblogs-common-spring-boot-starter + ${zyjblogs.version} + + + cn.zyjblogs.starter + zyjblogs-oauth-spring-boot-starter + ${zyjblogs.version} + + + cn.zyjblogs.starter + zyjblogs-redis-spring-boot-starter + ${zyjblogs.version} + + + cn.zyjblogs.starter + zyjblogs-web-spring-boot-starter + ${zyjblogs.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${compler.maven.plugin.version} + + true + + + + + \ No newline at end of file diff --git a/zyjblogs-common-spring-boot-starter/pom.xml b/zyjblogs-common-spring-boot-starter/pom.xml index 86da8b4..afc7386 100644 --- a/zyjblogs-common-spring-boot-starter/pom.xml +++ b/zyjblogs-common-spring-boot-starter/pom.xml @@ -6,16 +6,22 @@ zyjblogs-parent cn.zyjblogs 1.0-SNAPSHOT + 4.0.0 cn.zyjblogs.starter zyjblogs-common-spring-boot-starter 1.0-SNAPSHOT + 11 11 + + org.springframework.boot + spring-boot-starter-web + org.springframework.boot diff --git a/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/context/BaseContextHandler.java b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/context/BaseContext.java similarity index 75% rename from zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/context/BaseContextHandler.java rename to zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/context/BaseContext.java index b428b76..5394bb3 100644 --- a/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/context/BaseContextHandler.java +++ b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/context/BaseContext.java @@ -6,14 +6,15 @@ import com.alibaba.ttl.TransmittableThreadLocal; import java.util.Locale; -public class BaseContextHandler { +public class BaseContext { private static final ThreadLocal CONTEXT = new TransmittableThreadLocal() { + @Override public ContextDto initialValue() { return ContextDto.builder().build(); } }; - public BaseContextHandler() { + public BaseContext() { } public static ContextDto get() { @@ -32,13 +33,6 @@ public class BaseContextHandler { return ((ContextDto)CONTEXT.get()).getUsername(); } - public static Integer getSource() { - return ((ContextDto)CONTEXT.get()).getSource(); - } - - public static String getTenantId() { - return ((ContextDto)CONTEXT.get()).getTenantId(); - } public static String getToken() { return ((ContextDto)CONTEXT.get()).getToken(); @@ -48,10 +42,6 @@ public class BaseContextHandler { return (ContextDto) BeanUtils.map((ContextDto)CONTEXT.get(), ContextDto.class); } - public static Locale getLanguage() { - return ((ContextDto)CONTEXT.get()).getLanguage(); - } - public static void clear() { CONTEXT.remove(); } diff --git a/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/dto/ContextDto.java b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/dto/ContextDto.java index 5c0782b..1b0a4bb 100644 --- a/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/dto/ContextDto.java +++ b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/dto/ContextDto.java @@ -11,11 +11,7 @@ import java.util.Locale; @NoArgsConstructor @Builder public class ContextDto { - private String tenantId; private String userId; private String username; - private Integer source; - private Integer isTenantCreator; private String token; - private Locale language; } \ No newline at end of file diff --git a/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/exception/AbstractBusinessException.java b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/exception/AbstractBusinessException.java new file mode 100644 index 0000000..b59d2bc --- /dev/null +++ b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/exception/AbstractBusinessException.java @@ -0,0 +1,34 @@ +package cn.zyjblogs.starter.common.exception; + +import cn.zyjblogs.starter.common.entity.response.HttpCode; + +public class AbstractBusinessException extends RuntimeException{ + private static final long serialVersionUID = -6583471361241853199L; + /** + * 异常码 + */ + private HttpCode responseCode; + + /** + * 异常描述 + */ + private String message; + + + public AbstractBusinessException() { + + } + + /** + * 创建业务异常对象 + * + * @param responseCode 错误码 + * @param message 错误消息 + */ + public AbstractBusinessException(HttpCode responseCode, String message) { + super(message); + this.responseCode = responseCode; + this.message = message; + } + +} diff --git a/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/exception/AbstractFrameworkException.java b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/exception/AbstractFrameworkException.java index d97154f..5abb81d 100644 --- a/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/exception/AbstractFrameworkException.java +++ b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/exception/AbstractFrameworkException.java @@ -1,5 +1,8 @@ package cn.zyjblogs.starter.common.exception; +/** + * @author zhuyijun + */ public abstract class AbstractFrameworkException extends RuntimeException { public AbstractFrameworkException(String message) { super(message); diff --git a/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/utils/jwt/JwtParserUtils.java b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/utils/jwt/JwtParserUtils.java deleted file mode 100644 index e571251..0000000 --- a/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/utils/jwt/JwtParserUtils.java +++ /dev/null @@ -1,234 +0,0 @@ -// -// Source code recreated from a .class file by IntelliJ IDEA -// (powered by FernFlower decompiler) -// - -package cn.zyjblogs.starter.common.utils.jwt; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Clock; -import io.jsonwebtoken.CompressionCodec; -import io.jsonwebtoken.CompressionCodecResolver; -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.Header; -import io.jsonwebtoken.JwsHeader; -import io.jsonwebtoken.Jwt; -import io.jsonwebtoken.JwtParser; -import io.jsonwebtoken.MalformedJwtException; -import io.jsonwebtoken.PrematureJwtException; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.SignatureException; -import io.jsonwebtoken.SigningKeyResolver; -import io.jsonwebtoken.UnsupportedJwtException; -import io.jsonwebtoken.impl.DefaultClaims; -import io.jsonwebtoken.impl.DefaultClock; -import io.jsonwebtoken.impl.DefaultHeader; -import io.jsonwebtoken.impl.DefaultJws; -import io.jsonwebtoken.impl.DefaultJwsHeader; -import io.jsonwebtoken.impl.DefaultJwt; -import io.jsonwebtoken.impl.DefaultJwtParser; -import io.jsonwebtoken.impl.TextCodec; -import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver; -import io.jsonwebtoken.impl.crypto.JwtSignatureValidator; -import io.jsonwebtoken.lang.Assert; -import io.jsonwebtoken.lang.Objects; -import io.jsonwebtoken.lang.Strings; -import java.security.Key; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Map; -import javax.crypto.spec.SecretKeySpec; - -public class JwtParserUtils extends DefaultJwtParser { - private long allowedClockSkewMillis = 0L; - private static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; - private CompressionCodecResolver compressionCodecResolver = new DefaultCompressionCodecResolver(); - private byte[] keyBytes; - private Key key; - private SigningKeyResolver signingKeyResolver; - private Clock clock; - private boolean checkExpired; - - public JwtParserUtils(boolean checkExpired) { - this.clock = DefaultClock.INSTANCE; - this.checkExpired = checkExpired; - } - - public Jwt parse(String jwt) throws ExpiredJwtException, MalformedJwtException, SignatureException { - Assert.hasText(jwt, "JWT String argument cannot be null or empty."); - String base64UrlEncodedHeader = null; - String base64UrlEncodedPayload = null; - String base64UrlEncodedDigest = null; - int delimiterCount = 0; - StringBuilder sb = new StringBuilder(128); - char[] var7 = jwt.toCharArray(); - int var8 = var7.length; - - for(int var9 = 0; var9 < var8; ++var9) { - char c = var7[var9]; - if (c == '.') { - CharSequence tokenSeq = Strings.clean(sb); - String token = tokenSeq != null ? tokenSeq.toString() : null; - if (delimiterCount == 0) { - base64UrlEncodedHeader = token; - } else if (delimiterCount == 1) { - base64UrlEncodedPayload = token; - } - - ++delimiterCount; - sb.setLength(0); - } else { - sb.append(c); - } - } - - if (delimiterCount != 2) { - String msg = "JWT strings must contain exactly 2 period characters. Found: " + delimiterCount; - throw new MalformedJwtException(msg); - } else { - if (sb.length() > 0) { - base64UrlEncodedDigest = sb.toString(); - } - - if (base64UrlEncodedPayload == null) { - throw new MalformedJwtException("JWT string '" + jwt + "' is missing a body/payload."); - } else { - Header header = null; - CompressionCodec compressionCodec = null; - String payload; - if (base64UrlEncodedHeader != null) { - payload = TextCodec.BASE64URL.decodeToString(base64UrlEncodedHeader); - Map m = this.readValue(payload); - if (base64UrlEncodedDigest != null) { - header = new DefaultJwsHeader(m); - } else { - header = new DefaultHeader(m); - } - - compressionCodec = this.compressionCodecResolver.resolveCompressionCodec((Header)header); - } - - if (compressionCodec != null) { - byte[] decompressed = compressionCodec.decompress(TextCodec.BASE64URL.decode(base64UrlEncodedPayload)); - payload = new String(decompressed, Strings.UTF_8); - } else { - payload = TextCodec.BASE64URL.decodeToString(base64UrlEncodedPayload); - } - - Claims claims = null; - if (payload.charAt(0) == '{' && payload.charAt(payload.length() - 1) == '}') { - Map claimsMap = this.readValue(payload); - claims = new DefaultClaims(claimsMap); - } - - if (base64UrlEncodedDigest != null) { - JwsHeader jwsHeader = (JwsHeader)header; - SignatureAlgorithm algorithm = null; - String object; - if (header != null) { - object = jwsHeader.getAlgorithm(); - if (Strings.hasText(object)) { - algorithm = SignatureAlgorithm.forName(object); - } - } - - if (algorithm == null || algorithm == SignatureAlgorithm.NONE) { - object = "JWT string has a digest/signature, but the header does not reference a valid signature algorithm."; - throw new MalformedJwtException(object); - } - - if (this.key != null && this.keyBytes != null) { - throw new IllegalStateException("A key object and key bytes cannot both be specified. Choose either."); - } - - if ((this.key != null || this.keyBytes != null) && this.signingKeyResolver != null) { - object = this.key != null ? "a key object" : "key bytes"; - throw new IllegalStateException("A signing key resolver and " + object + " cannot both be specified. Choose either."); - } - - Key key = this.key; - if (key == null) { - byte[] keyBytes = this.keyBytes; - if (Objects.isEmpty(keyBytes) && this.signingKeyResolver != null) { - if (claims != null) { - key = this.signingKeyResolver.resolveSigningKey(jwsHeader, claims); - } else { - key = this.signingKeyResolver.resolveSigningKey(jwsHeader, payload); - } - } - - if (!Objects.isEmpty(keyBytes)) { - Assert.isTrue(algorithm.isHmac(), "Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance."); - key = new SecretKeySpec(keyBytes, algorithm.getJcaName()); - } - } - - Assert.notNull(key, "A signing key must be specified if the specified JWT is digitally signed."); - String jwtWithoutSignature = base64UrlEncodedHeader + "." + base64UrlEncodedPayload; - - JwtSignatureValidator validator; - try { - validator = this.createSignatureValidator(algorithm, (Key)key); - } catch (IllegalArgumentException var26) { - String algName = algorithm.getValue(); - String msg = "The parsed JWT indicates it was signed with the " + algName + " signature algorithm, but the specified signing key of type " + key.getClass().getName() + " may not be used to validate " + algName + " signatures. Because the specified signing key reflects a specific and expected algorithm, and the JWT does not reflect this algorithm, it is likely that the JWT was not expected and therefore should not be trusted. Another possibility is that the parser was configured with the incorrect signing key, but this cannot be assumed for security reasons."; - throw new UnsupportedJwtException(msg, var26); - } - - if (!validator.isValid(jwtWithoutSignature, base64UrlEncodedDigest)) { - String msg = "JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted."; - throw new SignatureException(msg); - } - } - - boolean allowSkew = this.allowedClockSkewMillis > 0L; - if (claims != null) { - Date now = this.clock.now(); - long nowTime = now.getTime(); - Date exp = claims.getExpiration(); - String nbfVal; - SimpleDateFormat sdf; - if (exp != null && this.checkExpired) { - long maxTime = nowTime - this.allowedClockSkewMillis; - Date max = allowSkew ? new Date(maxTime) : now; - if (max.after(exp)) { - sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - String expVal = sdf.format(exp); - nbfVal = sdf.format(now); - long differenceMillis = maxTime - exp.getTime(); - String msg = "JWT expired at " + expVal + ". Current time: " + nbfVal + ", a difference of " + differenceMillis + " milliseconds. Allowed clock skew: " + this.allowedClockSkewMillis + " milliseconds."; - throw new ExpiredJwtException((Header)header, claims, msg); - } - } - - Date nbf = claims.getNotBefore(); - if (nbf != null) { - long minTime = nowTime + this.allowedClockSkewMillis; - Date min = allowSkew ? new Date(minTime) : now; - if (min.before(nbf)) { - sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - nbfVal = sdf.format(nbf); - String nowVal = sdf.format(now); - long differenceMillis = nbf.getTime() - minTime; - String msg = "JWT must not be accepted before " + nbfVal + ". Current time: " + nowVal + ", a difference of " + differenceMillis + " milliseconds. Allowed clock skew: " + this.allowedClockSkewMillis + " milliseconds."; - throw new PrematureJwtException((Header)header, claims, msg); - } - } - } - - Object body = claims != null ? claims : payload; - if (base64UrlEncodedDigest != null) { - return new DefaultJws((JwsHeader)header, body, base64UrlEncodedDigest); - } else { - return new DefaultJwt((Header)header, body); - } - } - } - } - - public JwtParser setSigningKey(Key key) { - Assert.notNull(key, "signing key cannot be null."); - this.key = key; - return this; - } -} diff --git a/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/utils/rsa/RsaUtils.java b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/utils/rsa/RsaUtils.java index 030b4fd..f921e09 100644 --- a/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/utils/rsa/RsaUtils.java +++ b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/utils/rsa/RsaUtils.java @@ -1,114 +1,147 @@ package cn.zyjblogs.starter.common.utils.rsa; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.Cipher; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.security.*; -import java.security.spec.InvalidKeySpecException; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; +import java.util.stream.Collectors; public class RsaUtils { - private static final int DEFAULT_KEY_SIZE = 2048; + private static final String DEFAULT_ALGORITHM = "RSA"; + private static final Logger log = LoggerFactory.getLogger(RsaUtils.class); - /** - * 从文件中读取公钥 - * - * @param filename 公钥保存路径 - * @return 公钥对象 - * @throws Exception - */ - public static PublicKey getPublicKey(String filename) throws Exception { - byte[] bytes = readFile(filename); - return getPublicKey(bytes); + private RsaUtils() { } /** - * 从文件中读取密钥 + * 生成密钥对 * - * @param filename 私钥保存路径 - * @return 私钥对象 - * @throws Exception + * @param + * @return cn.com.hatechframework.common.entity.dto.RsaKeyDto + * @author meiji + * @date 2021/11/4 9:43 */ - public static PrivateKey getPrivateKey(String filename) throws Exception { - byte[] bytes = readFile(filename); - return getPrivateKey(bytes); + public static void generateKey() throws NoSuchAlgorithmException { + KeyPairGenerator generator = KeyPairGenerator.getInstance(DEFAULT_ALGORITHM); + // 密钥长度 + generator.initialize(1024, new SecureRandom()); + KeyPair keyPair = generator.generateKeyPair(); + RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); + // 密钥 Base64 字符串化 + String publicKey = Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded()); + String privateKey = Base64.getEncoder().encodeToString((rsaPrivateKey.getEncoded())); + System.out.println("公钥:\r\n" + publicKey); + System.out.println("私钥:\r\n" + privateKey); + } + + /** + * 公钥加密 + * + * @param rawStr + * @param publicKey + * @return java.lang.String + * @author meiji + * @date 2021/11/4 9:43 + */ + public static String publicKeyEncrypt(String rawStr, String publicKey) throws Exception { + byte[] decoded = Base64.getDecoder().decode(publicKey); + RSAPublicKey rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance(DEFAULT_ALGORITHM).generatePublic(new X509EncodedKeySpec(decoded)); + Cipher cipher = Cipher.getInstance(DEFAULT_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey); + return Base64.getEncoder().encodeToString(cipher.doFinal(rawStr.getBytes(StandardCharsets.UTF_8))); + } + + /** + * 公钥解密 + * + * @param encodeStr + * @param publicKey + * @return java.lang.String + * @author meiji + * @date 2021/11/4 9:43 + */ + public static String publicKeyDecrypt(String encodeStr, String publicKey) throws Exception { + byte[] decoded = Base64.getDecoder().decode(publicKey); + RSAPublicKey rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance(DEFAULT_ALGORITHM).generatePublic(new X509EncodedKeySpec(decoded)); + Cipher cipher = Cipher.getInstance(DEFAULT_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey); + return new String(cipher.doFinal(Base64.getDecoder().decode(encodeStr.getBytes(StandardCharsets.UTF_8)))); + } + + /** + * 私钥加密 + * + * @param rawStr + * @param privateKey + * @return java.lang.String + * @author meiji + * @date 2021/11/4 9:43 + */ + public static String privateKeyEncrypt(String rawStr, String privateKey) throws Exception { + byte[] decoded = Base64.getDecoder().decode(privateKey); + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) KeyFactory.getInstance(DEFAULT_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(decoded)); + Cipher cipher = Cipher.getInstance(DEFAULT_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, rsaPrivateKey); + return Base64.getEncoder().encodeToString(cipher.doFinal(rawStr.getBytes(StandardCharsets.UTF_8))); + } + + /** + * 私钥解密 + * + * @param encodeStr + * @param privateKey + * @return java.lang.String + * @author meiji + * @date 2021/11/4 9:43 + */ + public static String privateKeyDecrypt(String encodeStr, String privateKey) throws Exception { + byte[] decoded = Base64.getDecoder().decode(privateKey); + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) KeyFactory.getInstance(DEFAULT_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(decoded)); + Cipher cipher = Cipher.getInstance(DEFAULT_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, rsaPrivateKey); + return new String(cipher.doFinal(Base64.getDecoder().decode(encodeStr.getBytes(StandardCharsets.UTF_8)))); } /** * 获取公钥 * - * @param bytes 公钥的字节形式 - * @return - * @throws Exception + * @param path 公钥路径 + * @return 公钥 + * @author tanyuanzhi + * @date 2021/11/9 10:06 */ - private static PublicKey getPublicKey(byte[] bytes) throws Exception { - bytes = Base64.getDecoder().decode(bytes); - X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes); - KeyFactory factory = KeyFactory.getInstance("RSA"); - return factory.generatePublic(spec); - } - - /** - * 获取密钥 - * - * @param bytes 私钥的字节形式 - * @return - * @throws Exception - */ - private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, - InvalidKeySpecException { - bytes = Base64.getDecoder().decode(bytes); - PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes); - KeyFactory factory = KeyFactory.getInstance("RSA"); - return factory.generatePrivate(spec); - } - - /** - * 根据密文,生成rsa公钥和私钥,并写入指定文件 - * - * @param publicKeyFilename 公钥文件绝对路径,比如:xxx/xxx/rsa_key.pub - * @param privateKeyFilename 私钥文件绝对路径,比如:xxx/xxx/rsa_key - * @param secret 生成密钥的密文 - */ - public static void generateKey(String publicKeyFilename, String privateKeyFilename, String - secret, int keySize) throws Exception { - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - SecureRandom secureRandom = new SecureRandom(secret.getBytes()); - keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom); - KeyPair keyPair = keyPairGenerator.genKeyPair(); - // 获取公钥并写出 - byte[] publicKeyBytes = keyPair.getPublic().getEncoded(); - publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes); - writeFile(publicKeyFilename, publicKeyBytes); - // 获取私钥并写出 - byte[] privateKeyBytes = keyPair.getPrivate().getEncoded(); - privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes); - writeFile(privateKeyFilename, privateKeyBytes); - } - public static void generateKey(String publicKeyFilename, String privateKeyFilename, String - secret) throws Exception { - generateKey(publicKeyFilename,privateKeyFilename,secret,DEFAULT_KEY_SIZE); - } - - private static byte[] readFile(String fileName) throws Exception { - return Files.readAllBytes(new File(fileName).toPath()); - } - - private static void writeFile(String destPath, byte[] bytes) throws IOException { - File dest = new File(destPath); - File dir = dest.getParentFile(); - //判断目录是否存在,不在则新建 - if(!dir.exists()){ - dir.mkdirs(); + public static PublicKey getPublicKeyByPath(String path) { + InputStream inputStream = RsaUtils.class.getClassLoader().getResourceAsStream(path); + if (inputStream == null) { + log.error("获取公钥出错,找不到公钥文件"); + return null; } - //判断文件是否存在,不在则新建 - if (!dest.exists()) { - dest.createNewFile(); + String publicKeyStr = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining()); + publicKeyStr = publicKeyStr.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", ""); + byte[] keyBytes = Base64.getMimeDecoder().decode(publicKeyStr); + // 通过X509编码的Key指令获得公钥对象 + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + try { + KeyFactory keyFactory = KeyFactory.getInstance(DEFAULT_ALGORITHM); + PublicKey publicKey = keyFactory.generatePublic(keySpec); + return publicKey; + } catch (Exception e) { + log.error("获取公钥出错", e); + return null; } - Files.write(dest.toPath(), bytes); } + } diff --git a/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/utils/web/RequestUtils.java b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/utils/web/RequestUtils.java new file mode 100644 index 0000000..9c947b1 --- /dev/null +++ b/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/utils/web/RequestUtils.java @@ -0,0 +1,35 @@ +package cn.zyjblogs.starter.common.utils.web; + +import cn.zyjblogs.starter.common.entity.constant.HttpHeaderConstant; +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.http.HttpServletRequest; + +public class RequestUtils { + + private RequestUtils() { + + } + + /** + * 请求是否来自网关 + * + * @param request 请求信息 + * @return boolean + * @date 2022/3/3 14:19 + */ + public static boolean requestFromGateway(HttpServletRequest request) { + return StringUtils.isNotEmpty(request.getHeader(HttpHeaderConstant.REQUEST_FROM_GATEWAY_KEY)); + } + + /** + * 请求是否来自feign调用 + * + * @param request 请求信息 + * @return boolean + * @date 2022/3/3 14:19 + */ + public static boolean requestFromFeign(HttpServletRequest request) { + return StringUtils.isNotEmpty(request.getHeader(HttpHeaderConstant.REQUEST_FROM_FEIGN_KEY)); + } +} diff --git a/zyjblogs-gateway/pom.xml b/zyjblogs-gateway/pom.xml index 4bb27ac..71cc304 100644 --- a/zyjblogs-gateway/pom.xml +++ b/zyjblogs-gateway/pom.xml @@ -3,9 +3,10 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - zyjblogs-parent + zyjblogs-cloud-dependencies cn.zyjblogs 1.0-SNAPSHOT + 4.0.0 @@ -31,11 +32,6 @@ spring-boot-starter-actuator - - org.springframework.boot - spring-boot-starter-data-redis-reactive - - org.apache.commons commons-pool2 @@ -43,7 +39,6 @@ zyjblogs-web-spring-boot-starter cn.zyjblogs.starter - ${zyjblogs.version} org.springframework.boot @@ -51,26 +46,27 @@ + + + + + + + com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config - - - checker-qual - org.checkerframework - - - error_prone_annotations - com.google.errorprone - - com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery + + org.springframework.cloud + spring-cloud-starter-loadbalancer + org.springframework.boot spring-boot-starter-test @@ -89,22 +85,5 @@ com.alibaba.csp sentinel-datasource-nacos - - org.springframework.cloud - spring-cloud-starter-loadbalancer - - - - - - - - - - - cn.zyjblogs.starter - zyjblogs-common-spring-boot-starter - ${zyjblogs.version} - \ No newline at end of file diff --git a/zyjblogs-gateway/src/main/java/cn/zyjblogs/gateway/filter/AuthFilter.java b/zyjblogs-gateway/src/main/java/cn/zyjblogs/gateway/filter/AuthFilter.java index ba149c0..de37932 100644 --- a/zyjblogs-gateway/src/main/java/cn/zyjblogs/gateway/filter/AuthFilter.java +++ b/zyjblogs-gateway/src/main/java/cn/zyjblogs/gateway/filter/AuthFilter.java @@ -63,6 +63,9 @@ public class AuthFilter implements GlobalFilter { if (isWhileList(path)) { return chain.filter(exchange); } + if (StringUtils.isEmpty(token)) { + return getErrorMono(response, HttpCode.UNAUTHORIZED, "无访问权限"); + } if (isExpired(token)) { log.info("token过期"); return getErrorMono(response, HttpCode.UNAUTHORIZED, "invalid_token"); @@ -90,9 +93,6 @@ public class AuthFilter implements GlobalFilter { * @date 2021/11/15 19:17 */ private boolean isExpired(String token) { - if (StringUtils.isEmpty(token)) { - return true; - } if (!token.startsWith(HttpHeaderConstant.AUTHORIZATION_TYPE)) { return true; } diff --git a/zyjblogs-oauth-spring-boot-starter/pom.xml b/zyjblogs-oauth-spring-boot-starter/pom.xml new file mode 100644 index 0000000..ba9b76f --- /dev/null +++ b/zyjblogs-oauth-spring-boot-starter/pom.xml @@ -0,0 +1,68 @@ + + + + zyjblogs-parent + cn.zyjblogs + 1.0-SNAPSHOT + + 4.0.0 + + cn.zyjblogs.starter + zyjblogs-oauth-spring-boot-starter + 1.0-SNAPSHOT + + + 11 + 11 + + + + cn.zyjblogs.starter + zyjblogs-common-spring-boot-starter + ${zyjblogs.version} + + + + + org.springframework.boot + spring-boot-autoconfigure + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure + + + org.springframework.cloud + spring-cloud-starter-oauth2 + + + cn.zyjblogs.starter + zyjblogs-redis-spring-boot-starter + ${zyjblogs.version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${compler.maven.plugin.version} + + true + + + + + \ No newline at end of file diff --git a/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/config/OauthFeignInterceptorAutoConfiguration.java b/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/config/OauthFeignInterceptorAutoConfiguration.java new file mode 100644 index 0000000..706e30d --- /dev/null +++ b/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/config/OauthFeignInterceptorAutoConfiguration.java @@ -0,0 +1,42 @@ +package cn.zyjblogs.starter.oauth.config; + +import cn.zyjblogs.starter.common.entity.constant.HttpHeaderConstant; +import cn.zyjblogs.starter.common.entity.context.BaseContext; +import feign.RequestInterceptor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author zhuyijun + */ +@Configuration +@ConditionalOnClass(RequestInterceptor.class) +public class OauthFeignInterceptorAutoConfiguration { + + @Bean + public RequestInterceptor oauthFeignRequestInterceptor() { + return template -> { + String token = BaseContext.getToken(); + if (StringUtils.isNotEmpty(token)) { + token = tokenCompletion(token); + template.header(HttpHeaderConstant.AUTHORIZATION, token); + } + template.header(HttpHeaderConstant.REQUEST_FROM_FEIGN_KEY, HttpHeaderConstant.REQUEST_FROM_FEIGN_VALUE); + }; + } + + /** + * token信息补全,token不能为空 + * @author liuweicheng + * @date 2022/03/12 + * @return 补全后得token + */ + private String tokenCompletion(String token){ + if(token.indexOf(HttpHeaderConstant.AUTHORIZATION_TYPE) != 0){ + token = HttpHeaderConstant.AUTHORIZATION_TYPE + " " + token; + } + return token; + } +} diff --git a/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/exception/AuthRuntimeException.java b/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/exception/AuthRuntimeException.java new file mode 100644 index 0000000..a22b33b --- /dev/null +++ b/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/exception/AuthRuntimeException.java @@ -0,0 +1,17 @@ +package cn.zyjblogs.starter.oauth.exception; + +import cn.zyjblogs.starter.common.entity.response.HttpCode; +import cn.zyjblogs.starter.common.exception.AbstractBusinessException; + +/** + * @author zhuyijun + */ +public class AuthRuntimeException extends AbstractBusinessException { + public AuthRuntimeException() { + super(); + } + + public AuthRuntimeException(HttpCode responseCode, String message) { + super(responseCode, message); + } +} diff --git a/zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/config/resource/ResourceServerConfig.java b/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/resource/ResourceServerConfig.java similarity index 68% rename from zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/config/resource/ResourceServerConfig.java rename to zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/resource/ResourceServerConfig.java index 85630be..93ccc83 100644 --- a/zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/config/resource/ResourceServerConfig.java +++ b/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/resource/ResourceServerConfig.java @@ -1,7 +1,8 @@ -package cn.zyjblogs.rbac.config.resource; +package cn.zyjblogs.starter.oauth.resource; import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.http.SessionCreationPolicy; @@ -19,13 +20,15 @@ import org.springframework.security.oauth2.provider.token.TokenStore; @Configuration @EnableResourceServer @RequiredArgsConstructor +@RefreshScope public class ResourceServerConfig extends ResourceServerConfigurerAdapter { - private static final String RESOURCE_ID="zyjblogs-rbac"; + @Value("${spring.application.name}") + private String resourceId; private final TokenStore tokenStore; @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { - resources.resourceId(RESOURCE_ID) + resources.resourceId(resourceId) // 验证令牌的服务 .tokenStore(tokenStore) .stateless(true); @@ -33,13 +36,17 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { - http.authorizeRequests() - .antMatchers("/**") - .access("#oauth2.hasAnyScope('all')") + http .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and() - .csrf().disable() - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS); + .authorizeRequests() + .antMatchers("/**") + .authenticated() +// .anyRequest().permitAll() +// .access("#oauth2.hasAnyScope('all')") + .and() + .csrf().disable(); + } } diff --git a/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/security/OauthAccessTokenConverter.java b/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/security/OauthAccessTokenConverter.java new file mode 100644 index 0000000..95042c1 --- /dev/null +++ b/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/security/OauthAccessTokenConverter.java @@ -0,0 +1,38 @@ +package cn.zyjblogs.starter.oauth.security; + +import cn.zyjblogs.starter.common.entity.constant.ContextKeyConstant; +import cn.zyjblogs.starter.common.entity.context.BaseContext; +import cn.zyjblogs.starter.common.entity.dto.ContextDto; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author zhuyijun + */ +@Component +public class OauthAccessTokenConverter extends DefaultAccessTokenConverter { + private static final Logger log = LoggerFactory.getLogger(OauthAccessTokenConverter.class); + + @Override + public Map convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { + return super.convertAccessToken(token, authentication); + + } + + @Override + public OAuth2Authentication extractAuthentication(Map map) { + OAuth2Authentication oAuth2Authentication = super.extractAuthentication(map); + oAuth2Authentication.setDetails(map); + String userId = (String) map.get(ContextKeyConstant.USER_ID_KEY); + String username = (String) map.get(ContextKeyConstant.USERNAME_KEY); + BaseContext.set(ContextDto.builder().userId(userId).username(username).token("").build()); + log.info("进入"); + return oAuth2Authentication; + } +} diff --git a/zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/config/security/TokenConfig.java b/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/security/TokenConfig.java similarity index 65% rename from zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/config/security/TokenConfig.java rename to zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/security/TokenConfig.java index 2a702a3..e3b3761 100644 --- a/zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/config/security/TokenConfig.java +++ b/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/security/TokenConfig.java @@ -1,5 +1,6 @@ -package cn.zyjblogs.rbac.config.security; +package cn.zyjblogs.starter.oauth.security; +import lombok.RequiredArgsConstructor; import org.apache.commons.io.IOUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -9,13 +10,16 @@ import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenCo import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; import java.io.IOException; +import java.nio.charset.StandardCharsets; /** * @author zhuyijun */ @Configuration +@RequiredArgsConstructor public class TokenConfig { - private String SIGNING_KEY="zyjblogs123"; + + private final OauthAccessTokenConverter oauthAccessTokenConverter; /** * 令牌存储策略 * @return @@ -29,18 +33,14 @@ public class TokenConfig { @Bean public JwtAccessTokenConverter accessTokenConverter(){ JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); - String privateKey = null; - String publicKey = null; try { - publicKey = IOUtils.toString(new ClassPathResource("public.txt").getInputStream()); - privateKey = IOUtils.toString(new ClassPathResource("private.txt").getInputStream()); + String publicKey = IOUtils.toString(new ClassPathResource("public.txt").getInputStream(), StandardCharsets.UTF_8); + // 公钥验签 + converter.setVerifierKey(publicKey); + converter.setAccessTokenConverter(oauthAccessTokenConverter); + return converter; } catch (final IOException e) { throw new RuntimeException("获取不到公私密钥"); } - // 私钥签名 - converter.setSigningKey(privateKey); - // 公钥验签 - converter.setVerifierKey(publicKey); - return converter; } } diff --git a/zyjblogs-oauth-spring-boot-starter/src/main/resources/META-INF/spring.factories b/zyjblogs-oauth-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..58ec7c5 --- /dev/null +++ b/zyjblogs-oauth-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,5 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + cn.zyjblogs.starter.oauth.config.OauthFeignInterceptorAutoConfiguration,\ + cn.zyjblogs.starter.oauth.resource.ResourceServerConfig,\ + cn.zyjblogs.starter.oauth.security.TokenConfig,\ + cn.zyjblogs.starter.oauth.security.OauthAccessTokenConverter diff --git a/zyjblogs-oauth-spring-boot-starter/src/main/resources/public.txt b/zyjblogs-oauth-spring-boot-starter/src/main/resources/public.txt new file mode 100644 index 0000000..2ed6092 --- /dev/null +++ b/zyjblogs-oauth-spring-boot-starter/src/main/resources/public.txt @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jXKxEFsDsjng2nHppqC +GTR1NQLfHJlGzc5hWalP/YgbJWIqdGXDy704Q2DuuoOe/t6KQcYI6/C7Ua9yumYp +MoKZOA5b7gmh/k0SUfsCErKwzE93DIAnLbRoT/hkGJD1Dn7V7yTzYf2BjaFoY5it +tZJ/UXM18TAqW7S1q0qCuv25Fb9NAEMh63EaX3N+DMW8rg51GBfRvtVfACbIyFo9 +8PW2/wOQhppGWkxdzgJdJUwPhZ+Fo9DZ18044hapYPNuZ31ordIGptYL6pB/0VKh +kbDLk4oOnkhhWW0DmsTSFyhOiaQqtuxdrjPV7sqR1NokreZAtbUctVNezNBlYWoJ +TwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/zyjblogs-oauth/pom.xml b/zyjblogs-oauth/pom.xml index 752939f..b3a6971 100644 --- a/zyjblogs-oauth/pom.xml +++ b/zyjblogs-oauth/pom.xml @@ -3,9 +3,10 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - zyjblogs-parent + zyjblogs-cloud-dependencies cn.zyjblogs 1.0-SNAPSHOT + 4.0.0 @@ -25,7 +26,6 @@ zyjblogs-web-spring-boot-starter cn.zyjblogs.starter - ${zyjblogs.version} org.springframework.boot @@ -59,16 +59,6 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config - - - checker-qual - org.checkerframework - - - error_prone_annotations - com.google.errorprone - - @@ -80,14 +70,16 @@ spring-boot-starter-test - org.springframework.boot - spring-boot-starter-data-redis + cn.zyjblogs.starter + zyjblogs-redis-spring-boot-starter cn.zyjblogs.starter zyjblogs-common-spring-boot-starter - ${zyjblogs.version} - compile + + + org.springframework.boot + spring-boot-devtools diff --git a/zyjblogs-oauth/src/main/java/cn/zyjblogs/oauth/config/security/JwtTokenConfig.java b/zyjblogs-oauth/src/main/java/cn/zyjblogs/oauth/config/security/JwtTokenConfig.java index 4f5e443..f7187b9 100644 --- a/zyjblogs-oauth/src/main/java/cn/zyjblogs/oauth/config/security/JwtTokenConfig.java +++ b/zyjblogs-oauth/src/main/java/cn/zyjblogs/oauth/config/security/JwtTokenConfig.java @@ -1,6 +1,7 @@ package cn.zyjblogs.oauth.config.security; import lombok.RequiredArgsConstructor; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.CharSetUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -13,6 +14,8 @@ import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenCo import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * @author zhuyijun @@ -34,18 +37,16 @@ public class JwtTokenConfig { @Bean public JwtAccessTokenConverter accessTokenConverter(){ JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); - String privateKey = null; - String publicKey = null; try { - publicKey = IOUtils.toString(new ClassPathResource("public.txt").getInputStream()); - privateKey = IOUtils.toString(new ClassPathResource("private.txt").getInputStream()); + String publicKey = IOUtils.toString(new ClassPathResource("public.txt").getInputStream(), StandardCharsets.UTF_8); + String privateKey = IOUtils.toString(new ClassPathResource("private.txt").getInputStream(),StandardCharsets.UTF_8); + // 私钥签名 + converter.setSigningKey(privateKey); + // 公钥验签 + converter.setVerifierKey(publicKey); } catch (final IOException e) { throw new RuntimeException("获取不到公私密钥"); } - // 私钥签名 - converter.setSigningKey(privateKey); - // 公钥验签 - converter.setVerifierKey(publicKey); DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter(); accessTokenConverter.setUserTokenConverter(oauthUserAuthenticationConverter); converter.setAccessTokenConverter(accessTokenConverter); diff --git a/zyjblogs-rbac/pom.xml b/zyjblogs-rbac/pom.xml index 9b19932..cdbd5ef 100644 --- a/zyjblogs-rbac/pom.xml +++ b/zyjblogs-rbac/pom.xml @@ -3,9 +3,10 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - zyjblogs-parent + zyjblogs-cloud-dependencies cn.zyjblogs 1.0-SNAPSHOT + 4.0.0 cn.zyjblogs.pubilc @@ -24,19 +25,23 @@ zyjblogs-web-spring-boot-starter cn.zyjblogs.starter - ${zyjblogs.version} + + + + + + + + + + + + cn.zyjblogs.starter + zyjblogs-oauth-spring-boot-starter - org.springframework.boot - spring-boot-starter-security - - - org.springframework.security.oauth.boot - spring-security-oauth2-autoconfigure - - - org.springframework.cloud - spring-cloud-starter-oauth2 + cn.zyjblogs.starter + zyjblogs-redis-spring-boot-starter org.springframework.cloud @@ -54,16 +59,6 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config - - - checker-qual - org.checkerframework - - - error_prone_annotations - com.google.errorprone - - @@ -74,6 +69,10 @@ org.springframework.boot spring-boot-starter-test + + org.springframework.boot + spring-boot-devtools + diff --git a/zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/config/security/WebSecurityConfiguration.java b/zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/config/security/WebSecurityConfiguration.java deleted file mode 100644 index 777cc6b..0000000 --- a/zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/config/security/WebSecurityConfiguration.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.zyjblogs.rbac.config.security; - -import org.springframework.context.annotation.Configuration; -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; - -@Configuration -@EnableWebSecurity -public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http.csrf().disable(); - //使HttpSecurity接收以"/login/","/oauth/"开头请求, 配置HttpSecurity不阻止swagger页面 - http.authorizeRequests() - .antMatchers("/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**", "/v2/api-docs/**") - .permitAll() - //以下请求必须认证通过 - .antMatchers("/demo/**", "/oauth/**", "/login") - .authenticated() - .anyRequest().permitAll(); - } -} \ No newline at end of file diff --git a/zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/server/user/controller/UserController.java b/zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/server/user/controller/UserController.java index 5ca97ac..3beb947 100644 --- a/zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/server/user/controller/UserController.java +++ b/zyjblogs-rbac/src/main/java/cn/zyjblogs/rbac/server/user/controller/UserController.java @@ -2,7 +2,9 @@ package cn.zyjblogs.rbac.server.user.controller; import cn.zyjblogs.rbac.server.user.po.UserPo; import cn.zyjblogs.rbac.server.user.service.UserService; +import cn.zyjblogs.starter.common.entity.context.BaseContext; import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @@ -15,10 +17,13 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("/user") @RequiredArgsConstructor @ResponseBody +@Log4j2 public class UserController { private final UserService userService; @GetMapping("/id") public UserPo findById(String id){ + log.info(BaseContext.getUserId()); + log.info(BaseContext.getUsername()); return userService.getById(id); } diff --git a/zyjblogs-redis-spring-boot-starter/pom.xml b/zyjblogs-redis-spring-boot-starter/pom.xml new file mode 100644 index 0000000..49c95b1 --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/pom.xml @@ -0,0 +1,71 @@ + + + + zyjblogs-parent + cn.zyjblogs + 1.0-SNAPSHOT + + 4.0.0 + + cn.zyjblogs.starter + zyjblogs-redis-spring-boot-starter + 1.0-SNAPSHOT + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-autoconfigure + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + com.fasterxml.jackson.core + jackson-databind + + + org.projectlombok + lombok + + + + com.fasterxml.jackson.core + jackson-annotations + + + + + com.alibaba + fastjson + + + + org.apache.commons + commons-pool2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${compler.maven.plugin.version} + + true + + + + + \ No newline at end of file diff --git a/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/config/RedisConfig.java b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/config/RedisConfig.java new file mode 100644 index 0000000..9342417 --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/config/RedisConfig.java @@ -0,0 +1,53 @@ +package cn.zyjblogs.starter.redis.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * @author zhuyijun + */ +@Configuration +@AutoConfigureAfter(RedisAutoConfiguration.class) +public class RedisConfig { + + /** + * Redis配置 + * 针对key、value、hashKey、hashValue做序列化 + * @return org.springframework.data.redis.core.RedisTemplate + */ + @Bean + @ConditionalOnMissingBean( + name = {"redisTemplate"} + ) + @SuppressWarnings("all") + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + + //解决查询缓存转换异常的问题 + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL); + GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(om); + + //序列号key value + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer); + + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } +} diff --git a/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/RedisTemplateHandler.java b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/RedisTemplateHandler.java new file mode 100644 index 0000000..93ae8f1 --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/RedisTemplateHandler.java @@ -0,0 +1,1157 @@ +package cn.zyjblogs.starter.redis.utils; + +import org.springframework.context.annotation.Lazy; +import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + + +@Component +@Lazy +public class RedisTemplateHandler { + + @Resource + @Lazy + private RedisTemplate redisTemplate; + + public void set(K key,V value){ + redisTemplate.opsForValue().set(key,value); + } + /** + * @param key 键 + * @param value 值 + * @param l + * @param timeUnit + * @author zhuyijun + * @date 2021/12/16 14:33 + * @return void + */ + public void set(K key,V value,long l,TimeUnit timeUnit){ + redisTemplate.opsForValue().set(key,value,l,timeUnit); + } + + public V get(K key) { + return redisTemplate.opsForValue().get(key); + } + /** + * 删除key + * @param key key + * @author zhuyijun + * @date 2021/12/16 15:45 + * @return void + */ + public void delete(K key) { + redisTemplate.delete(key); + } + + /** + * 通过前缀删除 + * @param key + */ + public void deleteByPrefix(K key){ + Set keys = redisTemplate.keys(key); + if (!CollectionUtils.isEmpty(keys)){ + redisTemplate.delete(keys); + } + } + /** + * 批量删除key + * @param keys + */ + public void delete(Collection keys) { + redisTemplate.delete(keys); + } + + /** + * 序列化key + * @param key + * @return + */ + public byte[] dump(K key) { + return redisTemplate.dump(key); + } + + /** + * 是否存在key + * @param key + * @return + */ + public Boolean hasKey(K key) { + return redisTemplate.hasKey(key); + } + + /** + * 设置过期时间 + * + * @param key + * @param timeout + * @param unit + * @return + */ + public Boolean expire(K key, long timeout, TimeUnit unit) { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 设置过期时间 + * + * @param key + * @param date + * @return + */ + public Boolean expireAt(K key, Date date) { + return redisTemplate.expireAt(key, date); + } + + /** + * 查找匹配的key + * + * @param pattern + * @return + */ + public Set keys(K pattern) { + return redisTemplate.keys(pattern); + } + + /** + * 将当前数据库的 key 移动到给定的数据库 db 当中 + * + * @param key + * @param dbIndex + * @return + */ + public Boolean move(K key, int dbIndex) { + return redisTemplate.move(key, dbIndex); + } + + /** + * 移除 key 的过期时间,key 将持久保持 + * + * @param key + * @return + */ + public Boolean persist(K key) { + return redisTemplate.persist(key); + } + + /** + * 返回 key 的剩余的过期时间 + * + * @param key + * @param unit + * @return + */ + public Long getExpire(K key, TimeUnit unit) { + return redisTemplate.getExpire(key, unit); + } + + /** + * 返回 key 的剩余的过期时间 + * + * @param key + * @return + */ + public Long getExpire(K key) { + return redisTemplate.getExpire(key); + } + /** + * 返回 key 所储存的值的类型 + * + * @param key + * @return + */ + public DataType type(K key) { + return redisTemplate.type(key); + } + /** + * 获取存储在哈希表中指定字段的值 + * + * @param key + * @param field + * @return + */ + public HV hGet(K key, K field) { + return redisTemplate.opsForHash().get(key, field); + } + + + /** + * 获取所有给定字段的值 + * + * @param key + * @return + */ + public Map hGetAll(K key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 获取所有给定字段的值 + * + * @param key + * @param fields + * @return + */ + public List hMultiGet(K key, Collection fields) { + return redisTemplate.opsForHash().multiGet(key, fields); + } + + public boolean hPut(K key, HK hashKey, HV value) { + try { + if (value != null) { + this.redisTemplate.opsForHash().put(key, hashKey, value); + return true; + } else { + return false; + } + } catch (Exception var4) { + return false; + } + + } + + public void hPutAll(K key, Map maps) { + redisTemplate.opsForHash().putAll(key, maps); + } + + /** + * 仅当hashKey不存在时才设置 + * + * @param key + * @param hashKey + * @param value + * @return + */ + public Boolean hPutIfAbsent(K key, HK hashKey, HV value) { + return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value); + } + + /** + * 删除一个或多个哈希表字段 + * + * @param key + * @param fields + * @return + */ + public Long hDelete(K key, Object... fields) { + return redisTemplate.opsForHash().delete(key, fields); + } + + /** + * 查看哈希表 key 中,指定的字段是否存在 + * + * @param key + * @param field + * @return + */ + public boolean hExists(K key, HV field) { + return redisTemplate.opsForHash().hasKey(key, field); + } + + /** + * 为哈希表 key 中的指定字段的整数值加上增量 increment + * + * @param key + * @param field + * @param increment + * @return + */ + public Long hIncrBy(K key, HK field, long increment) { + return redisTemplate.opsForHash().increment(key, field, increment); + } + + /** + * 为哈希表 key 中的指定字段的整数值加上增量 increment + * + * @param key + * @param field + * @param delta + * @return + */ + public Double hIncrByFloat(K key, HK field, double delta) { + return redisTemplate.opsForHash().increment(key, field, delta); + } + + /** + * 获取所有哈希表中的字段 + * + * @param key + * @return + */ + public Set hKeys(K key) { + return redisTemplate.opsForHash().keys(key); + } + + /** + * 获取哈希表中字段的数量 + * + * @param key + * @return + */ + public Long hSize(K key) { + return redisTemplate.opsForHash().size(key); + } + + /** + * 获取哈希表中所有值 + * + * @param key + * @return + */ + public List hValues(K key) { + return redisTemplate.opsForHash().values(key); + } + + /** + * 迭代哈希表中的键值对 + * + * @param key + * @param options + * @return + */ + public Cursor> hScan(K key, ScanOptions options) { + return redisTemplate.opsForHash().scan(key, options); + } + + /**------------------zSet相关操作--------------------------------*/ + + /** + * 添加元素,有序集合是按照元素的score值由小到大排列 + * + * @param key + * @param value + * @param score + * @return + */ + public Boolean zAdd(K key, V value, double score) { + return redisTemplate.opsForZSet().add(key, value, score); + } + + /** + * @param key + * @param values + * @return + */ + public Long zAdd(K key, Set> values) { + return redisTemplate.opsForZSet().add(key, values); + } + + /** + * @param key + * @param values + * @return + */ + public Long zRemove(K key, Object... values) { + return redisTemplate.opsForZSet().remove(key, values); + } + + /** + * 增加元素的score值,并返回增加后的值 + * + * @param key + * @param value + * @param delta + * @return + */ + public Double zIncrementScore(K key, V value, double delta) { + return redisTemplate.opsForZSet().incrementScore(key, value, delta); + } + /** + * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列 + * + * @param key + * @param value + * @return 0表示第一位 + */ + public Long zRank(K key, Object value) { + return redisTemplate.opsForZSet().rank(key, value); + } + + /** + * 返回元素在集合的排名,按元素的score值由大到小排列 + * + * @param key + * @param value + * @return + */ + public Long zReverseRank(K key, Object value) { + return redisTemplate.opsForZSet().reverseRank(key, value); + } + + /** + * 获取集合的元素, 从小到大排序 + * + * @param key + * @param start 开始位置 + * @param end 结束位置, -1查询所有 + * @return + */ + public Set zRange(K key, long start, long end) { + return redisTemplate.opsForZSet().range(key, start, end); + } + + /** + * 获取集合元素, 并且把score值也获取 + * + * @param key + * @param start + * @param end + * @return + */ + public Set> zRangeWithScores(K key, long start, + long end) { + return redisTemplate.opsForZSet().rangeWithScores(key, start, end); + } + + /** + * 根据Score值查询集合元素 + * + * @param key + * @param min 最小值 + * @param max 最大值 + * @return + */ + public Set zRangeByScore(K key, double min, double max) { + return redisTemplate.opsForZSet().rangeByScore(key, min, max); + } + + /** + * 根据Score值查询集合元素, 从小到大排序 + * + * @param key + * @param min 最小值 + * @param max 最大值 + * @return + */ + public Set> zRangeByScoreWithScores(K key, + double min, double max) { + return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max); + } + + /** + * @param key + * @param min + * @param max + * @param start + * @param end + * @return + */ + public Set> zRangeByScoreWithScores(K key, + double min, double max, long start, long end) { + return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max, + start, end); + } + + /** + * 获取集合的元素, 从大到小排序 + * + * @param key + * @param start + * @param end + * @return + */ + public Set zReverseRange(K key, long start, long end) { + return redisTemplate.opsForZSet().reverseRange(key, start, end); + } + + /** + * 获取集合的元素, 从大到小排序, 并返回score值 + * + * @param key + * @param start + * @param end + * @return + */ + public Set> zReverseRangeWithScores(K key, + long start, long end) { + return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, + end); + } + + /** + * 根据Score值查询集合元素, 从大到小排序 + * + * @param key + * @param min + * @param max + * @return + */ + public Set zReverseRangeByScore(K key, double min, + double max) { + return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max); + } + + /** + * 根据Score值查询集合元素, 从大到小排序 + * + * @param key + * @param min + * @param max + * @return + */ + public Set> zReverseRangeByScoreWithScores( + K key, double min, double max) { + return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, + min, max); + } + + /** + * @param key + * @param min + * @param max + * @param start + * @param end + * @return + */ + public Set zReverseRangeByScore(K key, double min, + double max, long start, long end) { + return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, + start, end); + } + + /** + * 根据score值获取集合元素数量 + * + * @param key + * @param min + * @param max + * @return + */ + public Long zCount(K key, double min, double max) { + return redisTemplate.opsForZSet().count(key, min, max); + } + + /** + * 获取集合大小 + * + * @param key + * @return + */ + public Long zSize(K key) { + return redisTemplate.opsForZSet().size(key); + } + + /** + * 获取集合大小 + * + * @param key + * @return + */ + public Long zZCard(K key) { + return redisTemplate.opsForZSet().zCard(key); + } + + /** + * 获取集合中value元素的score值 + * + * @param key + * @param value + * @return + */ + public Double zScore(K key, Object value) { + return redisTemplate.opsForZSet().score(key, value); + } + + /** + * 移除指定索引位置的成员 + * + * @param key + * @param start + * @param end + * @return + */ + public Long zRemoveRange(K key, long start, long end) { + return redisTemplate.opsForZSet().removeRange(key, start, end); + } + + /** + * 根据指定的score值的范围来移除成员 + * + * @param key + * @param min + * @param max + * @return + */ + public Long zRemoveRangeByScore(K key, double min, double max) { + return redisTemplate.opsForZSet().removeRangeByScore(key, min, max); + } + + /** + * 获取key和otherKey的并集并存储在destKey中 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long zUnionAndStore(K key, K otherKey, K destKey) { + return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey); + } + + /** + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long zUnionAndStore(K key, Collection otherKeys, + K destKey) { + return redisTemplate.opsForZSet() + .unionAndStore(key, otherKeys, destKey); + } + + /** + * 交集 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long zIntersectAndStore(K key, K otherKey, + K destKey) { + return redisTemplate.opsForZSet().intersectAndStore(key, otherKey, + destKey); + } + + /** + * 交集 + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long zIntersectAndStore(K key, Collection otherKeys, + K destKey) { + return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys, + destKey); + } + + /** + * @param key + * @param options + * @return + */ + public Cursor> zScan(K key, ScanOptions options) { + return redisTemplate.opsForZSet().scan(key, options); + } + + /** --------------------set相关操作-------------------------- */ + + /** + * set添加元素 + * + * @param key + * @param values + * @return + */ + public Long sAdd(K key, V... values) { + return redisTemplate.opsForSet().add(key, values); + } + + /** + * set移除元素 + * + * @param key + * @param values + * @return + */ + public Long sRemove(K key, V... values) { + return redisTemplate.opsForSet().remove(key, values); + } + + /** + * 移除并返回集合的一个随机元素 + * + * @param key + * @return + */ + public V sPop(K key) { + return redisTemplate.opsForSet().pop(key); + } + + /** + * 将元素value从一个集合移到另一个集合 + * + * @param key + * @param value + * @param destKey + * @return + */ + public Boolean sMove(K key, V value, K destKey) { + return redisTemplate.opsForSet().move(key, value, destKey); + } + + /** + * 获取集合的大小 + * + * @param key + * @return + */ + public Long sSize(K key) { + return redisTemplate.opsForSet().size(key); + } + + /** + * 判断集合是否包含value + * + * @param key + * @param value + * @return + */ + public Boolean sIsMember(K key, V value) { + return redisTemplate.opsForSet().isMember(key, value); + } + + /** + * 获取两个集合的交集 + * + * @param key + * @param otherKey + * @return + */ + public Set sIntersect(K key, K otherKey) { + return redisTemplate.opsForSet().intersect(key, otherKey); + } + + /** + * 获取key集合与多个集合的交集 + * + * @param key + * @param otherKeys + * @return + */ + public Set sIntersect(K key, Collection otherKeys) { + return redisTemplate.opsForSet().intersect(key, otherKeys); + } + + /** + * key集合与otherKey集合的交集存储到destKey集合中 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long sIntersectAndStore(K key, K otherKey, K destKey) { + return redisTemplate.opsForSet().intersectAndStore(key, otherKey, + destKey); + } + + /** + * key集合与多个集合的交集存储到destKey集合中 + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long sIntersectAndStore(K key, Collection otherKeys, + K destKey) { + return redisTemplate.opsForSet().intersectAndStore(key, otherKeys, + destKey); + } + + /** + * 获取两个集合的并集 + * + * @param key + * @param otherKeys + * @return + */ + public Set sUnion(K key, K otherKeys) { + return redisTemplate.opsForSet().union(key, otherKeys); + } + + /** + * 获取key集合与多个集合的并集 + * + * @param key + * @param otherKeys + * @return + */ + public Set sUnion(K key, Collection otherKeys) { + return redisTemplate.opsForSet().union(key, otherKeys); + } + + /** + * key集合与otherKey集合的并集存储到destKey中 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long sUnionAndStore(K key, K otherKey, K destKey) { + return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey); + } + + /** + * key集合与多个集合的并集存储到destKey中 + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long sUnionAndStore(K key, Collection otherKeys, + K destKey) { + return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey); + } + + /** + * 获取两个集合的差集 + * + * @param key + * @param otherKey + * @return + */ + public Set sDifference(K key, K otherKey) { + return redisTemplate.opsForSet().difference(key, otherKey); + } + + /** + * 获取key集合与多个集合的差集 + * + * @param key + * @param otherKeys + * @return + */ + public Set sDifference(K key, Collection otherKeys) { + return redisTemplate.opsForSet().difference(key, otherKeys); + } + + /** + * key集合与otherKey集合的差集存储到destKey中 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long sDifference(K key, K otherKey, K destKey) { + return redisTemplate.opsForSet().differenceAndStore(key, otherKey, + destKey); + } + + /** + * key集合与多个集合的差集存储到destKey中 + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long sDifference(K key, Collection otherKeys, + K destKey) { + return redisTemplate.opsForSet().differenceAndStore(key, otherKeys, + destKey); + } + + /** + * 获取集合所有元素 + * + * @param key + * @return + */ + public Set setMembers(K key) { + return redisTemplate.opsForSet().members(key); + } + + /** + * 随机获取集合中的一个元素 + * + * @param key + * @return + */ + public V sRandomMember(K key) { + return redisTemplate.opsForSet().randomMember(key); + } + + /** + * 随机获取集合中count个元素 + * + * @param key + * @param count + * @return + */ + public List sRandomMembers(K key, long count) { + return redisTemplate.opsForSet().randomMembers(key, count); + } + + /** + * 随机获取集合中count个元素并且去除重复的 + * + * @param key + * @param count + * @return + */ + public Set sDistinctRandomMembers(K key, long count) { + return redisTemplate.opsForSet().distinctRandomMembers(key, count); + } + + /** + * @param key + * @param options + * @return + */ + public Cursor sScan(K key, ScanOptions options) { + return redisTemplate.opsForSet().scan(key, options); + } + + /** ------------------------list相关操作---------------------------- */ + + /** + * 通过索引获取列表中的元素 + * + * @param key + * @param index + * @return + */ + public V lIndex(K key, long index) { + return redisTemplate.opsForList().index(key, index); + } + + /** + * 获取列表指定范围内的元素 + * + * @param key + * @param start 开始位置, 0是开始位置 + * @param end 结束位置, -1返回所有 + * @return + */ + public List lRange(K key, long start, long end) { + return redisTemplate.opsForList().range(key, start, end); + } + + /** + * 存储在list头部 + * + * @param key + * @param value + * @return + */ + public Long lLeftPush(K key, V value) { + return redisTemplate.opsForList().leftPush(key, value); + } + + /** + * @param key + * @param value + * @return + */ + public Long lLeftPushAll(K key, V... value) { + return redisTemplate.opsForList().leftPushAll(key, value); + } + + /** + * @param key + * @param value + * @return + */ + public Long lLeftPushAll(K key, Collection value) { + return redisTemplate.opsForList().leftPushAll(key, value); + } + + /** + * 当list存在的时候才加入 + * + * @param key + * @param value + * @return + */ + public Long lLeftPushIfPresent(K key, V value) { + return redisTemplate.opsForList().leftPushIfPresent(key, value); + } + + /** + * 如果pivot存在,再pivot前面添加 + * + * @param key + * @param pivot + * @param value + * @return + */ + public Long lLeftPush(K key, V pivot, V value) { + return redisTemplate.opsForList().leftPush(key, pivot, value); + } + + /** + * @param key + * @param value + * @return + */ + public Long lRightPush(K key, V value) { + return redisTemplate.opsForList().rightPush(key, value); + } + + /** + * @param key + * @param value + * @return + */ + public Long lRightPushAll(K key, V... value) { + return redisTemplate.opsForList().rightPushAll(key, value); + } + + /** + * @param key + * @param value + * @return + */ + public Long lRightPushAll(K key, Collection value) { + return redisTemplate.opsForList().rightPushAll(key, value); + } + + /** + * 为已存在的列表添加值 + * + * @param key + * @param value + * @return + */ + public Long lRightPushIfPresent(K key, V value) { + return redisTemplate.opsForList().rightPushIfPresent(key, value); + } + + /** + * 在pivot元素的右边添加值 + * + * @param key + * @param pivot + * @param value + * @return + */ + public Long lRightPush(K key, V pivot, V value) { + return redisTemplate.opsForList().rightPush(key, pivot, value); + } + + /** + * 通过索引设置列表元素的值 + * + * @param key + * @param index 位置 + * @param value + */ + public void lSet(K key, long index, V value) { + redisTemplate.opsForList().set(key, index, value); + } + + /** + * 移出并获取列表的第一个元素 + * + * @param key + * @return 删除的元素 + */ + public V lLeftPop(K key) { + return redisTemplate.opsForList().leftPop(key); + } + + /** + * 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 + * + * @param key + * @param timeout 等待时间 + * @param unit 时间单位 + * @return + */ + public V lBLeftPop(K key, long timeout, TimeUnit unit) { + return redisTemplate.opsForList().leftPop(key, timeout, unit); + } + + /** + * 移除并获取列表最后一个元素 + * + * @param key + * @return 删除的元素 + */ + public V lRightPop(K key) { + return redisTemplate.opsForList().rightPop(key); + } + + /** + * 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 + * + * @param key + * @param timeout 等待时间 + * @param unit 时间单位 + * @return + */ + public V lBRightPop(K key, long timeout, TimeUnit unit) { + return redisTemplate.opsForList().rightPop(key, timeout, unit); + } + + /** + * 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 + * + * @param sourceKey + * @param destinationKey + * @return + */ + public V lRightPopAndLeftPush(K sourceKey, K destinationKey) { + return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, + destinationKey); + } + + /** + * 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 + * + * @param sourceKey + * @param destinationKey + * @param timeout + * @param unit + * @return + */ + public V lBRightPopAndLeftPush(K sourceKey, K destinationKey, + long timeout, TimeUnit unit) { + return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, + destinationKey, timeout, unit); + } + + /** + * 删除集合中值等于value得元素 + * + * @param key + * @param index index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素; + * index<0, 从尾部开始删除第一个值等于value的元素; + * @param value + * @return + */ + public Long lRemove(K key, long index, V value) { + return redisTemplate.opsForList().remove(key, index, value); + } + + /** + * 裁剪list + * + * @param key + * @param start + * @param end + */ + public void lTrim(K key, long start, long end) { + redisTemplate.opsForList().trim(key, start, end); + } + + /** + * 获取列表长度 + * + * @param key + * @return + */ + public Long lLen(K key) { + return redisTemplate.opsForList().size(key); + } +} diff --git a/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/AbstractLockExecutor.java b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/AbstractLockExecutor.java new file mode 100644 index 0000000..4687e2e --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/AbstractLockExecutor.java @@ -0,0 +1,9 @@ +package cn.zyjblogs.starter.redis.utils.lock; + +public abstract class AbstractLockExecutor implements LockExecutor { + + protected T obtainLockInstance(boolean locked, T lockInstance) { + return locked ? lockInstance : null; + } + +} \ No newline at end of file diff --git a/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/DefaultLockKeyBuilder.java b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/DefaultLockKeyBuilder.java new file mode 100644 index 0000000..7e88f6e --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/DefaultLockKeyBuilder.java @@ -0,0 +1,15 @@ +package cn.zyjblogs.starter.redis.utils.lock; + +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.util.Collection; + +@Component +public class DefaultLockKeyBuilder implements LockKeyBuilder { + + @Override + public String buildKey(Collection definitionKeys) { + return StringUtils.collectionToDelimitedString(definitionKeys, ".", "", ""); + } +} diff --git a/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/LockExecutor.java b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/LockExecutor.java new file mode 100644 index 0000000..b3e0c04 --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/LockExecutor.java @@ -0,0 +1,40 @@ +package cn.zyjblogs.starter.redis.utils.lock; + +/** + * Copyright (C), 2021, 北京同创永益科技发展有限公司 + * + * @author zhuyijun + * @version 3.0.0 + * @description 分布式锁核心处理器 + * @date 2022/5/23 10:26 + */ +public interface LockExecutor { + + /** + * 加锁 + * + * @param lockKey 锁标识 + * @param lockValue 锁值 + * @param expire 锁有效时间 + * @param acquireTimeout 获取锁超时时间 + * @return 锁信息 + */ + T acquire(String lockKey, String lockValue, long expire, long acquireTimeout); + + /** + * 解锁 + * + *
+     * 为何解锁需要校验lockValue
+     * 客户端A加锁,一段时间之后客户端A解锁,在执行releaseLock之前,锁突然过期了。
+     * 此时客户端B尝试加锁成功,然后客户端A再执行releaseLock方法,则将客户端B的锁给解除了。
+     * 
+ * + * @param key 加锁key + * @param value 加锁value + * @param lockInstance 锁实例 + * @return 是否释放成功 + */ + boolean releaseLock(String key, String value, T lockInstance); + +} \ No newline at end of file diff --git a/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/LockKeyBuilder.java b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/LockKeyBuilder.java new file mode 100644 index 0000000..ee8acae --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/LockKeyBuilder.java @@ -0,0 +1,14 @@ +package cn.zyjblogs.starter.redis.utils.lock; + +import java.util.Collection; + +public interface LockKeyBuilder { + + /** + * 构建key + * + * @param definitionKeys 定义 + * @return key + */ + String buildKey(Collection definitionKeys); +} diff --git a/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/RedisLock.java b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/RedisLock.java new file mode 100644 index 0000000..ab9816d --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/RedisLock.java @@ -0,0 +1,66 @@ +package cn.zyjblogs.starter.redis.utils.lock; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import org.springframework.util.StringUtils; + +/** + * Copyright (C), 2021, 北京同创永益科技发展有限公司 + * + * @author zhuyijun + * @version 3.0.0 + * @description + * @date 2022/5/23 11:01 + */ +@Data +@AllArgsConstructor +@Builder +public class RedisLock { + /** + * 锁名称 + */ + private String lockKey; + + /** + * 锁值 + */ + private String lockValue; + + /** + * 过期时间 + */ + private Long expire; + + /** + * 获取锁超时时间 + */ + private Long acquireTimeout; + + /** + * 获取锁次数 + */ + private int acquireCount; + + /** + * 锁实例 + */ + private Object lockInstance; + + /** + * 锁执行器 + */ + private LockExecutor lockExecutor; + + /*** + * 解锁 + * @author zhuyijun + * @Description + * @date 15:10 + */ + public void unLock() { + if (lockExecutor != null && lockInstance != null && !StringUtils.isEmpty(lockKey)) { + lockExecutor.releaseLock(lockKey, lockValue, lockInstance); + } + } +} diff --git a/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/RedisLockTemplate.java b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/RedisLockTemplate.java new file mode 100644 index 0000000..a53ad8b --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/RedisLockTemplate.java @@ -0,0 +1,122 @@ +package cn.zyjblogs.starter.redis.utils.lock; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * Copyright (C), 2021, 北京同创永益科技发展有限公司 + * + * @author zhuyijun + * @version 3.0.0 + * @description + * @date 2022/5/23 11:02 + */ +@Slf4j +@Component +public class RedisLockTemplate { + + @Resource + private LockExecutor RedisTemplateLockExecutor; + /** + * 默认失效时间 + */ + private Long defaultExpire = 30000L; + private Long defaultAcquireTimeout = 3000L; + /** + * 获取锁失败时重试时间间隔 单位:毫秒 + */ + private Long retryInterval = 100L; + + @Value("${zyjblogs.redis.prefix:lock}") + private String prefix; + /** + * 获取锁(不推荐) + * + * @param key + * @param expire + * @return + */ + public RedisLock lock(String key, long expire) { + return lock(key, expire, 0, null); + } + /** + * 加锁方法 + * + * @param key 锁key 同一个key只能被一个客户端持有 + * @param expire 过期时间(ms) 防止死锁 + * @param acquireTimeout 尝试获取锁超时时间(ms) + * @return 加锁成功返回锁信息 失败返回null + */ + public RedisLock tryLock(String key, long expire, long acquireTimeout) { + return lock(key, expire, acquireTimeout, null); + } + + /** + * 加锁方法 + * + * @param key 锁key 同一个key只能被一个客户端持有 + * @param expire 过期时间(ms) 防止死锁 + * @param acquireTimeout 尝试获取锁超时时间(ms) + * @param executor 执行器 + * @return 加锁成功返回锁信息 失败返回null + */ + public RedisLock lock(String key, long expire, long acquireTimeout, LockExecutor executor) { + key = prefix + key; + LockExecutor lockExecutor = obtainExecutor(executor); + expire = expire < 0 ? defaultExpire : expire; + long currentAcquireTimeout = acquireTimeout < 0 ? defaultAcquireTimeout : acquireTimeout; + int acquireCount = 0; + String value = UUID.randomUUID().toString(); + long start = System.currentTimeMillis(); + try { + do { + acquireCount++; + Object lockInstance = lockExecutor.acquire(key, value, expire, acquireTimeout); + if (null != lockInstance) { + log.debug("加锁成功 key:{}",key); + return RedisLock.builder() + .lockKey(key) + .lockValue(value) + .expire(expire) + .acquireTimeout(currentAcquireTimeout) + .acquireCount(acquireCount) + .lockInstance(lockInstance) + .lockExecutor(lockExecutor) + .build(); + } + TimeUnit.MILLISECONDS.sleep(retryInterval); + } while (System.currentTimeMillis() - start < currentAcquireTimeout); + } catch (InterruptedException e) { + log.error("lock error", e); + throw new RuntimeException("加锁失败"); + } + return null; + } + + protected LockExecutor obtainExecutor(LockExecutor lockExecutor) { + if (null == lockExecutor) { + return RedisTemplateLockExecutor; + } + return lockExecutor; + } + + /*** + * 解锁 + * @param redisLock + * @author zhuyijun + * @Description + * @date 11:29 + */ + public boolean unLock(RedisLock redisLock) { + if (null == redisLock) { + return false; + } + return redisLock.getLockExecutor().releaseLock(redisLock.getLockKey(), redisLock.getLockValue(), + redisLock.getLockInstance()); + } +} diff --git a/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/RedisTemplateLockExecutor.java b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/RedisTemplateLockExecutor.java new file mode 100644 index 0000000..1fa0aed --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/java/cn/zyjblogs/starter/redis/utils/lock/RedisTemplateLockExecutor.java @@ -0,0 +1,63 @@ +package cn.zyjblogs.starter.redis.utils.lock; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Collections; + +@Slf4j +@Component +public class RedisTemplateLockExecutor extends AbstractLockExecutor { + + private static final RedisScript SCRIPT_LOCK = new DefaultRedisScript<>("return redis.call('set',KEYS[1]," + + "ARGV[1],'NX','PX',ARGV[2])", String.class); + private static final RedisScript SCRIPT_UNLOCK = new DefaultRedisScript<>("if redis.call('get',KEYS[1]) " + + "== ARGV[1] then return tostring(redis.call('del', KEYS[1])==1) else return 'false' end", String.class); + private static final String LOCK_SUCCESS = "OK"; + + @Resource + private RedisTemplate redisTemplate; + + /*** + * 枷锁 + * @param lockKey + * @param lockValue + * @param expire + * @param acquireTimeout + * @author zhuyijun + * @Description + * @date 10:26 + */ + @Override + public String acquire(String lockKey, String lockValue, long expire, long acquireTimeout) { + String lock = redisTemplate.execute(SCRIPT_LOCK, + redisTemplate.getStringSerializer(), + redisTemplate.getStringSerializer(), + Collections.singletonList(lockKey), + lockValue, String.valueOf(expire)); + final boolean locked = LOCK_SUCCESS.equals(lock); + return obtainLockInstance(locked, lock); + } + + /*** + * 解锁 + * @param key + * @param value + * @param lockInstance + * @author zhuyijun + * @Description + * @date 10:26 + */ + @Override + public boolean releaseLock(String key, String value, String lockInstance) { + String releaseResult = redisTemplate.execute(SCRIPT_UNLOCK, + redisTemplate.getStringSerializer(), + redisTemplate.getStringSerializer(), + Collections.singletonList(key), value); + return Boolean.parseBoolean(releaseResult); + } +} \ No newline at end of file diff --git a/zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/MANIFEST.MF b/zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..966deb6 --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +Archiver-Version: Plexus Archiver +Created-By: Apache Maven 3.6.3 +Built-By: mitom +Build-Jdk: 11 diff --git a/zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/maven.cn.zyjblogs.starter.zyjblogs-web-spring-boot-starter/pom.properties b/zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/maven.cn.zyjblogs.starter.zyjblogs-web-spring-boot-starter/pom.properties new file mode 100644 index 0000000..dec09fc --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/maven.cn.zyjblogs.starter.zyjblogs-web-spring-boot-starter/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Mon Jul 25 15:29:06 CST 2022 +groupId=cn.zyjblogs.starter +artifactId=zyjblogs-redis-spring-boot-starter +version=1.0-SNAPSHOT diff --git a/zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/maven.cn.zyjblogs.starter.zyjblogs-web-spring-boot-starter/pom.xml b/zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/maven.cn.zyjblogs.starter.zyjblogs-web-spring-boot-starter/pom.xml new file mode 100644 index 0000000..537010f --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/maven.cn.zyjblogs.starter.zyjblogs-web-spring-boot-starter/pom.xml @@ -0,0 +1,77 @@ + + + + zyjblogs-parent + cn.zyjblogs + 1.0-SNAPSHOT + + 4.0.0 + + cn.zyjblogs.starter + zyjblogs-redis-spring-boot-starter + 1.0-SNAPSHOT + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-autoconfigure + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + com.fasterxml.jackson.core + jackson-databind + + + org.projectlombok + lombok + + + + com.fasterxml.jackson.core + jackson-annotations + + + + + com.alibaba + fastjson + + + + org.apache.commons + commons-pool2 + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${compler.maven.plugin.version} + + true + + + + + \ No newline at end of file diff --git a/zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/spring.factories b/zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..56fa7c4 --- /dev/null +++ b/zyjblogs-redis-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +## Auto Configure +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + cn.zyjblogs.starter.redis.config.RedisConfig diff --git a/zyjblogs-web-spring-boot-starter/pom.xml b/zyjblogs-web-spring-boot-starter/pom.xml index 1bd4807..338e470 100644 --- a/zyjblogs-web-spring-boot-starter/pom.xml +++ b/zyjblogs-web-spring-boot-starter/pom.xml @@ -6,6 +6,7 @@ zyjblogs-parent cn.zyjblogs 1.0-SNAPSHOT + 4.0.0 @@ -18,11 +19,6 @@ 11 - - - org.springframework.boot - spring-boot-starter-web -