修改小程序登录逻辑

This commit is contained in:
sh
2025-12-23 17:58:44 +08:00
parent 497e4f5001
commit 4ff76a3100
7 changed files with 395 additions and 40 deletions

View File

@@ -0,0 +1,211 @@
package com.ruoyi.common.core.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* JDK 1.8 兼容版分布式锁工具类(基于 Redis
*/
@Component
public class DistributedLockUtil {
@Autowired
private RedisCache redisCache;
// 锁默认配置
private static final long DEFAULT_EXPIRE_SECONDS = 30;
private static final long DEFAULT_ACQUIRE_TIMEOUT_SECONDS = 5;
private static final long RENEW_INTERVAL_SECONDS = DEFAULT_EXPIRE_SECONDS / 3;
// 续期线程池
private final ScheduledExecutorService renewExecutor = Executors.newScheduledThreadPool(
Runtime.getRuntime().availableProcessors(),
new ThreadFactory() {
private final AtomicBoolean init = new AtomicBoolean(false);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "distributed-lock-renewer");
thread.setDaemon(true);
if (init.compareAndSet(false, true)) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
renewExecutor.shutdown();
}
}));
}
return thread;
}
}
);
// 释放锁 Lua 脚本(原子操作)
private static final String RELEASE_LOCK_LUA_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
private final DefaultRedisScript<Long> releaseScript = new DefaultRedisScript<>();
// 初始化 Lua 脚本
{
releaseScript.setScriptText(RELEASE_LOCK_LUA_SCRIPT);
releaseScript.setResultType(Long.class);
}
/**
* 获取锁(带自动续期)
*/
public String acquireLockWithRenewal(String lockKey) {
return acquireLockWithRenewal(lockKey, DEFAULT_EXPIRE_SECONDS, DEFAULT_ACQUIRE_TIMEOUT_SECONDS);
}
/**
* 自定义参数获取锁
*/
public String acquireLockWithRenewal(String lockKey, long expireSeconds, long acquireTimeoutSeconds) {
String identifier = UUID.randomUUID().toString();
long endTime = System.currentTimeMillis() + acquireTimeoutSeconds * 1000;
while (System.currentTimeMillis() < endTime) {
boolean locked = tryLockOnce(lockKey, identifier, expireSeconds);
if (locked) {
startRenewal(lockKey, identifier, expireSeconds);
return identifier;
}
// 指数退避重试
try {
long sleepMs = calculateBackoffSleep(System.currentTimeMillis() - (endTime - acquireTimeoutSeconds * 1000));
Thread.sleep(sleepMs);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
}
return null;
}
/**
* 原子释放锁
*/
public boolean releaseLockSafely(String lockKey, String identifier) {
if (identifier == null) {
return false;
}
Long result = (Long) redisCache.redisTemplate.execute(
releaseScript,
Collections.singletonList(lockKey),
identifier
);
return result != null && result > 0;
}
/**
* 尝试获取一次锁
*/
private boolean tryLockOnce(String lockKey, String identifier, long expireSeconds) {
try {
if (!redisCache.hasKey(lockKey)) {
redisCache.setCacheObject(lockKey, identifier, (int) expireSeconds, TimeUnit.SECONDS);
String storedId = redisCache.getCacheObject(lockKey);
return identifier.equals(storedId);
}
return false;
} catch (Exception e) {
return false;
}
}
/**
* 启动锁自动续期
*/
private void startRenewal(final String lockKey, final String identifier, final long expireSeconds) {
final AtomicReference<ScheduledFuture<?>> futureRef = new AtomicReference<>();
Runnable renewalTask = new Runnable() {
@Override
public void run() {
try {
String storedId = redisCache.getCacheObject(lockKey);
if (identifier.equals(storedId)) {
redisCache.expire(lockKey, expireSeconds);
} else {
cancelFuture();
}
} catch (Exception e) {
cancelFuture();
}
}
private void cancelFuture() {
ScheduledFuture<?> future = futureRef.get();
if (future != null && !future.isCancelled()) {
future.cancel(false);
}
}
};
ScheduledFuture<?> future = renewExecutor.scheduleAtFixedRate(
renewalTask,
RENEW_INTERVAL_SECONDS,
RENEW_INTERVAL_SECONDS,
TimeUnit.SECONDS
);
futureRef.set(future);
}
/**
* 指数退避算法(计算重试间隔)
*/
private long calculateBackoffSleep(long elapsedMs) {
int retryCount = (int) (elapsedMs / 100);
long sleepMs = 100L * (1 << Math.min(retryCount, 10));
return Math.min(sleepMs, 1000);
}
/**
* 自动释放锁工具(支持 try-with-resources
*/
public static class AutoReleaseLock implements AutoCloseable {
private final DistributedLockUtil lockUtil;
private final String lockKey;
private final String identifier;
public AutoReleaseLock(DistributedLockUtil lockUtil, String lockKey, String identifier) {
this.lockUtil = lockUtil;
this.lockKey = lockKey;
this.identifier = identifier;
}
public boolean isLocked() {
return identifier != null;
}
@Override
public void close() {
if (isLocked()) {
lockUtil.releaseLockSafely(lockKey, identifier);
}
}
}
/**
* 简化锁使用
*/
public AutoReleaseLock tryLock(String lockKey) {
String identifier = acquireLockWithRenewal(lockKey);
return new AutoReleaseLock(this, lockKey, identifier);
}
}