diff --git a/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/annotation/Auth.java b/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/annotation/Auth.java new file mode 100644 index 0000000..f7dbd36 --- /dev/null +++ b/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/annotation/Auth.java @@ -0,0 +1,18 @@ +package com.qiuguo.iot.base.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 强制登陆 + * + * @author weiyachao + * @since 2023/9/20 15:55 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Auth { + +} diff --git a/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/constans/RedisConstans.java b/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/constans/RedisConstans.java index 8110227..0214625 100644 --- a/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/constans/RedisConstans.java +++ b/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/constans/RedisConstans.java @@ -6,4 +6,6 @@ package com.qiuguo.iot.base.constans; public class RedisConstans { public static String DEVICE_INFO = "device::info::"; + + public static String IOT_TOKEN = "iot_token:"; } diff --git a/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/constans/UserAuthContains.java b/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/constans/UserAuthContains.java new file mode 100644 index 0000000..b916128 --- /dev/null +++ b/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/constans/UserAuthContains.java @@ -0,0 +1,16 @@ +package com.qiuguo.iot.base.constans; + +/** + * XXX + * + * @author weiyachao 包含 + * @since 2023/9/20 16:25 + */ +public interface UserAuthContains { + + String API_TOKEN = "Api-Token"; + + String API_TYPE = "Api-Type"; + + +} diff --git a/iot-modules/iot-box-user-api/pom.xml b/iot-modules/iot-box-user-api/pom.xml index 041cf45..b240751 100644 --- a/iot-modules/iot-box-user-api/pom.xml +++ b/iot-modules/iot-box-user-api/pom.xml @@ -62,6 +62,11 @@ ${hsweb.orm.version} + + org.springframework.boot + spring-boot-starter-data-redis-reactive + + org.hswebframework.web hsweb-starter diff --git a/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/IotBoxUserApiApplication.java b/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/IotBoxUserApiApplication.java index 13cd50b..17269cf 100644 --- a/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/IotBoxUserApiApplication.java +++ b/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/IotBoxUserApiApplication.java @@ -4,10 +4,12 @@ import com.tuya.connector.spring.annotations.ConnectorScan; import org.hswebframework.web.crud.annotation.EnableEasyormRepository; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication(scanBasePackages = {"com.qiuguo.iot.user.api", "com.qiuguo.iot.data.service"}) @EnableEasyormRepository(value = "com.qiuguo.iot.data.entity.*") @ConnectorScan(basePackages = "com.qiuguo.iot.user.api.service") +@EnableAspectJAutoProxy public class IotBoxUserApiApplication { public static void main(String[] args) { diff --git a/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/config/AuthAspect.java b/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/config/AuthAspect.java new file mode 100644 index 0000000..5a6dc21 --- /dev/null +++ b/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/config/AuthAspect.java @@ -0,0 +1,50 @@ +package com.qiuguo.iot.user.api.config; + +import com.qiuguo.iot.base.annotation.Auth; +import com.qiuguo.iot.base.constans.UserAuthContains; +import java.lang.reflect.Method; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.ReactiveStringRedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; + +/** + * XXX + * + * @author weiyachao + * @since 2023/9/20 18:50 + */ + +@Aspect +@Component +public class AuthAspect { + + @Autowired + private ReactiveStringRedisTemplate reactiveRedisTemplate; + // @Autowired(required = false) + // private ServerWebExchange serverWebExchange; + // + // @Autowired + // private ServerHttpRequest httpServletRequest; + + + + @Around("@annotation(com.qiuguo.iot.base.annotation.Auth)") // 切入点表达式,这里使用了自定义注解 + public Object authenticate(ProceedingJoinPoint joinPoint) throws Throwable { + + + // String first = httpServletRequest.getHeaders().getFirst(UserAuthContains.API_TOKEN); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + Auth annotation = method.getAnnotation(Auth.class); + System.out.println("annotation = " + annotation); + + return joinPoint.proceed(); + } + + +} diff --git a/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/filter/AuthFilter.java b/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/filter/AuthFilter.java new file mode 100644 index 0000000..50014c2 --- /dev/null +++ b/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/filter/AuthFilter.java @@ -0,0 +1,70 @@ +package com.qiuguo.iot.user.api.filter; + +import com.qiuguo.iot.base.annotation.Auth; +import com.qiuguo.iot.base.constans.RedisConstans; +import com.qiuguo.iot.base.constans.UserAuthContains; +import java.lang.annotation.ElementType; +import java.time.Duration; +import java.util.Map; +import javax.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.data.redis.core.ReactiveRedisTemplate; +import org.springframework.data.redis.core.ReactiveStringRedisTemplate; +import org.springframework.util.ObjectUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; +import org.springframework.web.servlet.HandlerMapping; +import reactor.core.publisher.Mono; + +/** + * XXX + * + * @author weiyachao + * @since 2023/9/20 16:06 + */ +@Configuration +@Slf4j +@Order(-1) +public class AuthFilter implements WebFilter { + + // @Autowired + private ReactiveStringRedisTemplate reactiveRedisTemplate; + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + HandlerMethod handlerMethod = exchange.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE); + Object attribute = exchange.getAttribute("org.springframework.web.server.ServerWebExchange.LOG_ID"); + System.out.println("attribute = " + attribute); + if (handlerMethod != null && handlerMethod.getMethod().isAnnotationPresent(Auth.class)) { + // 如果请求方法上有 Auth 注解,执行登录验证逻辑 + String api_token = exchange.getRequest().getHeaders().getFirst(UserAuthContains.API_TOKEN); + String api_type = exchange.getRequest().getHeaders().getFirst(UserAuthContains.API_TYPE); + if (ObjectUtils.isEmpty(api_token) || ObjectUtils.isEmpty(api_type)) { + return Mono.error(new RuntimeException("未登录")); + } + String key = RedisConstans.IOT_TOKEN.concat(api_token); + return reactiveRedisTemplate.getExpire(key).map(Duration::getSeconds).flatMap(ttl -> { + if (ttl == -1) { + // 用户没登陆 + return Mono.error(new RuntimeException("未登录")); + } else if (ttl <= 3600) { + // token 将要失效 + return reactiveRedisTemplate.expire(key, Duration.ofDays(7)) + .then(chain.filter(exchange)); + } else { + // 正常登录 + return chain.filter(exchange); + } + }); + } + + return chain.filter(exchange); + } + +}