From 2c31bf6d53b53ab703d55f5ec70c417f2b82b807 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 8 Oct 2025 02:07:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9Eheritage-plugins?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E6=8F=92=E4=BB=B6=EF=BC=88=E7=AC=AC2?= =?UTF-8?q?=E9=83=A8=E5=88=86=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增heritage-limit:接口限流插件,基于Redis实现 - 新增heritage-repect:防重复提交插件,防止表单重复提交 - 新增heritage-sa-token:Sa-Token认证插件,提供登录认证和权限验证 --- heritage-plugins/heritage-limit/pom.xml | 32 +++ .../heritage/limit/anno/EnableCoderLimit.java | 15 ++ .../limit/aspect/CoderLimitAspect.java | 83 +++++++ .../limit/config/CoderRedisLimitUtil.java | 54 +++++ heritage-plugins/heritage-repect/pom.xml | 30 +++ .../repect/anno/EnableCoderRepeatSubmit.java | 16 ++ .../heritage/repect/aspect/RedisService.java | 206 ++++++++++++++++++ .../repect/aspect/RepeatSubmitAspect.java | 91 ++++++++ heritage-plugins/heritage-sa-token/pom.xml | 58 +++++ .../satoken/anno/EnableCoderSaToken.java | 16 ++ .../satoken/config/CoderSaTokenFilter.java | 50 +++++ .../config/CoderSaTokenInterceptor.java | 68 ++++++ .../satoken/config/CoderSaTokenListener.java | 159 ++++++++++++++ .../config/CoderSaTokenPasswordUtil.java | 39 ++++ .../config/CoderSaTokenStpInterfaceImpl.java | 74 +++++++ .../satoken/config/SaTokenConfigure.java | 39 ++++ .../service/loginlog/SaLoginLogService.java | 17 ++ .../loginlog/SaLoginLogServiceImpl.java | 75 +++++++ .../satoken/service/menu/SaMenuService.java | 20 ++ .../service/menu/SaMenuServiceImpl.java | 32 +++ .../satoken/service/role/SaRoleService.java | 19 ++ .../service/role/SaRoleServiceImpl.java | 32 +++ 22 files changed, 1225 insertions(+) create mode 100644 heritage-plugins/heritage-limit/pom.xml create mode 100755 heritage-plugins/heritage-limit/src/main/java/org/leocoder/heritage/limit/anno/EnableCoderLimit.java create mode 100755 heritage-plugins/heritage-limit/src/main/java/org/leocoder/heritage/limit/aspect/CoderLimitAspect.java create mode 100755 heritage-plugins/heritage-limit/src/main/java/org/leocoder/heritage/limit/config/CoderRedisLimitUtil.java create mode 100644 heritage-plugins/heritage-repect/pom.xml create mode 100755 heritage-plugins/heritage-repect/src/main/java/org/leocoder/heritage/repect/anno/EnableCoderRepeatSubmit.java create mode 100755 heritage-plugins/heritage-repect/src/main/java/org/leocoder/heritage/repect/aspect/RedisService.java create mode 100755 heritage-plugins/heritage-repect/src/main/java/org/leocoder/heritage/repect/aspect/RepeatSubmitAspect.java create mode 100644 heritage-plugins/heritage-sa-token/pom.xml create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/anno/EnableCoderSaToken.java create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenFilter.java create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenInterceptor.java create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenListener.java create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenPasswordUtil.java create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenStpInterfaceImpl.java create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/SaTokenConfigure.java create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/loginlog/SaLoginLogService.java create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/loginlog/SaLoginLogServiceImpl.java create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/menu/SaMenuService.java create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/menu/SaMenuServiceImpl.java create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/role/SaRoleService.java create mode 100755 heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/role/SaRoleServiceImpl.java diff --git a/heritage-plugins/heritage-limit/pom.xml b/heritage-plugins/heritage-limit/pom.xml new file mode 100644 index 0000000..8c917e2 --- /dev/null +++ b/heritage-plugins/heritage-limit/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + org.leocoder.heritage + heritage-plugins + ${revision} + + + + heritage-limit + heritage-limit + 限流插件 + + + + + org.leocoder.heritage + heritage-common + ${revision} + + + + org.springframework.boot + spring-boot-starter-aop + + + + + \ No newline at end of file diff --git a/heritage-plugins/heritage-limit/src/main/java/org/leocoder/heritage/limit/anno/EnableCoderLimit.java b/heritage-plugins/heritage-limit/src/main/java/org/leocoder/heritage/limit/anno/EnableCoderLimit.java new file mode 100755 index 0000000..112ab9d --- /dev/null +++ b/heritage-plugins/heritage-limit/src/main/java/org/leocoder/heritage/limit/anno/EnableCoderLimit.java @@ -0,0 +1,15 @@ +package org.leocoder.heritage.limit.anno; + +import org.leocoder.heritage.limit.config.CoderRedisLimitUtil; +import org.springframework.context.annotation.Import; + +import java.lang.annotation.*; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import({CoderRedisLimitUtil.class}) +public @interface EnableCoderLimit { + +} diff --git a/heritage-plugins/heritage-limit/src/main/java/org/leocoder/heritage/limit/aspect/CoderLimitAspect.java b/heritage-plugins/heritage-limit/src/main/java/org/leocoder/heritage/limit/aspect/CoderLimitAspect.java new file mode 100755 index 0000000..5ad053d --- /dev/null +++ b/heritage-plugins/heritage-limit/src/main/java/org/leocoder/heritage/limit/aspect/CoderLimitAspect.java @@ -0,0 +1,83 @@ +package org.leocoder.heritage.limit.aspect; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.leocoder.heritage.common.anno.CoderLimit; +import org.leocoder.heritage.common.enmus.limit.LimitType; +import org.leocoder.heritage.common.exception.coder.ParamsException; +import org.leocoder.heritage.common.utils.ip.IpUtil; +import org.leocoder.heritage.limit.config.CoderRedisLimitUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.lang.reflect.Method; + +/** + * @author Leocoder + * @description [CoderLimitAspect限流处理] + */ +@Slf4j +@Aspect +@Order(2) +@Component +public class CoderLimitAspect { + + @Autowired + private CoderRedisLimitUtil CoderRedisLimitUtil; + + /** + * @description [前置通知,判断是否超出限流次数】 + */ + @Before("@annotation(limit)") + public void doBefore(JoinPoint point, CoderLimit limit) { + try { + // log.info("限流开始进入 =>"); + // 拼接key + String key = getCombineKey(limit, point); + // 判断是否超出限流次数 + if (!CoderRedisLimitUtil.limit(key, limit.count(), limit.time())) { + throw new ParamsException(limit.message()); + } + } catch (ParamsException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("接口限流异常,请稍候再试"); + } + } + + /** + * @description [根据限流类型拼接key】 + */ + public String getCombineKey(CoderLimit limit, JoinPoint point) { + // 获取服务请求的对象 + ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = requestAttributes.getRequest(); + StringBuilder sb = new StringBuilder(limit.prefix()); + // 按照IP限流 + if (limit.type() == LimitType.IP) { + String ipAddr = IpUtil.getIpAddr(request); + // 检查字符串中是否包含逗号,这种情况设置waf会出现多个IP,第一个是真实IP,后面的都是阿里云IP + int commaIndex = ipAddr.indexOf(','); + if (commaIndex > -1) { + // 如果有逗号,取逗号前的部分 + ipAddr = ipAddr.substring(0, commaIndex); + } + sb.append(ipAddr).append(":"); + // log.info("限流IP:{}", ipAddr); + } + // 拼接类名和方法名 + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class targetClass = method.getDeclaringClass(); + sb.append(targetClass.getName()).append("-").append(method.getName()); + return sb.toString(); + } + +} diff --git a/heritage-plugins/heritage-limit/src/main/java/org/leocoder/heritage/limit/config/CoderRedisLimitUtil.java b/heritage-plugins/heritage-limit/src/main/java/org/leocoder/heritage/limit/config/CoderRedisLimitUtil.java new file mode 100755 index 0000000..7fd411e --- /dev/null +++ b/heritage-plugins/heritage-limit/src/main/java/org/leocoder/heritage/limit/config/CoderRedisLimitUtil.java @@ -0,0 +1,54 @@ +package org.leocoder.heritage.limit.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Leocoder + * @description [RedisLimitUtil接口限流] + */ +@Slf4j +@Component +public class CoderRedisLimitUtil { + + @Autowired + private StringRedisTemplate redisTemplate; + + /** + * @param key 键 + * @param count 限流次数 + * @param times 限流时间 + * @description [限流] + * 通过 Lua 脚本,根据 Redis 中缓存的键值判断限流时间(也是 key 的过期时间)内,访问次数是否超出了限流次数,没超出则访问次数 +1,返回 true,超出了则返回 false。 + */ + public boolean limit(String key, int count, int times) { + try { + String script = "local lockKey = KEYS[1]\n" + + "local lockCount = KEYS[2]\n" + + "local lockExpire = KEYS[3]\n" + + "local currentCount = tonumber(redis.call('get', lockKey) or \"0\")\n" + + "if currentCount < tonumber(lockCount)\n" + + "then\n" + + " redis.call(\"INCRBY\", lockKey, \"1\")\n" + + " redis.call(\"expire\", lockKey, lockExpire)\n" + + " return true\n" + + "else\n" + + " return false\n" + + "end"; + RedisScript redisScript = new DefaultRedisScript<>(script, Boolean.class); + List keys = Arrays.asList(key, String.valueOf(count), String.valueOf(times)); + return Boolean.TRUE.equals(redisTemplate.execute(redisScript, keys)); + } catch (Exception e) { + log.error("限流脚本执行失败:{}", e.getMessage()); + } + return false; + } + +} diff --git a/heritage-plugins/heritage-repect/pom.xml b/heritage-plugins/heritage-repect/pom.xml new file mode 100644 index 0000000..53a73c4 --- /dev/null +++ b/heritage-plugins/heritage-repect/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + org.leocoder.heritage + heritage-plugins + ${revision} + + + + heritage-repect + heritage-repect + 防重复提交插件 + + + + + org.leocoder.heritage + heritage-common + ${revision} + + + + org.springframework.boot + spring-boot-starter-aop + + + \ No newline at end of file diff --git a/heritage-plugins/heritage-repect/src/main/java/org/leocoder/heritage/repect/anno/EnableCoderRepeatSubmit.java b/heritage-plugins/heritage-repect/src/main/java/org/leocoder/heritage/repect/anno/EnableCoderRepeatSubmit.java new file mode 100755 index 0000000..e091269 --- /dev/null +++ b/heritage-plugins/heritage-repect/src/main/java/org/leocoder/heritage/repect/anno/EnableCoderRepeatSubmit.java @@ -0,0 +1,16 @@ +package org.leocoder.heritage.repect.anno; + +import org.leocoder.heritage.repect.aspect.RedisService; +import org.leocoder.heritage.repect.aspect.RepeatSubmitAspect; +import org.springframework.context.annotation.Import; + +import java.lang.annotation.*; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import({RepeatSubmitAspect.class, RedisService.class}) +public @interface EnableCoderRepeatSubmit { + +} diff --git a/heritage-plugins/heritage-repect/src/main/java/org/leocoder/heritage/repect/aspect/RedisService.java b/heritage-plugins/heritage-repect/src/main/java/org/leocoder/heritage/repect/aspect/RedisService.java new file mode 100755 index 0000000..6beead1 --- /dev/null +++ b/heritage-plugins/heritage-repect/src/main/java/org/leocoder/heritage/repect/aspect/RedisService.java @@ -0,0 +1,206 @@ +package org.leocoder.heritage.repect.aspect; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.*; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @author Leocoder + * @description [RedisService] + */ +@Component +@RequiredArgsConstructor +public class RedisService { + + public final RedisTemplate redisTemplate; + + /** + * @param key 缓存的键值 + * @param value 缓存的值 + * @return 缓存的对象 + * @description [缓存基本的对象,Integer、String、实体类等] + */ + public ValueOperations setCacheObject(String key, T value) { + ValueOperations operation = redisTemplate.opsForValue(); + operation.set(key, value); + return operation; + } + + /** + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + * @return 缓存的对象 + * @description [缓存基本的对象,Integer、String、实体类等] + */ + public ValueOperations setCacheObject(String key, T value, Integer timeout, TimeUnit timeUnit) { + ValueOperations operation = redisTemplate.opsForValue(); + operation.set(key, value, timeout, timeUnit); + return operation; + } + + /** + * @param key 缓存键值 + * @return 缓存键值对应的数据 + * @description [获得缓存的基本对象] + */ + public T getCacheObject(String key) { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * @description [删除单个对象] + */ + public void deleteObject(String key) { + redisTemplate.delete(key); + } + + /** + * @description [删除集合对象] + */ + public void deleteObject(Collection collection) { + redisTemplate.delete(collection); + } + + /** + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + * @description [缓存List数据] + */ + public ListOperations setCacheList(String key, List dataList) { + ListOperations listOperation = redisTemplate.opsForList(); + if (null != dataList) { + int size = dataList.size(); + for (int i = 0; i < size; i++) { + listOperation.leftPush(key, dataList.get(i)); + } + } + return listOperation; + } + + /** + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + * @description [获得缓存的list对象] + */ + public List getCacheList(String key) { + List dataList = new ArrayList<>(); + ListOperations listOperation = redisTemplate.opsForList(); + Long size = listOperation.size(key); + + for (int i = 0; i < size; i++) { + dataList.add(listOperation.index(key, i)); + } + return dataList; + } + + /** + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + * @description [缓存Set] + */ + public BoundSetOperations setCacheSet(String key, Set dataSet) { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * @description [获得缓存的set] + */ + public Set getCacheSet(String key) { + Set dataSet = new HashSet<>(); + BoundSetOperations operation = redisTemplate.boundSetOps(key); + dataSet = operation.members(); + return dataSet; + } + + /** + * @description [缓存Map] + */ + public HashOperations setCacheMap(String key, Map dataMap) { + HashOperations hashOperations = redisTemplate.opsForHash(); + if (null != dataMap) { + for (Map.Entry entry : dataMap.entrySet()) { + hashOperations.put(key, entry.getKey(), entry.getValue()); + } + } + return hashOperations; + } + + /** + * @description [获得缓存的Map] + */ + public Map getCacheMap(String key) { + Map map = redisTemplate.opsForHash().entries(key); + return map; + } + + /** + * @param pattern 字符串前缀 + * @return 对象列表 + * @description [获得缓存的基本对象列表] + */ + public Collection keys(String pattern) { + return redisTemplate.keys(pattern); + } + + /** + * @description [此key是否存在] + */ + public boolean haskey(String key) { + return redisTemplate.hasKey(key); + } + + /** + * @description [获取key的过期时间] + */ + public Long getExpire(String key) { + return redisTemplate.getExpire(key); + } + + + public ValueOperations setBillObject(String key, List> value) { + ValueOperations operation = redisTemplate.opsForValue(); + operation.set(key, (T) value); + return operation; + } + + /** + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + * @return 缓存的对象 + * @description [缓存list>] + */ + public ValueOperations setBillObject(String key, List> value, Integer timeout, TimeUnit timeUnit) { + ValueOperations operation = redisTemplate.opsForValue(); + operation.set(key, (T) value, timeout, timeUnit); + return operation; + } + + /** + * @description [缓存Map] + */ + public HashOperations setCKdBillMap(String key, Map dataMap) { + HashOperations hashOperations = redisTemplate.opsForHash(); + if (null != dataMap) { + for (Map.Entry entry : dataMap.entrySet()) { + hashOperations.put(key, entry.getKey(), entry.getValue()); + } + } + return hashOperations; + } + +} diff --git a/heritage-plugins/heritage-repect/src/main/java/org/leocoder/heritage/repect/aspect/RepeatSubmitAspect.java b/heritage-plugins/heritage-repect/src/main/java/org/leocoder/heritage/repect/aspect/RepeatSubmitAspect.java new file mode 100755 index 0000000..8b1d9ed --- /dev/null +++ b/heritage-plugins/heritage-repect/src/main/java/org/leocoder/heritage/repect/aspect/RepeatSubmitAspect.java @@ -0,0 +1,91 @@ +package org.leocoder.heritage.repect.aspect; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.leocoder.heritage.common.anno.CoderRepeatSubmit; +import org.leocoder.heritage.common.constants.CoderCacheConstants; +import org.leocoder.heritage.common.exception.RepeatSubmitException; +import org.leocoder.heritage.common.utils.ip.IpUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.concurrent.TimeUnit; + +/** + * @author Leocoder + * @description [NoRepeatSubmitAop] + */ +@Aspect +@Component +@Order(1) +@Slf4j +public class RepeatSubmitAspect { + + @Autowired + private RedisService redisService; + + @Value("${sa-token.token-name}") + private String tokenName; + + @Value("${sa-token.token-prefix}") + private String tokenPrefix; + + @Around(value = "@annotation(repeatSubmit)") + public Object around(ProceedingJoinPoint joinPoint, CoderRepeatSubmit repeatSubmit) throws Throwable { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + assert attributes != null; + // 请求地址 + HttpServletRequest request = attributes.getRequest(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + // System.out.println("方法名称:"+ signature.getMethod().getName()); + // System.out.println("方法类型:"+ signature.getReturnType()); + // System.out.println("参数名称:"+ Arrays.toString(signature.getParameterNames())); + // System.out.println("参数类型:"+ Arrays.toString(signature.getParameterTypes())); + // 获得客户端的IP地址 + String userIp = IpUtil.getIpAddr(request); + // 检查字符串中是否包含逗号,这种情况设置waf会出现多个IP,第一个是真实IP,后面的都是阿里云IP + int commaIndex = userIp.indexOf(','); + if (commaIndex > -1) { + // 如果有逗号,取逗号前的部分 + userIp = userIp.substring(0, commaIndex); + } + // 针对某个人的话,就是用token,如果没有token就使用客户端IP进行辨别 + String authorization = request.getHeader(tokenName); + String header = null; + if (StringUtils.isNotBlank(authorization)) { + header = authorization.replace(" ", "").replace(tokenPrefix, ""); + } + // 定义redis的key + String key = null; + if (StringUtils.isNotBlank(header)) { + // 这里是唯一标识,根据情况而定,里面添加用户ID或者IP地址最好,否则同一个接口一秒只能使用一次 + key = repeatSubmit.prefix() + userIp + ":[" + signature.getMethod().getName() + "-" + request.getServletPath() + "-" + header + "]"; + } else { + // 这里是唯一标识,根据情况而定,里面添加用户ID或者IP地址最好,否则同一个接口一秒只能使用一次 + key = repeatSubmit.prefix() + userIp + ":[" + signature.getMethod().getName() + "-" + request.getServletPath() + "]"; + } + // log.info("重复提交操作电脑IP:{}", userIp); + // log.info("重复提交redis-key:{}", key); + // 如果缓存中有这个url视为重复提交 + if (!redisService.haskey(key)) { + // 通过,执行下一步 + Object o = joinPoint.proceed(); + // 然后存入redis并且设置1s倒计时 + redisService.setCacheObject(key, CoderCacheConstants.REPEAT_SUBMIT_KEY, repeatSubmit.value(), TimeUnit.MILLISECONDS); + // 返回结果 + return o; + } else { + throw new RepeatSubmitException(500, repeatSubmit.message()); + } + } + +} diff --git a/heritage-plugins/heritage-sa-token/pom.xml b/heritage-plugins/heritage-sa-token/pom.xml new file mode 100644 index 0000000..8a51280 --- /dev/null +++ b/heritage-plugins/heritage-sa-token/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + + org.leocoder.heritage + heritage-plugins + ${revision} + + + + heritage-sa-token + heritage-sa-token + Sa-Token模块 + + + + + org.leocoder.heritage + heritage-common + ${revision} + + + + org.leocoder.heritage + heritage-mybatisplus + ${revision} + + + + org.springframework.boot + spring-boot-starter-aop + + + + cn.dev33 + sa-token-spring-boot3-starter + + + + cn.dev33 + sa-token-redis-jackson + + + + + org.apache.commons + commons-pool2 + + + + cn.dev33 + sa-token-spring-aop + + + + \ No newline at end of file diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/anno/EnableCoderSaToken.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/anno/EnableCoderSaToken.java new file mode 100755 index 0000000..578d04e --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/anno/EnableCoderSaToken.java @@ -0,0 +1,16 @@ +package org.leocoder.heritage.satoken.anno; + +import org.leocoder.heritage.satoken.config.CoderSaTokenFilter; +import org.leocoder.heritage.satoken.config.CoderSaTokenInterceptor; +import org.springframework.context.annotation.Import; + +import java.lang.annotation.*; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import({ CoderSaTokenInterceptor.class, CoderSaTokenFilter.class }) +public @interface EnableCoderSaToken { + +} diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenFilter.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenFilter.java new file mode 100755 index 0000000..08740f7 --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenFilter.java @@ -0,0 +1,50 @@ +package org.leocoder.heritage.satoken.config; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.filter.SaServletFilter; +import cn.dev33.satoken.router.SaHttpMethod; +import cn.dev33.satoken.router.SaRouter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Leocoder + * @description [Sa-Token代码方式进行配置] + */ +@Configuration +public class CoderSaTokenFilter { + + /** + * @description [全局过滤器-只用来设置跨域资源和开启浏览器默认XSS防护] + * @author Leocoder + */ + @Bean + public SaServletFilter getSaServletFilter() { + return new SaServletFilter() + // 前置函数:在每次认证函数之前执行 + .setBeforeAuth(r -> { + // ---------- 设置一些安全响应头 ---------- + SaHolder.getResponse() + // 允许指定域访问跨域资源 + .setHeader("Access-Control-Allow-Origin", "*") + .setHeader("Access-Control-Allow-Methods", "*") + .setHeader("Access-Control-Max-Age", "3600") + .setHeader("Access-Control-Allow-Headers", "*") + .setHeader("Content-Type", "application/json;charset=UTF-8") + // 服务器名称 + .setServer("Coder-Admin") + // 是否可以在iframe显示视图: DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以 + .setHeader("X-Frame-Options", "SAMEORIGIN") + // 是否启用浏览器默认XSS防护: 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时,停止渲染页面 + .setHeader("X-XSS-Protection", "1; mode=block") + // 禁用浏览器内容嗅探 + .setHeader("X-Content-Type-Options", "nosniff"); + // 如果是预检请求,则立即返回到前端 + SaRouter.match(SaHttpMethod.OPTIONS) + .free(obj -> { + }) + .back(); + }); + } + +} diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenInterceptor.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenInterceptor.java new file mode 100755 index 0000000..ce09bf7 --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenInterceptor.java @@ -0,0 +1,68 @@ +package org.leocoder.heritage.satoken.config; + +import cn.dev33.satoken.interceptor.SaInterceptor; +import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.stp.StpUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Leocoder + * @description [Sa-Token代码方式进行配置] + */ +@Configuration +public class CoderSaTokenInterceptor implements WebMvcConfigurer { + + @Value("${coder.filePath}") + private String baseFilePath; + + /** + * @description [注册拦截器] + * 使用后必须携带 Authorization[此名称在yml中可自行配置]- Bearer token值[除非不被拦截,但是获取不到当前会话用户ID] + * @author Leocoder + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + + // 注册登录拦截器 + registry.addInterceptor(new SaInterceptor(handle -> { + // 白名单 + List ignoreUrls = new ArrayList<>(); + // favicon.ico浏览器标签Logo + ignoreUrls.add("/favicon.ico"); + // 验证码 + ignoreUrls.add("/captcha/**"); + // 登录,退出登录 + ignoreUrls.add("/auth/**"); + // 测试接口 + // ignoreUrls.add("/coder/**"); + // 上传文件接口 + ignoreUrls.add("/CoderFile/**"); + // 后端项目详情 + ignoreUrls.add("/"); + // 静态资源 + // ignoreUrls.add("/*.html"); + // ignoreUrls.add("/**/*.html"); + // ignoreUrls.add("/**/*.css"); + // ignoreUrls.add("/**/*.js"); + // 上传路径 + ignoreUrls.add(baseFilePath + "/**"); + // Swagger API文档相关路径 + ignoreUrls.add("/swagger-ui/**"); + ignoreUrls.add("/v3/api-docs/**"); + ignoreUrls.add("/v3/api-docs"); + ignoreUrls.add("/swagger-ui.html"); + ignoreUrls.add("/webjars/**"); + + // 除白名单路径外均需要登录认证 + SaRouter.match("/**").notMatch(ignoreUrls).check(r -> StpUtil.checkLogin()); + })).addPathPatterns("/**"); + + } + +} diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenListener.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenListener.java new file mode 100755 index 0000000..fd3787f --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenListener.java @@ -0,0 +1,159 @@ +package org.leocoder.heritage.satoken.config; + +import cn.dev33.satoken.annotation.handler.SaAnnotationHandlerInterface; +import cn.dev33.satoken.config.SaTokenConfig; +import cn.dev33.satoken.listener.SaTokenListener; +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.leocoder.heritage.common.constants.CoderConstants; +import org.leocoder.heritage.mybatisplus.mapper.system.SysLoginUserMapper; +import org.leocoder.heritage.satoken.service.loginlog.SaLoginLogService; +import org.springframework.stereotype.Component; + +/** + * @author Leocoder + * @description [自定义侦听器的实现] + */ +@Slf4j +@RequiredArgsConstructor +@Component +public class CoderSaTokenListener implements SaTokenListener { + + private final SysLoginUserMapper sysLoginUserMapper; + + private final SaLoginLogService saLoginLogService; + + /** + * @description [每次登录时触发] + */ + @Override + public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) { + log.info("自定义侦听器实现-doLogin"); + log.info("登录类型:{}, 登录ID:{}, Token值:{}, 登录Model:{}", loginType, loginId, tokenValue, loginParameter); + } + + /** + * @description [每次注销时触发] + */ + @Override + public void doLogout(String loginType, Object loginId, String tokenValue) { + log.info("自定义侦听器实现 doLogout"); + // 保存注销日志 + saLoginLogService.addLoginLog(sysLoginUserMapper.selectById(Long.valueOf(String.valueOf(loginId))).getLoginName(), CoderConstants.ZERO_STRING, "退出登录"); + } + + /** + * @description [每次被踢下线时触发] + */ + @Override + public void doKickout(String loginType, Object loginId, String tokenValue) { + log.info("自定义侦听器实现 doKickout"); + // 保存踢下线日志 + saLoginLogService.addLoginLog(sysLoginUserMapper.selectById(Long.valueOf(String.valueOf(loginId))).getLoginName(), CoderConstants.ZERO_STRING, "强退下线"); + } + + /** + * @description [每次被顶下线时触发] + */ + @Override + public void doReplaced(String loginType, Object loginId, String tokenValue) { + log.info("自定义侦听器实现 doReplaced"); + // 保存被顶下线日志 + saLoginLogService.addLoginLog(sysLoginUserMapper.selectById(Long.valueOf(String.valueOf(loginId))).getLoginName(), CoderConstants.ZERO_STRING, "被顶下线"); + } + + /** + * @description [每次被封禁时触发] + */ + @Override + public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) { + log.info("自定义侦听器实现 doDisable"); + // 保存被封禁时日志 + saLoginLogService.addLoginLog(sysLoginUserMapper.selectById(Long.valueOf(String.valueOf(loginId))).getLoginName(), CoderConstants.ZERO_STRING, "账号被封禁"); + } + + /** + * @description [每次被解封时触发] + */ + @Override + public void doUntieDisable(String loginType, Object loginId, String service) { + log.info("自定义侦听器实现 doUntieDisable"); + // 保存被解封时日志 + saLoginLogService.addLoginLog(sysLoginUserMapper.selectById(Long.valueOf(String.valueOf(loginId))).getLoginName(), "0", "账号被解封"); + } + + /** + * @description [每次二级认证时触发] + */ + @Override + public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) { + log.info("自定义侦听器实现 doOpenSafe"); + } + + /** + * @description [每次退出二级认证时触发] + */ + @Override + public void doCloseSafe(String loginType, String tokenValue, String service) { + log.info("自定义侦听器实现 doCloseSafe"); + } + + /** + * @description [每次创建Session时触发] + */ + @Override + public void doCreateSession(String id) { + log.info("自定义侦听器实现 doCreateSession"); + } + + /** + * @description [每次注销Session时触发] + */ + @Override + public void doLogoutSession(String id) { + log.info("自定义侦听器实现 doLogoutSession"); + } + + /** + * @description [每次Token续期时触发] + */ + @Override + public void doRenewTimeout(String loginType, Object loginId, String tokenValue, long timeout) { + log.info("自定义侦听器实现 doRenewTimeout"); + } + + /** + * @description [全局组件载入成功] + */ + @Override + public void doRegisterComponent(String compName, Object compObj) { + log.info("全局组件载入成功 doRegisterComponent"); + } + + /** + * @description [注解扩展] + */ + @Override + public void doRegisterAnnotationHandler(SaAnnotationHandlerInterface handler) { + log.info("注解扩展实现 doRegisterAnnotationHandler"); + } + + /** + * @description [会话组件重置] + */ + @Override + public void doSetStpLogic(StpLogic stpLogic) { + log.info("会话组件重置成功 doSetStpLogic"); + } + + /** + * @description [全局配置] + */ + @Override + public void doSetConfig(SaTokenConfig config) { + log.info("全局配置实现 doSetConfig"); + } + +} diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenPasswordUtil.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenPasswordUtil.java new file mode 100755 index 0000000..df381eb --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenPasswordUtil.java @@ -0,0 +1,39 @@ +package org.leocoder.heritage.satoken.config; + +import cn.dev33.satoken.secure.SaSecureUtil; + +/** + * @author Leocoder + * @description [PasswordUtil-用户名+密码加密,然后跟数据库进行对比] + */ +public class CoderSaTokenPasswordUtil { + + /** + * MD5加密后迭代次数(默认2次) + */ + private static final int MD5ENCRYPTNUMBER = 2; + + private CoderSaTokenPasswordUtil() { + throw new AssertionError(); + } + + /** + * @param salt 盐值随机数 + * @param password 密码 + * @description [字符串加密函数MD5实现] + */ + public static String getPassword(String password, String salt) { + String initMd5Pwd = password + salt; + for (int i = 0; i < MD5ENCRYPTNUMBER; i++) { + initMd5Pwd = SaSecureUtil.md5(initMd5Pwd); + } + return initMd5Pwd; + } + + public static void main(String[] args) { + System.out.println("管理员密码:" + getPassword("123456", "20231123")); + System.out.println("Coder密码:" + getPassword("123456", "666666")); + System.out.println("YXT密码:" + getPassword("123456", "666666")); + } + +} diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenStpInterfaceImpl.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenStpInterfaceImpl.java new file mode 100755 index 0000000..7820982 --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/CoderSaTokenStpInterfaceImpl.java @@ -0,0 +1,74 @@ +package org.leocoder.heritage.satoken.config; + +import cn.dev33.satoken.stp.StpInterface; +import cn.hutool.core.collection.CollectionUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.leocoder.heritage.common.exception.coder.YUtil; +import org.leocoder.heritage.common.satoken.CoderLoginUtil; +import org.leocoder.heritage.satoken.service.menu.SaMenuService; +import org.leocoder.heritage.satoken.service.role.SaRoleService; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Leocoder + * @description [StpInterfaceImpl自定义权限验证接口扩展] + */ +@Slf4j +@RequiredArgsConstructor +// 保证此类被SpringBoot扫描,完成Sa-Token的自定义权限验证扩展 +@Component +public class CoderSaTokenStpInterfaceImpl implements StpInterface { + + private final SaRoleService saRoleService; + + private final SaMenuService saMenuService; + + /** + * @description [返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)] + * @author Leocoder + */ + @Override + public List getRoleList(Object loginId, String loginType) { + // 1、数据库查询用户拥有的角色码 + List roleCodeList = saRoleService.listAuthRoleCode(Long.valueOf(loginId.toString())); + // 2、返回角色码集合 + return roleCodeList; + } + + /** + * @description [返回一个账号所拥有的权限码集合] + * @author Leocoder + */ + @Override + public List getPermissionList(Object loginId, String loginType) { + // 1、声明权限码集合 + Set permissionSet = new HashSet<>(); + // 2、遍历角色列表,查询拥有的角色权限码 + List roleKeyList = getRoleList(loginId, loginType); + if (CollectionUtil.isEmpty(roleKeyList)) { + return new ArrayList<>(permissionSet); + } + // 3、角色判断:超级管理员 roleKeyList.contains(CoderConstants.CODER_ADMIN) + if (CoderLoginUtil.getIsCoderAdmin()) { + permissionSet.add("*"); + return new ArrayList<>(permissionSet); + } + // 4、角色判断:其他角色 + List menuAuthlist = null; + for (String roleKey : roleKeyList) { + // 5、根据角色码查询拥有的权限码 + menuAuthlist = saMenuService.listMenuAuth(roleKey); + YUtil.isTrue(CollectionUtil.isEmpty(menuAuthlist), "该用户角色未分配菜单权限,禁止登录"); + permissionSet.addAll(menuAuthlist); + } + // 6、返回权限码集合 + return new ArrayList<>(permissionSet); + } + +} diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/SaTokenConfigure.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/SaTokenConfigure.java new file mode 100755 index 0000000..7c5767d --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/config/SaTokenConfigure.java @@ -0,0 +1,39 @@ +package org.leocoder.heritage.satoken.config; + +import cn.dev33.satoken.interceptor.SaInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @author Leocoder + * @description [SaTokenConfigure-Sa-Token 使用全局拦截器完成注解鉴权功能,为了不为项目带来不必要的性能负担,拦截器默认处于关闭状态 + * 因此,为了使用注解鉴权,你必须手动将 Sa-Token 的全局拦截器注册到你项目中] + */ +@Configuration +public class SaTokenConfigure implements WebMvcConfigurer { + + // 注册 Sa-Token 拦截器,打开注解式鉴权功能 + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 注册 Sa-Token 拦截器,打开注解式鉴权功能 + registry.addInterceptor(new SaInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns( + // 排除静态资源 + "/picture/**", + // 排除其他静态资源 + "/favicon.ico", + "/static/**", + "/css/**", + "/js/**", + "/img/**", + // 排除API文档 + "/swagger-ui/**", + "/v3/api-docs/**", + "/swagger-ui.html", + "/webjars/**" + ); + } + +} diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/loginlog/SaLoginLogService.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/loginlog/SaLoginLogService.java new file mode 100755 index 0000000..9a3e237 --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/loginlog/SaLoginLogService.java @@ -0,0 +1,17 @@ +package org.leocoder.heritage.satoken.service.loginlog; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.leocoder.heritage.domain.pojo.system.SysLoginLog; + +/** + * @author Leocoder + * @description [SysLoginLogService] + */ +public interface SaLoginLogService extends IService { + + /** + * @description [保存登录日志] + * @author Leocoder + */ + void addLoginLog(String loginName, String loginStatus, String message); +} diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/loginlog/SaLoginLogServiceImpl.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/loginlog/SaLoginLogServiceImpl.java new file mode 100755 index 0000000..abf0f57 --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/loginlog/SaLoginLogServiceImpl.java @@ -0,0 +1,75 @@ +package org.leocoder.heritage.satoken.service.loginlog; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import eu.bitwalker.useragentutils.UserAgent; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.leocoder.heritage.common.enmus.log.ClientType; +import org.leocoder.heritage.common.utils.ip.IpAddressUtil; +import org.leocoder.heritage.common.utils.ip.IpUtil; +import org.leocoder.heritage.common.utils.ip.ServletUtil; +import org.leocoder.heritage.domain.pojo.system.SysLoginLog; +import org.leocoder.heritage.mybatisplus.mapper.system.SysLoginLogMapper; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; + +/** + * @author Leocoder + * @description [SysLoginLogServiceImpl] + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SaLoginLogServiceImpl extends ServiceImpl implements SaLoginLogService { + + private final IpAddressUtil ipAddressUtil; + + /** + * @description [保存登录日志] + * @author Leocoder + */ + @Override + public void addLoginLog(String loginName, String loginStatus, String message) { + // 1、new一个登录日志对象,用来装载信息 + SysLoginLog sysLoginLog = new SysLoginLog(); + try { + sysLoginLog.setLoginName(loginName); + sysLoginLog.setLoginStatus(loginStatus); + // LocalDateTime.now() 时间类型为 LocalDateTime类型 + sysLoginLog.setLoginTime(LocalDateTime.now()); + sysLoginLog.setMessage(message); + // 2、登录IP地址 + sysLoginLog.setLoginIp(IpUtil.getIpAddr(ServletUtil.getRequest())); + // 3、登录地理位置 + sysLoginLog.setLoginAddress(ipAddressUtil.getAddress(sysLoginLog.getLoginIp())); + // 4、UserAgent信息 + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtil.getRequest().getHeader("User-Agent")); + // 5、登录浏览器 + sysLoginLog.setBrowser(userAgent.getBrowser().getName()); + // 6、登录操作系统 + sysLoginLog.setOs(userAgent.getOperatingSystem().getName()); + // 7、登录设备 + String deviceName = userAgent.getOperatingSystem().getDeviceType().getName(); + if (StringUtils.isBlank(deviceName)) { + deviceName = "Default"; + } else if ("Computer".equals(deviceName)) { + deviceName = ClientType.PC.name(); + } else if ("Mobile".equals(deviceName)) { + deviceName = ClientType.MOBILE.name(); + } + sysLoginLog.setDeviceName(deviceName); + } catch (Exception e) { + sysLoginLog.setMessage(e.getMessage()); + sysLoginLog.setLoginStatus("1"); + log.error("登录日志异常信息:{}", e.getMessage()); + e.printStackTrace(); + } finally { + // 8、登录日志保存 + this.save(sysLoginLog); + } + + } + +} diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/menu/SaMenuService.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/menu/SaMenuService.java new file mode 100755 index 0000000..4c3f534 --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/menu/SaMenuService.java @@ -0,0 +1,20 @@ +package org.leocoder.heritage.satoken.service.menu; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.leocoder.heritage.domain.pojo.system.SysMenu; + +import java.util.List; + +/** + * @author Leocoder + * @description [SaMenuService] + */ +public interface SaMenuService extends IService { + + /** + * @description [根据角色编码查询菜单权限-Sa-Token权限] + * @author Leocoder + */ + List listMenuAuth(String roleCode); + +} diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/menu/SaMenuServiceImpl.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/menu/SaMenuServiceImpl.java new file mode 100755 index 0000000..6130b95 --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/menu/SaMenuServiceImpl.java @@ -0,0 +1,32 @@ +package org.leocoder.heritage.satoken.service.menu; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.leocoder.heritage.domain.pojo.system.SysMenu; +import org.leocoder.heritage.mybatisplus.mapper.system.SysMenuMapper; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author Leocoder + * @description [SysMenuServiceImpl] + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SaMenuServiceImpl extends ServiceImpl implements SaMenuService { + + private final SysMenuMapper menuMapper; + + /** + * @description [根据角色编码查询菜单权限-Sa-Token权限] + * @author Leocoder + */ + @Override + public List listMenuAuth(String roleCode) { + return menuMapper.listMenuAuth(roleCode); + } + +} diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/role/SaRoleService.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/role/SaRoleService.java new file mode 100755 index 0000000..ecfaa32 --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/role/SaRoleService.java @@ -0,0 +1,19 @@ +package org.leocoder.heritage.satoken.service.role; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.leocoder.heritage.domain.pojo.system.SysRole; + +import java.util.List; + +/** + * @author Leocoder + * @description [SaRoleService] + */ +public interface SaRoleService extends IService { + + /** + * @description [查询用户拥有正常角色-Sa-Token角色权限] + * @author Leocoder + */ + List listAuthRoleCode(Long userId); +} diff --git a/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/role/SaRoleServiceImpl.java b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/role/SaRoleServiceImpl.java new file mode 100755 index 0000000..f6c7b9e --- /dev/null +++ b/heritage-plugins/heritage-sa-token/src/main/java/org/leocoder/heritage/satoken/service/role/SaRoleServiceImpl.java @@ -0,0 +1,32 @@ +package org.leocoder.heritage.satoken.service.role; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.leocoder.heritage.domain.pojo.system.SysRole; +import org.leocoder.heritage.mybatisplus.mapper.system.SysRoleMapper; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author Leocoder + * @description [SysRoleServiceImpl] + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SaRoleServiceImpl extends ServiceImpl implements SaRoleService { + + private final SysRoleMapper roleMapper; + + /** + * @description [查询用户拥有正常角色-Sa-Token角色权限] + * @author Leocoder + */ + @Override + public List listAuthRoleCode(Long userId) { + return roleMapper.listAuthRoleCode(userId); + } + +}