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 deleted file mode 100644 index f7dbd36..0000000 --- a/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/annotation/Auth.java +++ /dev/null @@ -1,18 +0,0 @@ -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-gateway/pom.xml b/iot-gateway/pom.xml index 6e514d6..04ecd3d 100644 --- a/iot-gateway/pom.xml +++ b/iot-gateway/pom.xml @@ -59,6 +59,16 @@ 1.2.83 compile + + org.springframework.boot + spring-boot-starter-data-redis-reactive + + + com.qiuguo.iot + iot-base + 0.0.1-SNAPSHOT + compile + diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java index 7810bbd..dfb0065 100644 --- a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java @@ -13,8 +13,8 @@ import org.springframework.web.filter.CorsFilter; * 全局跨域配置. * */ -@AutoConfiguration -@EnableConfigurationProperties(CorsProperties.class) +// @AutoConfiguration +// @EnableConfigurationProperties(CorsProperties.class) public class GlobalCorsConfiguration { /** @@ -22,8 +22,8 @@ public class GlobalCorsConfiguration { * * @param corsProperties 跨域配置 */ - @Bean - @ConditionalOnMissingBean(CorsFilter.class) + // @Bean + // @ConditionalOnMissingBean(CorsFilter.class) public CorsFilter corsFilter(CorsProperties corsProperties) { CorsConfiguration config = new CorsConfiguration(); // 设置允许跨域访问的域名 diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java index a0ef19d..e9368e5 100644 --- a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java @@ -13,7 +13,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; */ @Getter @Setter -@ConfigurationProperties(prefix = "application.cors") +// @ConfigurationProperties(prefix = "application.cors") public class CorsProperties { /** diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/AuthFilter.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/AuthFilter.java new file mode 100644 index 0000000..3d9791d --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/AuthFilter.java @@ -0,0 +1,69 @@ +package com.qiuguo.iot.gateway.filter; + +import com.qiuguo.iot.base.constans.RedisConstans; +import com.qiuguo.iot.base.constans.UserAuthContains; +import com.qiuguo.iot.gateway.config.properties.XssProperties; +import java.time.Duration; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.data.redis.core.ReactiveStringRedisTemplate; +import org.springframework.http.server.RequestPath; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * XXX + * + * @author weiyachao + * @since 2023/9/21 17:56 + */ +@Component +@Slf4j +public class AuthFilter implements GlobalFilter, Ordered { + + @Autowired + private ReactiveStringRedisTemplate reactiveRedisTemplate; + + @Autowired + private XssProperties xssProperties; + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + String url = request.getURI().toString(); + if (xssProperties.getExcludeUrls().contains(url)) { + return chain.filter(exchange); + } + + 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); + } + }); + } + + + @Override + public int getOrder() { + return -1; + } +} diff --git a/iot-gateway/src/main/resources/bootstrap.yml b/iot-gateway/src/main/resources/bootstrap.yml index 8efac3c..b854733 100644 --- a/iot-gateway/src/main/resources/bootstrap.yml +++ b/iot-gateway/src/main/resources/bootstrap.yml @@ -19,4 +19,35 @@ spring: # 共享配置 shared-configs: - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + gateway: + discovery: + locator: + # 开启服务发现 + enabled: true + # 忽略注册中心服务的大小写 + lower-case-service-id: true + globalcors: + corsConfigurations: + '[/**]': + # 允许携带认证信息 + allow-credentials: true + # 允许跨域的源(网站域名/ip),设置*为全部 + allowedOriginPatterns: "*" + # 允许跨域的method, 默认为GET和OPTIONS,设置*为全部 + allowedMethods: "*" + # 允许跨域请求里的head字段,设置*为全部 + allowedHeaders: "*" + routes: + +# 安全配置 +security: + # 防止XSS攻击 + xss: + enabled: true + # 排除的路径 + exclude-urls: + - /ehs-audit/web/audit-content +application: + cors: + allowed-crigin-patterns: diff --git a/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/controller/user/UserController.java b/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/controller/user/UserController.java index 1d31b5e..a99c9b0 100644 --- a/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/controller/user/UserController.java +++ b/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/controller/user/UserController.java @@ -1,9 +1,16 @@ package com.qiuguo.iot.user.api.controller.user; import com.alibaba.fastjson.JSONObject; +import com.qiuguo.iot.base.constans.RedisConstans; +import com.qiuguo.iot.base.constans.UserAuthContains; +import java.time.Duration; import java.util.Objects; +import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.ReactiveRedisTemplate; +import org.springframework.data.redis.core.ReactiveStringRedisTemplate; import org.springframework.http.HttpHeaders; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -31,7 +38,7 @@ import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VAL public class UserController { private final WebClient webClient = WebClient.builder() .defaultHeader(HttpHeaders.CONTENT_TYPE,APPLICATION_FORM_URLENCODED_VALUE) - .defaultHeader("Api-Type","web") + .defaultHeader("Api-Type","iot") .build(); @Value("${userUrl.baseUrl}") @@ -64,12 +71,15 @@ public class UserController { @Value("${userUrl.editUserInfoUrl}") private String editUserInfoUrl; + @Resource + private ReactiveStringRedisTemplate reactiveStringRedisTemplate; + /** * 修改登录密码-auth */ @PostMapping("/change") - public Mono change(@RequestBody JSONObject jsonObject, @RequestHeader("Api-Token") String token, - @RequestHeader("Api-Type") String type) { + public Mono change(@RequestBody JSONObject jsonObject, @RequestHeader(UserAuthContains.API_TOKEN) String token, + @RequestHeader(UserAuthContains.API_TYPE) String type) { WebClient authWebClient = getAuthWebClient(token, type); return authWebClient.post().uri(baseUrl + changeUrl).bodyValue(getMultiValueMap(jsonObject)).retrieve() .bodyToMono(JSONObject.class).doOnNext(res -> { @@ -83,8 +93,8 @@ public class UserController { * 账号注销-auth */ @PostMapping("/userCance") - public Mono userCance(@RequestBody JSONObject jsonObject, @RequestHeader("Api-Token") String token, - @RequestHeader("Api-Type") String type) { + public Mono userCance(@RequestBody JSONObject jsonObject, @RequestHeader(UserAuthContains.API_TOKEN) String token, + @RequestHeader(UserAuthContains.API_TYPE) String type) { return getAuthWebClient(token, type).post().uri(baseUrl + userCancelUrl) .bodyValue(getMultiValueMap(jsonObject)) .retrieve() @@ -99,8 +109,8 @@ public class UserController { * 修改用户信息-auth */ @PostMapping("/edit/userInfo") - public Mono editUserInfo(@RequestBody JSONObject jsonObject, @RequestHeader("Api-Token") String token, - @RequestHeader("Api-Type") String type) { + public Mono editUserInfo(@RequestBody JSONObject jsonObject, @RequestHeader(UserAuthContains.API_TOKEN) String token, + @RequestHeader(UserAuthContains.API_TYPE) String type) { return webClient.mutate() .defaultHeader("Api-Token", token) .defaultHeader("Api-Type", type).build().post().uri(editUserInfoUrl) @@ -117,8 +127,8 @@ public class UserController { * 个人信息管理-auth */ @GetMapping("/userInfo") - public Mono getUserInfo(@RequestHeader("Api-Token") String token, - @RequestHeader("Api-Type") String type) { + public Mono getUserInfo(@RequestHeader(UserAuthContains.API_TOKEN) String token, + @RequestHeader(UserAuthContains.API_TYPE) String type) { return getAuthWebClient(token, type).get().uri(userInfoUrl).retrieve() .bodyToMono(JSONObject.class).doOnNext(res -> { if (!Objects.equals(res.getInteger("code"), 200)) { @@ -131,8 +141,8 @@ public class UserController { * 是否设置登录密码-auth */ @GetMapping("/first/password") - public Mono firstPassword(@RequestHeader("Api-Token") String token, - @RequestHeader("Api-Type") String type) { + public Mono firstPassword(@RequestHeader(UserAuthContains.API_TOKEN) String token, + @RequestHeader(UserAuthContains.API_TYPE) String type) { return getAuthWebClient(token, type).get().uri(firstPasswordUrl).retrieve() .bodyToMono(JSONObject.class).doOnNext(res -> { if (!Objects.equals(res.getInteger("code"), 200)) { @@ -166,7 +176,9 @@ public class UserController { .retrieve().bodyToMono(JSONObject.class).flatMap(res -> { if (Objects.equals(res.getInteger("code"), 1) && !res.getString("info") .contains("该手机号还没有注册哦")) { - return Mono.just(res); + String token = res.getJSONObject("data").getJSONObject("token").getString("token"); + return reactiveStringRedisTemplate.opsForValue() + .set(RedisConstans.IOT_TOKEN.concat(token), token, Duration.ofDays(7)).then(Mono.just(res)); } else if(!res.getString("info").contains("该手机号还没有注册哦")){ return Mono.error(new RuntimeException(res.getString("info"))); }else { @@ -186,10 +198,14 @@ public class UserController { object.add("phone", jsonObject.getString("phone")); object.add("verify", jsonObject.getString("verify")); return webClient.post().uri(baseUrl + smsUrl).bodyValue(object).retrieve() - .bodyToMono(JSONObject.class).doOnNext(twoRes -> { + .bodyToMono(JSONObject.class).flatMap(twoRes -> { if (!Objects.equals(twoRes.getInteger("code"), 1)) { - throw new RuntimeException(twoRes.getString("info")); + return Mono.error(new RuntimeException(twoRes.getString("info"))); } + String token = res.getJSONObject("data").getJSONObject("token").getString("token"); + return reactiveStringRedisTemplate.opsForValue() + .set(RedisConstans.IOT_TOKEN.concat(token), token, Duration.ofDays(7)).then(Mono.just(res)); + }); } }); @@ -205,11 +221,15 @@ public class UserController { log.info("UserController[]loginByPwd[]jsonObject:{}", jsonObject); return webClient.post().uri(baseUrl + pwdUrl).bodyValue(getMultiValueMap(jsonObject)).retrieve() .bodyToMono(JSONObject.class) - .doOnNext(res -> { + .flatMap(res -> { if (!Objects.equals(res.getInteger("code"), 1)) { - throw new RuntimeException(res.getString("info")); + return Mono.error(new RuntimeException(res.getString("info"))); } + String token = res.getJSONObject("data").getJSONObject("token").getString("token"); + return reactiveStringRedisTemplate.opsForValue() + .set(RedisConstans.IOT_TOKEN.concat(token), token, Duration.ofDays(7)).then(Mono.just(res)); }); + } private MultiValueMap getMultiValueMap(JSONObject jsonObject) { 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 index 82e29df..ae8d0bd 100644 --- 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 @@ -1,21 +1,9 @@ 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; diff --git a/iot-modules/iot-box-user-api/src/main/resources/bootstrap.yml b/iot-modules/iot-box-user-api/src/main/resources/bootstrap.yml index 5b7a48f..f5cc792 100644 --- a/iot-modules/iot-box-user-api/src/main/resources/bootstrap.yml +++ b/iot-modules/iot-box-user-api/src/main/resources/bootstrap.yml @@ -10,10 +10,10 @@ spring: nacos: discovery: # 服务注册地址 - server-addr: 172.24.218.235:8848/ + server-addr: 192.168.8.146:32470 config: # 配置中心地址 - server-addr: 172.24.218.235:8848/ + server-addr: 192.168.8.146:32470 # 配置文件格式 file-extension: yml # 共享配置 diff --git a/iot-modules/iot-box-user-api/src/test/java/UserTest.java b/iot-modules/iot-box-user-api/src/test/java/UserTest.java index 59628cf..54ce71d 100644 --- a/iot-modules/iot-box-user-api/src/test/java/UserTest.java +++ b/iot-modules/iot-box-user-api/src/test/java/UserTest.java @@ -18,7 +18,7 @@ import org.springframework.boot.test.context.SpringBootTest; @Slf4j public class UserTest { - public String deviceId = "6c4a153095be2b7f8baofp"; + public String deviceId = "6cae26f5512eee7c12aqd9"; public String spaceId = "163257138"; @@ -88,7 +88,7 @@ public class UserTest { JSONObject js3 = new JSONObject(); js3.put("code", "bright_value_v2"); js3.put("value", 10); - commands.put("commands", Arrays.asList( js3)); + commands.put("commands", Arrays.asList(jsonObject)); Object controlDevice = tuyaDeviceConnector.controlDevice(deviceId,commands); System.out.println(controlDevice);