Browse Source

授权认证重构集成sa-token

xiwa 3 years ago
parent
commit
32d35c2db0
58 changed files with 1926 additions and 300 deletions
  1. 2 2
      README.md
  2. 1 1
      common/pom.xml
  3. 1 1
      common/src/main/java/cc/iotkit/common/ComponentClassLoader.java
  4. 9 2
      common/src/main/java/cc/iotkit/common/Constants.java
  5. 0 1
      common/src/main/java/cc/iotkit/common/utils/CodecUtil.java
  6. 1 1
      dao/pom.xml
  7. 33 0
      dao/src/main/java/cc/iotkit/dao/OauthClientCache.java
  8. 11 0
      dao/src/main/java/cc/iotkit/dao/OauthClientRepository.java
  9. 33 0
      dao/src/main/java/cc/iotkit/dao/UserInfoCache.java
  10. 2 0
      dao/src/main/java/cc/iotkit/dao/UserInfoRepository.java
  11. 81 95
      manager/pom.xml
  12. 11 2
      manager/src/main/java/cc/iotkit/manager/Application.java
  13. 0 0
      manager/src/main/java/cc/iotkit/manager/config/AutoBeanConfig.java1
  14. 10 0
      manager/src/main/java/cc/iotkit/manager/config/CacheConfig.java
  15. 8 20
      manager/src/main/java/cc/iotkit/manager/config/EmbeddedElasticSearchConfig.java
  16. 16 0
      manager/src/main/java/cc/iotkit/manager/config/EmbeddedRedisConfig.java
  17. 7 1
      manager/src/main/java/cc/iotkit/manager/config/GlobalExceptionHandler.java
  18. 0 0
      manager/src/main/java/cc/iotkit/manager/config/KeycloakSecurityConfig.java1
  19. 4 0
      manager/src/main/java/cc/iotkit/manager/config/ResponseResultHandler.java
  20. 66 0
      manager/src/main/java/cc/iotkit/manager/config/SaTokenConfigure.java
  21. 55 0
      manager/src/main/java/cc/iotkit/manager/config/SecurityConfig.java1
  22. 3 3
      manager/src/main/java/cc/iotkit/manager/controller/ProductController.java
  23. 0 2
      manager/src/main/java/cc/iotkit/manager/controller/SpaceDeviceController.java
  24. 10 22
      manager/src/main/java/cc/iotkit/manager/controller/ThirdAuthController.java
  25. 2 44
      manager/src/main/java/cc/iotkit/manager/controller/aligenie/AligenieDeviceController.java
  26. 0 4
      manager/src/main/java/cc/iotkit/manager/controller/aligenie/AligenieProductController.java
  27. 0 0
      manager/src/main/java/cc/iotkit/manager/service/KeycloakAdminService.java1
  28. 4 18
      manager/src/main/java/cc/iotkit/manager/utils/AuthUtil.java
  29. 18 22
      manager/src/main/resources/application-dev.yml
  30. 19 23
      manager/src/main/resources/application.yml
  31. 2 2
      model/pom.xml
  32. 25 0
      model/src/main/java/cc/iotkit/model/OauthClient.java
  33. 14 0
      model/src/main/java/cc/iotkit/model/Role.java
  34. 11 1
      model/src/main/java/cc/iotkit/model/ThirdUserSession.java
  35. 53 0
      oauth2-server/pom.xml
  36. 131 0
      oauth2-server/src/main/java/cc/iotkit/oauth/controller/AuthClientController.java
  37. 97 0
      oauth2-server/src/main/java/cc/iotkit/oauth/controller/AuthServerController.java
  38. 51 0
      oauth2-server/src/main/java/cc/iotkit/oauth/service/SaOAuth2TemplateImpl.java
  39. 38 0
      oauth2-server/src/main/java/cc/iotkit/oauth/service/StpInterfaceImpl.java
  40. 62 0
      oauth2-server/src/main/java/cc/iotkit/oauth/service/TokenRequestHandler.java
  41. 737 0
      oauth2-server/src/main/java/cc/iotkit/utils/SoMap.java
  42. 117 0
      oauth2-server/src/main/resources/templates/confirm.html
  43. 123 0
      oauth2-server/src/main/resources/templates/login.html
  44. 36 11
      pom.xml
  45. 1 1
      protocol-gateway/component-server/pom.xml
  46. 1 1
      protocol-gateway/component/pom.xml
  47. 1 1
      protocol-gateway/converter/pom.xml
  48. 4 4
      protocol-gateway/emqx-component/dependency-reduced-pom.xml
  49. 1 1
      protocol-gateway/emqx-component/pom.xml
  50. 2 2
      protocol-gateway/http-biz-component/dependency-reduced-pom.xml
  51. 1 1
      protocol-gateway/http-biz-component/pom.xml
  52. 1 1
      protocol-gateway/mqtt-client-simulator/pom.xml
  53. 2 2
      protocol-gateway/mqtt-client-simulator/src/main/java/cc/iotkit/simulator/Application.java
  54. 4 4
      protocol-gateway/mqtt-component/dependency-reduced-pom.xml
  55. 1 1
      protocol-gateway/mqtt-component/pom.xml
  56. 1 1
      protocol-gateway/pom.xml
  57. 1 1
      rule-engine/pom.xml
  58. 1 1
      standalone-package/pom.xml

+ 2 - 2
README.md

@@ -4,7 +4,7 @@
 此仓库为奇特物联(iotkit)物联网平台开源项目。
 此仓库为奇特物联(iotkit)物联网平台开源项目。
 奇特物联是一个开源的物联网基础开发平台,提供了物联网及相关业务开发的常见基础功能, 能帮助你快速搭建自己的物联网相关业务平台。
 奇特物联是一个开源的物联网基础开发平台,提供了物联网及相关业务开发的常见基础功能, 能帮助你快速搭建自己的物联网相关业务平台。
 
 
-系统包含了品类、物模型、消息转换、通讯组件(mqtt通讯组件、小度音箱接入组件、onenet Studio接入组件)、云端低代码设备开发、设备管理、规则引擎、第三方平台接入、数据流转、数据可视化、报警中心等模块和智能家居APP(小程序)。
+系统包含了品类、物模型、消息转换、通讯组件(mqtt通讯组件、小度音箱接入组件、onenet Studio接入组件)、云端低代码设备开发、设备管理、规则引擎、第三方平台接入、数据流转、数据可视化、报警中心等模块和智能家居APP(小程序),集成了[Sa-Token](https://gitee.com/dromara/sa-token) 认证框架
 
 
  **前端项目见:** https://gitee.com/iotkit-open-source/iot-console-web
  **前端项目见:** https://gitee.com/iotkit-open-source/iot-console-web
 
 
@@ -15,7 +15,7 @@
 
 
 #### 软件架构
 #### 软件架构
 软件架构说明
 软件架构说明
-本系统采用springboot、mongodb、redis、elasticsearch、pulsar、keycloak等框架和第三方软件
+本系统采用springboot、mongodb、redis、elasticsearch、pulsar、sa-token等框架和第三方软件
 
 
 
 
 #### 安装教程
 #### 安装教程

+ 1 - 1
common/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>iotkit-parent</artifactId>
         <artifactId>iotkit-parent</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 1 - 1
common/src/main/java/cc/iotkit/common/ComponentClassLoader.java

@@ -32,7 +32,7 @@ public class ComponentClassLoader {
         classLoaders.put(name, classLoader);
         classLoaders.put(name, classLoader);
 
 
         Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
         Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
-        if (!method.isAccessible()) {
+        if (!method.canAccess(classLoader)) {
             method.setAccessible(true);
             method.setAccessible(true);
         }
         }
 
 

+ 9 - 2
common/src/main/java/cc/iotkit/common/Constants.java

@@ -1,7 +1,5 @@
 package cc.iotkit.common;
 package cc.iotkit.common;
 
 
-import lombok.Data;
-
 public interface Constants {
 public interface Constants {
 
 
     String PRODUCT_SECRET = "xdkKUymrEGSCYWswqCvSPyRSFvH5j7CU";
     String PRODUCT_SECRET = "xdkKUymrEGSCYWswqCvSPyRSFvH5j7CU";
@@ -18,6 +16,10 @@ public interface Constants {
 
 
     String THING_MODEL_CACHE = "thing_model_cache";
     String THING_MODEL_CACHE = "thing_model_cache";
 
 
+    String USER_CACHE = "user_info_cache";
+
+    String OAUTH_CLIENT_CACHE = "oauth_client_cache";
+
     String WECHAT_APP_ID = "wx791cb7bf75950e0c";
     String WECHAT_APP_ID = "wx791cb7bf75950e0c";
 
 
     String WECHAT_APP_SECRET = "eeef73ce71f1a722ad6298985d859844";
     String WECHAT_APP_SECRET = "eeef73ce71f1a722ad6298985d859844";
@@ -66,6 +68,11 @@ public interface Constants {
      */
      */
     String HTTP_CONSUMER_DEVICE_INFO_TOPIC = "device_info:";
     String HTTP_CONSUMER_DEVICE_INFO_TOPIC = "device_info:";
 
 
+    /**
+     * 写权限
+     */
+    String PERMISSION_WRITE = "write";
+
     /**
     /**
      * 三方平台类型
      * 三方平台类型
      */
      */

+ 0 - 1
common/src/main/java/cc/iotkit/common/utils/CodecUtil.java

@@ -96,5 +96,4 @@ public class CodecUtil {
         encryptStr = new String(HexUtil.parseHex(encryptStr));
         encryptStr = new String(HexUtil.parseHex(encryptStr));
         return StringUtils.isEmpty(encryptStr) ? "" : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
         return StringUtils.isEmpty(encryptStr) ? "" : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
     }
     }
-
 }
 }

+ 1 - 1
dao/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>iotkit-parent</artifactId>
         <artifactId>iotkit-parent</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 33 - 0
dao/src/main/java/cc/iotkit/dao/OauthClientCache.java

@@ -0,0 +1,33 @@
+package cc.iotkit.dao;
+
+import cc.iotkit.common.Constants;
+import cc.iotkit.model.OauthClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Repository;
+
+import javax.annotation.PostConstruct;
+
+@Repository
+public class OauthClientCache {
+
+    @Autowired
+    private OauthClientRepository oauthClientRepository;
+
+    private static OauthClientCache INSTANCE;
+
+    @PostConstruct
+    public void init() {
+        INSTANCE = this;
+    }
+
+    public static OauthClientCache getInstance() {
+        return INSTANCE;
+    }
+
+    @Cacheable(value = Constants.OAUTH_CLIENT_CACHE, key = "#clientId")
+    public OauthClient getClient(String clientId) {
+        return oauthClientRepository.findById(clientId).orElse(null);
+    }
+
+}

+ 11 - 0
dao/src/main/java/cc/iotkit/dao/OauthClientRepository.java

@@ -0,0 +1,11 @@
+package cc.iotkit.dao;
+
+import cc.iotkit.model.OauthClient;
+import org.springframework.data.mongodb.repository.MongoRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface OauthClientRepository extends MongoRepository<OauthClient, String> {
+
+
+}

+ 33 - 0
dao/src/main/java/cc/iotkit/dao/UserInfoCache.java

@@ -0,0 +1,33 @@
+package cc.iotkit.dao;
+
+import cc.iotkit.common.Constants;
+import cc.iotkit.model.UserInfo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Repository;
+
+import javax.annotation.PostConstruct;
+
+@Repository
+public class UserInfoCache {
+
+    @Autowired
+    private UserInfoRepository userInfoRepository;
+
+    private static UserInfoCache INSTANCE;
+
+    @PostConstruct
+    public void init() {
+        INSTANCE = this;
+    }
+
+    public static UserInfoCache getInstance() {
+        return INSTANCE;
+    }
+
+    @Cacheable(value = Constants.USER_CACHE, key = "#uid")
+    public UserInfo getUserInfo(String uid) {
+        return userInfoRepository.findById(uid).orElse(null);
+    }
+
+}

+ 2 - 0
dao/src/main/java/cc/iotkit/dao/UserInfoRepository.java

@@ -9,6 +9,8 @@ import java.util.List;
 @Repository
 @Repository
 public interface UserInfoRepository extends MongoRepository<UserInfo, String> {
 public interface UserInfoRepository extends MongoRepository<UserInfo, String> {
 
 
+    UserInfo findByUid(String uid);
+
     List<UserInfo> findByType(int type);
     List<UserInfo> findByType(int type);
 
 
     List<UserInfo> findByTypeAndOwnerId(int type, String ownerId);
     List<UserInfo> findByTypeAndOwnerId(int type, String ownerId);

+ 81 - 95
manager/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>iotkit-parent</artifactId>
         <artifactId>iotkit-parent</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 
@@ -28,39 +28,20 @@
             <artifactId>spring-boot-starter-cache</artifactId>
             <artifactId>spring-boot-starter-cache</artifactId>
         </dependency>
         </dependency>
 
 
-        <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-openfeign</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-security</artifactId>
-        </dependency>
-
         <dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-validation</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-spring-boot-starter</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-spring-security-adapter</artifactId>
+            <artifactId>spring-boot-starter-logging</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-to-slf4j</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         </dependency>
 
 
         <dependency>
         <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-admin-client</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>javax.ws.rs</groupId>
-            <artifactId>javax.ws.rs-api</artifactId>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
         </dependency>
         </dependency>
 
 
         <dependency>
         <dependency>
@@ -68,11 +49,6 @@
             <artifactId>caffeine</artifactId>
             <artifactId>caffeine</artifactId>
         </dependency>
         </dependency>
 
 
-        <dependency>
-            <groupId>com.squareup.okhttp3</groupId>
-            <artifactId>okhttp</artifactId>
-        </dependency>
-
         <dependency>
         <dependency>
             <groupId>org.projectlombok</groupId>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
             <artifactId>lombok</artifactId>
@@ -114,6 +90,11 @@
             <artifactId>joda-time</artifactId>
             <artifactId>joda-time</artifactId>
         </dependency>
         </dependency>
 
 
+        <dependency>
+            <groupId>it.ozimov</groupId>
+            <artifactId>embedded-redis</artifactId>
+        </dependency>
+
         <dependency>
         <dependency>
             <groupId>cc.iotkit</groupId>
             <groupId>cc.iotkit</groupId>
             <artifactId>model</artifactId>
             <artifactId>model</artifactId>
@@ -144,69 +125,74 @@
             <artifactId>converter</artifactId>
             <artifactId>converter</artifactId>
         </dependency>
         </dependency>
 
 
+        <dependency>
+            <groupId>cc.iotkit</groupId>
+            <artifactId>oauth2-server</artifactId>
+        </dependency>
+
     </dependencies>
     </dependencies>
 
 
-<!--    <build>-->
-<!--        <plugins>-->
-<!--            <plugin>-->
-<!--                <groupId>org.springframework.boot</groupId>-->
-<!--                <artifactId>spring-boot-maven-plugin</artifactId>-->
-<!--                <configuration>-->
-<!--                    <excludes>-->
-<!--                        <exclude>-->
-<!--                            <groupId>org.projectlombok</groupId>-->
-<!--                            <artifactId>lombok</artifactId>-->
-<!--                        </exclude>-->
-<!--                    </excludes>-->
-<!--                </configuration>-->
-<!--            </plugin>-->
-<!--        </plugins>-->
-<!--    </build>-->
-
-
-        <build>
-
-            <plugins>
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-compiler-plugin</artifactId>
-                    <version>3.0</version>
-                    <configuration>
-                        <source>11</source>
-                        <target>11</target>
-                        <encoding>UTF-8</encoding>
-                    </configuration>
-                </plugin>
-
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-assembly-plugin</artifactId>
-                    <dependencies>
-                        <dependency>
-                            <groupId>cc.iotkit</groupId>
-                            <artifactId>standalone-package</artifactId>
-                            <version>${project.version}</version>
-                        </dependency>
-                    </dependencies>
-                    <configuration>
-                        <appendAssemblyId>false</appendAssemblyId>
-                        <descriptorRefs>
-                            <descriptorRef>standalone-package</descriptorRef>
-                        </descriptorRefs>
-                    </configuration>
-                    <executions>
-                        <execution>
-                            <id>make-assembly</id>
-                            <phase>package</phase>
-                            <goals>
-                                <goal>single</goal>
-                            </goals>
-                        </execution>
-                    </executions>
-                </plugin>
-
-            </plugins>
-        </build>
+    <!--    <build>-->
+    <!--        <plugins>-->
+    <!--            <plugin>-->
+    <!--                <groupId>org.springframework.boot</groupId>-->
+    <!--                <artifactId>spring-boot-maven-plugin</artifactId>-->
+    <!--                <configuration>-->
+    <!--                    <excludes>-->
+    <!--                        <exclude>-->
+    <!--                            <groupId>org.projectlombok</groupId>-->
+    <!--                            <artifactId>lombok</artifactId>-->
+    <!--                        </exclude>-->
+    <!--                    </excludes>-->
+    <!--                </configuration>-->
+    <!--            </plugin>-->
+    <!--        </plugins>-->
+    <!--    </build>-->
+
+
+    <build>
+
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.0</version>
+                <configuration>
+                    <source>11</source>
+                    <target>11</target>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <dependencies>
+                    <dependency>
+                        <groupId>cc.iotkit</groupId>
+                        <artifactId>standalone-package</artifactId>
+                        <version>${project.version}</version>
+                    </dependency>
+                </dependencies>
+                <configuration>
+                    <appendAssemblyId>false</appendAssemblyId>
+                    <descriptorRefs>
+                        <descriptorRef>standalone-package</descriptorRef>
+                    </descriptorRefs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+        </plugins>
+    </build>
 
 
 
 
 </project>
 </project>

+ 11 - 2
manager/src/main/java/cc/iotkit/manager/Application.java

@@ -1,16 +1,25 @@
 package cc.iotkit.manager;
 package cc.iotkit.manager;
 
 
+import cc.iotkit.manager.config.EmbeddedElasticSearchConfig;
+import cc.iotkit.manager.config.EmbeddedRedisConfig;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 
 
 @Slf4j
 @Slf4j
-@EnableFeignClients(basePackages = {"cc.iotkit.deviceapi"})
 @SpringBootApplication(scanBasePackages = {"cc.iotkit"})
 @SpringBootApplication(scanBasePackages = {"cc.iotkit"})
+@EnableWebMvc
 public class Application {
 public class Application {
 
 
     public static void main(String[] args) {
     public static void main(String[] args) {
+        if (EmbeddedElasticSearchConfig.embeddedEnable()) {
+            EmbeddedElasticSearchConfig.startEmbeddedElasticSearch();
+        }
+        if (EmbeddedRedisConfig.embeddedEnable()) {
+            EmbeddedRedisConfig.startEmbeddedRedisServer();
+        }
+
         SpringApplication.run(Application.class, args);
         SpringApplication.run(Application.class, args);
     }
     }
 
 

+ 0 - 0
manager/src/main/java/cc/iotkit/manager/config/AutoBeanConfig.java → manager/src/main/java/cc/iotkit/manager/config/AutoBeanConfig.java1


+ 10 - 0
manager/src/main/java/cc/iotkit/manager/config/CacheConfig.java

@@ -61,6 +61,16 @@ public class CacheConfig {
                         Caffeine.newBuilder()
                         Caffeine.newBuilder()
                                 .expireAfterWrite(5, TimeUnit.MINUTES)
                                 .expireAfterWrite(5, TimeUnit.MINUTES)
                                 .build()
                                 .build()
+                ), new CaffeineCache(
+                        Constants.USER_CACHE,
+                        Caffeine.newBuilder()
+                                .expireAfterWrite(5, TimeUnit.MINUTES)
+                                .build()
+                ), new CaffeineCache(
+                        Constants.OAUTH_CLIENT_CACHE,
+                        Caffeine.newBuilder()
+                                .expireAfterWrite(5, TimeUnit.MINUTES)
+                                .build()
                 )
                 )
         ));
         ));
         return manager;
         return manager;

+ 8 - 20
manager/src/main/java/cc/iotkit/manager/config/ElasticSearchConfig.java → manager/src/main/java/cc/iotkit/manager/config/EmbeddedElasticSearchConfig.java

@@ -1,43 +1,31 @@
 package cc.iotkit.manager.config;
 package cc.iotkit.manager.config;
 
 
 import lombok.SneakyThrows;
 import lombok.SneakyThrows;
-import lombok.extern.slf4j.Slf4j;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.node.InternalSettingsPreparer;
 import org.elasticsearch.node.InternalSettingsPreparer;
 import org.elasticsearch.node.Node;
 import org.elasticsearch.node.Node;
 import org.elasticsearch.transport.Netty4Plugin;
 import org.elasticsearch.transport.Netty4Plugin;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.stereotype.Component;
 
 
 import java.util.Collections;
 import java.util.Collections;
 
 
-@Slf4j
-@Configuration
-public class ElasticSearchConfig {
+public class EmbeddedElasticSearchConfig {
 
 
     static {
     static {
         System.setProperty("es.set.netty.runtime.available.processors", "false");
         System.setProperty("es.set.netty.runtime.available.processors", "false");
     }
     }
 
 
+    public static boolean embeddedEnable() {
+        return "true".equals(System.getProperty("embeddedElasticSearch"));
+    }
+
     @SneakyThrows
     @SneakyThrows
-    @Bean
-    public EmbeddedElasticSearch getEmbeddedElasticSearch(ConfigProperty configProperty) {
-        if (configProperty.enabled) {
-            EmbeddedElasticSearch embeddedElasticSearch = new EmbeddedElasticSearch(configProperty);
-            embeddedElasticSearch.start();
-            return embeddedElasticSearch;
-        }
-        return null;
+    public static void startEmbeddedElasticSearch() {
+        EmbeddedElasticSearch embeddedElasticSearch = new EmbeddedElasticSearch(new ConfigProperty());
+        embeddedElasticSearch.start();
     }
     }
 
 
-    @Component
-    @ConfigurationProperties(prefix = "elasticsearch.embedded")
     public static class ConfigProperty {
     public static class ConfigProperty {
 
 
-        private boolean enabled;
-
         private String dataPath = "./data/elasticsearch";
         private String dataPath = "./data/elasticsearch";
 
 
         private String homePath = "./";
         private String homePath = "./";

+ 16 - 0
manager/src/main/java/cc/iotkit/manager/config/EmbeddedRedisConfig.java

@@ -0,0 +1,16 @@
+package cc.iotkit.manager.config;
+
+import redis.embedded.RedisServer;
+
+public class EmbeddedRedisConfig {
+
+    public static boolean embeddedEnable() {
+        return "true".equals(System.getProperty("embeddedRedisServer"));
+    }
+
+    public static void startEmbeddedRedisServer() {
+        RedisServer redisServer = new RedisServer();
+        redisServer.start();
+    }
+
+}

+ 7 - 1
manager/src/main/java/cc/iotkit/manager/config/GlobalExceptionHandler.java

@@ -1,5 +1,6 @@
 package cc.iotkit.manager.config;
 package cc.iotkit.manager.config;
 
 
+import cn.dev33.satoken.exception.NotLoginException;
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.NoArgsConstructor;
@@ -18,7 +19,12 @@ public class GlobalExceptionHandler {
     @ResponseBody
     @ResponseBody
     public RequestResult handleException(Exception e, HttpServletResponse response) {
     public RequestResult handleException(Exception e, HttpServletResponse response) {
         log.error("handler exception", e);
         log.error("handler exception", e);
-        if(e.getMessage().contains("Unauthorized")){
+        if (e instanceof NotLoginException) {
+            response.setStatus(401);
+            return new RequestResult("401", "未授权的请求");
+        }
+
+        if (e.getMessage().contains("Unauthorized")) {
             response.setStatus(403);
             response.setStatus(403);
             return new RequestResult("403", "没有权限");
             return new RequestResult("403", "没有权限");
         }
         }

+ 0 - 0
manager/src/main/java/cc/iotkit/manager/config/KeycloakSecurityConfig.java → manager/src/main/java/cc/iotkit/manager/config/KeycloakSecurityConfig.java1


+ 4 - 0
manager/src/main/java/cc/iotkit/manager/config/ResponseResultHandler.java

@@ -1,5 +1,6 @@
 package cc.iotkit.manager.config;
 package cc.iotkit.manager.config;
 
 
+import cn.dev33.satoken.util.SaResult;
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.NoArgsConstructor;
@@ -34,6 +35,9 @@ public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
             GlobalExceptionHandler.RequestResult requestResult = (GlobalExceptionHandler.RequestResult) body;
             GlobalExceptionHandler.RequestResult requestResult = (GlobalExceptionHandler.RequestResult) body;
             return new ApiResponse(Integer.parseInt(requestResult.getCode()), requestResult.getMessage(),
             return new ApiResponse(Integer.parseInt(requestResult.getCode()), requestResult.getMessage(),
                     "", System.currentTimeMillis());
                     "", System.currentTimeMillis());
+        } else if (body instanceof SaResult) {
+            SaResult result = (SaResult) body;
+            return new ApiResponse(result.getCode(), result.getMsg(), result.getData(), System.currentTimeMillis());
         } else if (body instanceof Map) {
         } else if (body instanceof Map) {
             Map map = (Map) body;
             Map map = (Map) body;
             //spring mvc内部异常
             //spring mvc内部异常

+ 66 - 0
manager/src/main/java/cc/iotkit/manager/config/SaTokenConfigure.java

@@ -0,0 +1,66 @@
+package cc.iotkit.manager.config;
+
+import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
+import cn.dev33.satoken.interceptor.SaRouteInterceptor;
+import cn.dev33.satoken.router.SaRouter;
+import cn.dev33.satoken.stp.StpUtil;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class SaTokenConfigure implements WebMvcConfigurer {
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        // 注册注解拦截器
+        registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
+        // 注册路由拦截器,自定义认证规则
+        registry.addInterceptor(new SaRouteInterceptor((req, res, handler) -> {
+            System.out.println(req.getRequestPath());
+            // 根据路由划分模块,不同模块不同鉴权
+            SaRouter
+                    //管理员、系统用户角色能使用的功能
+                    .match("/**")
+                    .notMatch("/oauth2/**","/*.png").check(c -> StpUtil.checkRoleOr("iot_admin", "iot_system"))
+                    //需要有可写权限的功能
+                    .match(
+                            "/**/save*/**",
+                            "/**/remove*/**",
+                            "/**/del*/**",
+                            "/**/add*/**",
+                            "/**/clear*/**",
+                            "/**/set*/**",
+                            "/**/set",
+                            "/**/invoke"
+                    ).check(c -> StpUtil.checkPermission("write"))
+                    //管理员、系统、客户端用户角色能使用的功能
+                    .match("/space/addSpace/**",
+                            "/space/saveSpace/**",
+                            "/space/delSpace/**",
+                            "/space/saveHome/**",
+                            "/space/currentHome/**",
+                            "/space/myRecentDevices/**",
+                            "/space/spaces/**",
+                            "/space/myDevices/**",
+                            "/space/findDevice/**",
+                            "/space/addDevice/**",
+                            "/space/saveDevice",
+                            "/space/removeDevice",
+                            "/space/device/*",
+                            "/device/*/consumer/*",
+                            "/device/*/service/property/set",
+                            "/device/*/service/*/invoke"
+                    )
+                    .check(c -> StpUtil.checkRoleOr("iot_admin", "iot_system", "iot_client"))
+            ;
+        })).addPathPatterns("/**")
+                .excludePathPatterns(
+                        "/*.png",
+                        "/oauth2/**", "/*.html",
+                        "/favicon.ico", "/v2/api-docs",
+                        "/webjars/**", "/swagger-resources/**",
+                        "/*.js");
+    }
+
+}

+ 55 - 0
manager/src/main/java/cc/iotkit/manager/config/SecurityConfig.java1

@@ -0,0 +1,55 @@
+package cc.iotkit.manager.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+@Configuration
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Value("${app.systemRole}")
+    private String systemRole;
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+//        super.configure(http);
+        http
+                .authorizeRequests()
+                .antMatchers("/oauth2/**", "/*.html", "/favicon.ico", "/v2/api-docs", "/webjars/**", "/swagger-resources/**", "/*.js").permitAll()
+                .antMatchers("/api/**").hasRole("iot_client_user")
+                .antMatchers("/aligenieDevice/invoke/**").hasRole("iot_client_user")
+                //客户端用户写权限
+                .antMatchers("/space/addSpace/**").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/space/saveSpace/**").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/space/delSpace/**").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/space/saveHome/**").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/space/currentHome/**").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/space/myRecentDevices/**").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/space/spaces/**").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/space/myDevices/**").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/space/findDevice/**").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/space/addDevice/**").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/space/saveDevice").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/space/removeDevice").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/space/device/*").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/device/*/consumer/*").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/device/*/service/property/set").hasAnyRole("iot_write", "iot_client_user")
+                .antMatchers("/device/*/service/*/invoke").hasAnyRole("iot_write", "iot_client_user")
+
+
+                .antMatchers(HttpMethod.DELETE).hasRole("iot_write")
+                .antMatchers(HttpMethod.PUT).hasRole("iot_write")
+                .antMatchers("/**/save*/**").hasRole("iot_write")
+                .antMatchers("/**/remove*/**").hasRole("iot_write")
+                .antMatchers("/**/del*/**").hasRole("iot_write")
+                .antMatchers("/**/add*/**").hasRole("iot_write")
+                .antMatchers("/**/clear*/**").hasRole("iot_write")
+                .antMatchers("/**/set*/**").hasRole("iot_write")
+                .antMatchers("/**/set").hasRole("iot_write")
+                .antMatchers("/**/invoke").hasRole("iot_write")
+                .antMatchers("/**").hasAnyRole(systemRole)
+                .and().csrf().disable();
+    }
+}

+ 3 - 3
manager/src/main/java/cc/iotkit/manager/controller/ProductController.java

@@ -13,6 +13,7 @@ import cc.iotkit.model.product.Category;
 import cc.iotkit.model.product.Product;
 import cc.iotkit.model.product.Product;
 import cc.iotkit.model.product.ProductModel;
 import cc.iotkit.model.product.ProductModel;
 import cc.iotkit.model.product.ThingModel;
 import cc.iotkit.model.product.ThingModel;
+import cn.dev33.satoken.annotation.SaCheckRole;
 import com.aliyun.oss.OSS;
 import com.aliyun.oss.OSS;
 import com.aliyun.oss.OSSClientBuilder;
 import com.aliyun.oss.OSSClientBuilder;
 import com.aliyun.oss.model.PutObjectResult;
 import com.aliyun.oss.model.PutObjectResult;
@@ -23,7 +24,6 @@ import org.springframework.data.domain.Example;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartFile;
 
 
@@ -101,14 +101,14 @@ public class ProductController {
         return categoryRepository.findAll();
         return categoryRepository.findAll();
     }
     }
 
 
-    @PreAuthorize("hasRole('iot_admin')")
+    @SaCheckRole("iot_admin")
     @PostMapping("/saveCategory")
     @PostMapping("/saveCategory")
     public void saveCategory(Category cate) {
     public void saveCategory(Category cate) {
         cate.setCreateAt(System.currentTimeMillis());
         cate.setCreateAt(System.currentTimeMillis());
         categoryRepository.save(cate);
         categoryRepository.save(cate);
     }
     }
 
 
-    @PreAuthorize("hasRole('iot_admin')")
+    @SaCheckRole("iot_admin")
     @PostMapping("/delCategory")
     @PostMapping("/delCategory")
     public void delCategory(String id) {
     public void delCategory(String id) {
         categoryRepository.deleteById(id);
         categoryRepository.deleteById(id);

+ 0 - 2
manager/src/main/java/cc/iotkit/manager/controller/SpaceDeviceController.java

@@ -16,7 +16,6 @@ import cc.iotkit.model.space.SpaceDevice;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Example;
 import org.springframework.data.domain.Example;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -103,7 +102,6 @@ public class SpaceDeviceController {
                 .build();
                 .build();
     }
     }
 
 
-    @PreAuthorize("hasRole('iot_system_user')")
     @GetMapping("/{userId}/devices")
     @GetMapping("/{userId}/devices")
     public List<SpaceDeviceVo> getDevices(@PathVariable("userId") String userId) {
     public List<SpaceDeviceVo> getDevices(@PathVariable("userId") String userId) {
         List<SpaceDevice> spaceDevices = spaceDeviceRepository.findAll(Example.of(SpaceDevice.builder().uid(userId).build()));
         List<SpaceDevice> spaceDevices = spaceDeviceRepository.findAll(Example.of(SpaceDevice.builder().uid(userId).build()));

+ 10 - 22
manager/src/main/java/cc/iotkit/manager/controller/ThirdAuthController.java

@@ -2,17 +2,17 @@ package cc.iotkit.manager.controller;
 
 
 import cc.iotkit.common.Constants;
 import cc.iotkit.common.Constants;
 import cc.iotkit.common.exception.BizException;
 import cc.iotkit.common.exception.BizException;
+import cc.iotkit.common.utils.CodecUtil;
 import cc.iotkit.common.utils.ReflectUtil;
 import cc.iotkit.common.utils.ReflectUtil;
 import cc.iotkit.dao.AligenieDeviceRepository;
 import cc.iotkit.dao.AligenieDeviceRepository;
 import cc.iotkit.dao.UserInfoRepository;
 import cc.iotkit.dao.UserInfoRepository;
 import cc.iotkit.manager.service.DataOwnerService;
 import cc.iotkit.manager.service.DataOwnerService;
-import cc.iotkit.manager.service.KeycloakAdminService;
 import cc.iotkit.manager.service.PulsarAdminService;
 import cc.iotkit.manager.service.PulsarAdminService;
 import cc.iotkit.manager.utils.AuthUtil;
 import cc.iotkit.manager.utils.AuthUtil;
 import cc.iotkit.model.UserInfo;
 import cc.iotkit.model.UserInfo;
+import cn.dev33.satoken.annotation.SaCheckRole;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
 import java.util.*;
 import java.util.*;
@@ -24,8 +24,6 @@ public class UserInfoController {
     @Value("${app.systemRole}")
     @Value("${app.systemRole}")
     private String systemRole;
     private String systemRole;
 
 
-    @Autowired
-    private KeycloakAdminService keycloakAdminService;
     @Autowired
     @Autowired
     private UserInfoRepository userInfoRepository;
     private UserInfoRepository userInfoRepository;
     @Autowired
     @Autowired
@@ -39,7 +37,7 @@ public class UserInfoController {
     /**
     /**
      * 平台用户列表
      * 平台用户列表
      */
      */
-    @PreAuthorize("hasRole('iot_admin')")
+    @SaCheckRole("iot_admin")
     @GetMapping("/platform/users")
     @GetMapping("/platform/users")
     public List<UserInfo> getPlatformUsers() {
     public List<UserInfo> getPlatformUsers() {
         return userInfoRepository.findByType(UserInfo.USER_TYPE_PLATFORM);
         return userInfoRepository.findByType(UserInfo.USER_TYPE_PLATFORM);
@@ -54,18 +52,10 @@ public class UserInfoController {
             user.setId(UUID.randomUUID().toString());
             user.setId(UUID.randomUUID().toString());
             user.setType(UserInfo.USER_TYPE_PLATFORM);
             user.setType(UserInfo.USER_TYPE_PLATFORM);
             user.setOwnerId(AuthUtil.getUserId());
             user.setOwnerId(AuthUtil.getUserId());
-            user.setRoles(Arrays.asList(Constants.ROLE_SYSTEM));
+            user.setRoles(Collections.singletonList(Constants.ROLE_SYSTEM));
+            user.setPermissions(Collections.singletonList(Constants.PERMISSION_WRITE));
             user.setCreateAt(System.currentTimeMillis());
             user.setCreateAt(System.currentTimeMillis());
-            UserInfo keycloakUser = keycloakAdminService.getUser(user.getUid());
-            if (keycloakUser != null) {
-                user.setId(keycloakUser.getId());
-                keycloakAdminService.updateUser(user);
-            } else {
-                keycloakAdminService.createUser(user, Constants.PWD_SYSTEM_USER);
-            }
-            if (!pulsarAdminService.tenantExists(user.getUid())) {
-                pulsarAdminService.createTenant(user.getUid());
-            }
+            user.setSecret(CodecUtil.aesEncrypt(Constants.PWD_SYSTEM_USER, Constants.PWD_SYSTEM_USER));
             userInfoRepository.save(user);
             userInfoRepository.save(user);
         } catch (Throwable e) {
         } catch (Throwable e) {
             throw new BizException("add platform user error", e);
             throw new BizException("add platform user error", e);
@@ -84,25 +74,23 @@ public class UserInfoController {
      * 添加C端用户
      * 添加C端用户
      */
      */
     @PostMapping("/client/user/add")
     @PostMapping("/client/user/add")
-    public void addClientUser(@RequestBody UserInfo user) {
+    public void addClientUser(@RequestBody UserInfo user) throws Exception {
         user.setType(UserInfo.USER_TYPE_CLIENT);
         user.setType(UserInfo.USER_TYPE_CLIENT);
         user.setOwnerId(AuthUtil.getUserId());
         user.setOwnerId(AuthUtil.getUserId());
         user.setRoles(Collections.singletonList(Constants.ROLE_CLIENT));
         user.setRoles(Collections.singletonList(Constants.ROLE_CLIENT));
         user.setCreateAt(System.currentTimeMillis());
         user.setCreateAt(System.currentTimeMillis());
-        String uid = keycloakAdminService.createUser(user, Constants.PWD_CLIENT_USER);
-        user.setId(uid);
+        user.setSecret(CodecUtil.aesEncrypt(Constants.PWD_CLIENT_USER, Constants.ACCOUNT_SECRET));
         userInfoRepository.save(user);
         userInfoRepository.save(user);
     }
     }
 
 
     @PostMapping("/client/user/{id}/delete")
     @PostMapping("/client/user/{id}/delete")
     public void deleteClientUser(@PathVariable("id") String id) {
     public void deleteClientUser(@PathVariable("id") String id) {
         Optional<UserInfo> optUser = userInfoRepository.findById(id);
         Optional<UserInfo> optUser = userInfoRepository.findById(id);
-        if (!optUser.isPresent()) {
+        if (optUser.isEmpty()) {
             throw new BizException("user does not exist");
             throw new BizException("user does not exist");
         }
         }
         UserInfo user = optUser.get();
         UserInfo user = optUser.get();
         ownerService.checkOwner(user);
         ownerService.checkOwner(user);
-        keycloakAdminService.deleteUser(id);
         userInfoRepository.deleteById(id);
         userInfoRepository.deleteById(id);
         aligenieDeviceRepository.deleteByUid(user.getId());
         aligenieDeviceRepository.deleteByUid(user.getId());
     }
     }
@@ -110,7 +98,7 @@ public class UserInfoController {
     @PostMapping("/client/user/save")
     @PostMapping("/client/user/save")
     public void saveClientUser(@RequestBody UserInfo user) {
     public void saveClientUser(@RequestBody UserInfo user) {
         Optional<UserInfo> userOpt = userInfoRepository.findById(user.getId());
         Optional<UserInfo> userOpt = userInfoRepository.findById(user.getId());
-        if (!userOpt.isPresent()) {
+        if (userOpt.isEmpty()) {
             return;
             return;
         }
         }
         UserInfo oldUser = userOpt.get();
         UserInfo oldUser = userOpt.get();

+ 2 - 44
manager/src/main/java/cc/iotkit/manager/controller/aligenie/AligenieDeviceController.java

@@ -1,8 +1,6 @@
 package cc.iotkit.manager.controller.aligenie;
 package cc.iotkit.manager.controller.aligenie;
 
 
-import cc.iotkit.common.Constants;
 import cc.iotkit.common.exception.BizException;
 import cc.iotkit.common.exception.BizException;
-import cc.iotkit.common.utils.UniqueIdUtil;
 import cc.iotkit.dao.*;
 import cc.iotkit.dao.*;
 import cc.iotkit.manager.service.DataOwnerService;
 import cc.iotkit.manager.service.DataOwnerService;
 import cc.iotkit.manager.service.DeviceService;
 import cc.iotkit.manager.service.DeviceService;
@@ -10,21 +8,13 @@ import cc.iotkit.model.UserInfo;
 import cc.iotkit.model.aligenie.AligenieDevice;
 import cc.iotkit.model.aligenie.AligenieDevice;
 import cc.iotkit.model.aligenie.AligenieProduct;
 import cc.iotkit.model.aligenie.AligenieProduct;
 import cc.iotkit.model.device.DeviceInfo;
 import cc.iotkit.model.device.DeviceInfo;
-import cc.iotkit.model.device.message.ThingModelMessage;
 import lombok.Data;
 import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.pulsar.client.api.Producer;
-import org.apache.pulsar.client.api.PulsarClient;
-import org.apache.pulsar.client.api.PulsarClientException;
-import org.apache.pulsar.client.impl.schema.JSONSchema;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
-import javax.annotation.PostConstruct;
-import java.util.HashMap;
 import java.util.List;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.Optional;
 
 
 @Slf4j
 @Slf4j
@@ -46,24 +36,9 @@ public class AligenieDeviceController {
     private DeviceService deviceService;
     private DeviceService deviceService;
     @Autowired
     @Autowired
     private DeviceDao deviceDao;
     private DeviceDao deviceDao;
-    @Value("${app.aligenie.push.device}")
-    private String pushDevice;
     @Value("${pulsar.broker}")
     @Value("${pulsar.broker}")
     private String pulsarBrokerUrl;
     private String pulsarBrokerUrl;
 
 
-    private Producer<ThingModelMessage> deviceMessageProducer;
-
-    @PostConstruct
-    public void init() throws PulsarClientException {
-        //初始化pulsar客户端
-        PulsarClient client = PulsarClient.builder()
-                .serviceUrl(pulsarBrokerUrl)
-                .build();
-        deviceMessageProducer = client.newProducer(JSONSchema.of(ThingModelMessage.class))
-                .topic("persistent://iotkit/default/" + Constants.THING_MODEL_MESSAGE_TOPIC)
-                .create();
-    }
-
     @GetMapping("/list/{uid}")
     @GetMapping("/list/{uid}")
     public List<AligenieDevice> getDevices(@PathVariable("uid") String uid) {
     public List<AligenieDevice> getDevices(@PathVariable("uid") String uid) {
         UserInfo user = userInfoRepository.findById(uid).get();
         UserInfo user = userInfoRepository.findById(uid).get();
@@ -73,9 +48,9 @@ public class AligenieDeviceController {
 
 
     @PostMapping("/bind/{uid}")
     @PostMapping("/bind/{uid}")
     public void bind(@PathVariable("uid") String uid,
     public void bind(@PathVariable("uid") String uid,
-                     @RequestBody List<Device> devices) throws PulsarClientException {
+                     @RequestBody List<Device> devices) {
         Optional<UserInfo> optUser = userInfoRepository.findById(uid);
         Optional<UserInfo> optUser = userInfoRepository.findById(uid);
-        if (!optUser.isPresent()) {
+        if (optUser.isEmpty()) {
             throw new BizException("user does not exist");
             throw new BizException("user does not exist");
         }
         }
         UserInfo user = optUser.get();
         UserInfo user = optUser.get();
@@ -106,23 +81,6 @@ public class AligenieDeviceController {
             deviceDao.updateTag(device.getDeviceId(),
             deviceDao.updateTag(device.getDeviceId(),
                     new DeviceInfo.Tag("aligenie", "天猫精灵接入", "是"));
                     new DeviceInfo.Tag("aligenie", "天猫精灵接入", "是"));
         }
         }
-
-        DeviceInfo deviceInfo = deviceRepository.findByDeviceId(pushDevice);
-        if (deviceInfo == null) {
-            return;
-        }
-
-        Map<String, Object> uidData = new HashMap<>();
-        uidData.put("uid", uid);
-        deviceMessageProducer.send(ThingModelMessage.builder()
-                .deviceId(pushDevice)
-                .productKey(deviceInfo.getProductKey())
-                .deviceName(deviceInfo.getDeviceName())
-                .type(ThingModelMessage.TYPE_EVENT)
-                .identifier("userDevicesChange")
-                .mid(UniqueIdUtil.newRequestId())
-                .data(uidData)
-                .build());
     }
     }
 
 
     @Data
     @Data

+ 0 - 4
manager/src/main/java/cc/iotkit/manager/controller/aligenie/AligenieProductController.java

@@ -1,19 +1,15 @@
 package cc.iotkit.manager.controller.aligenie;
 package cc.iotkit.manager.controller.aligenie;
 
 
 import cc.iotkit.dao.AligenieProductRepository;
 import cc.iotkit.dao.AligenieProductRepository;
-import cc.iotkit.manager.model.aligenie.AligenieProductVo;
 import cc.iotkit.manager.service.DataOwnerService;
 import cc.iotkit.manager.service.DataOwnerService;
 import cc.iotkit.manager.utils.AuthUtil;
 import cc.iotkit.manager.utils.AuthUtil;
 import cc.iotkit.model.aligenie.AligenieProduct;
 import cc.iotkit.model.aligenie.AligenieProduct;
-import cc.iotkit.model.product.Product;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Example;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.bind.annotation.RestController;
 
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 
 
 @RestController
 @RestController

+ 0 - 0
manager/src/main/java/cc/iotkit/manager/service/KeycloakAdminService.java → manager/src/main/java/cc/iotkit/manager/service/KeycloakAdminService.java1


+ 4 - 18
manager/src/main/java/cc/iotkit/manager/utils/AuthUtil.java

@@ -1,33 +1,19 @@
 package cc.iotkit.manager.utils;
 package cc.iotkit.manager.utils;
 
 
 import cc.iotkit.common.Constants;
 import cc.iotkit.common.Constants;
-import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
+import cn.dev33.satoken.stp.StpUtil;
 
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
-import java.util.stream.Collectors;
 
 
 public class AuthUtil {
 public class AuthUtil {
 
 
     public static String getUserId() {
     public static String getUserId() {
-        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-        if (authentication == null) {
-            return "";
-        }
-        if (authentication instanceof KeycloakAuthenticationToken) {
-            KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) authentication;
-            return token.getName();
-        }
-        return "";
+        return StpUtil.getLoginId().toString();
     }
     }
 
 
     public static List<String> getUserRoles() {
     public static List<String> getUserRoles() {
-        return SecurityContextHolder.getContext()
-                .getAuthentication()
-                .getAuthorities()
-                .stream().map((role) -> role.getAuthority().replace("ROLE_", ""))
-                .collect(Collectors.toList());
+        return StpUtil.getRoleList();
     }
     }
 
 
     public static boolean isAdmin() {
     public static boolean isAdmin() {

+ 18 - 22
manager/src/main/resources/application-dev.yml

@@ -1,3 +1,6 @@
+server:
+  port: 8086
+
 spring:
 spring:
   servlet:
   servlet:
     multipart:
     multipart:
@@ -36,28 +39,21 @@ aliyun:
   accessKeyId: 填写阿里云accessKeyId
   accessKeyId: 填写阿里云accessKeyId
   accessKeySecret: 填写阿里云accessKeySecret
   accessKeySecret: 填写阿里云accessKeySecret
 
 
-keycloak:
-  realm : 填写keycloak中定义的realm
-  resource : iotkit
-  auth-server-url : 填写keycloak认证地址
-  ssl-required : external
-  use-resource-role-mappings : false
-  cors-max-age : 1000
-  cors-allowed-methods : POST PUT DELETE GET
-  cors-exposed-headers : WWW-Authenticate
-  bearer-only : false
-  enable-basic-auth : false
-  expose-token : true
-  verify-token-audience : false
-  connection-pool-size : 20
-  disable-trust-manager: true
-  allow-any-hostname : false
-  token-minimum-time-to-live : 10
-  min-time-between-jwks-requests : 10
-
-keycloak-admin-clientid : 填写keycloak中定义的clientId
-keycloak-admin-user : 填写keycloak中添加的管理员用户名
-keycloak-admin-password : 填写keycloak中添加的管理员密码
+sa-token:
+  # token名称 (同时也是cookie名称)
+  token-name: satoken
+  # token有效期,单位s 默认30天, -1代表永不过期
+  timeout: 2592000
+  # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
+  activity-timeout: -1
+  # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
+  is-concurrent: true
+  # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
+  is-share: false
+  # token风格
+  token-style: uuid
+  # 是否输出操作日志
+  is-log: false
 
 
 pulsar:
 pulsar:
   broker: pulsar://pulsar broker地址:6650
   broker: pulsar://pulsar broker地址:6650

+ 19 - 23
manager/src/main/resources/application.yml

@@ -1,3 +1,6 @@
+server:
+  port: 8086
+
 spring:
 spring:
   servlet:
   servlet:
     multipart:
     multipart:
@@ -34,29 +37,22 @@ aliyun:
   accessKeyId: 填写阿里云accessKeyId
   accessKeyId: 填写阿里云accessKeyId
   accessKeySecret: 填写阿里云accessKeySecret
   accessKeySecret: 填写阿里云accessKeySecret
 
 
-keycloak:
-  realm : 填写keycloak中定义的realm
-  resource : iotkit
-  auth-server-url : 填写keycloak认证地址
-  ssl-required : external
-  use-resource-role-mappings : false
-  cors-max-age : 1000
-  cors-allowed-methods : POST PUT DELETE GET
-  cors-exposed-headers : WWW-Authenticate
-  bearer-only : false
-  enable-basic-auth : false
-  expose-token : true
-  verify-token-audience : false
-  connection-pool-size : 20
-  disable-trust-manager: true
-  allow-any-hostname : false
-  token-minimum-time-to-live : 10
-  min-time-between-jwks-requests : 10
-
-keycloak-admin-clientid : 填写keycloak中定义的clientId
-keycloak-admin-user : 填写keycloak中添加的管理员用户名
-keycloak-admin-password : 填写keycloak中添加的管理员密码
-
+sa-token:
+  # token名称 (同时也是cookie名称)
+  token-name: satoken
+  # token有效期,单位s 默认30天, -1代表永不过期
+  timeout: 2592000
+  # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
+  activity-timeout: -1
+  # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
+  is-concurrent: true
+  # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
+  is-share: false
+  # token风格
+  token-style: uuid
+  # 是否输出操作日志
+  is-log: false
+  
 pulsar:
 pulsar:
   broker: pulsar://pulsar broker地址:6650
   broker: pulsar://pulsar broker地址:6650
   service: http://pulsar 服务地址:8080
   service: http://pulsar 服务地址:8080

+ 2 - 2
model/pom.xml

@@ -5,9 +5,9 @@
     <parent>
     <parent>
         <artifactId>iotkit-parent</artifactId>
         <artifactId>iotkit-parent</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
-    <version>0.1.0-SNAPSHOT</version>
+    <version>0.2.0-SNAPSHOT</version>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 
     <artifactId>model</artifactId>
     <artifactId>model</artifactId>

+ 25 - 0
model/src/main/java/cc/iotkit/model/OauthClient.java

@@ -0,0 +1,25 @@
+package cc.iotkit.model;
+
+import lombok.Data;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+/**
+ * oauth2的client
+ */
+@Data
+@Document
+public class OauthClient {
+
+    @Id
+    private String clientId;
+
+    private String name;
+
+    private String clientSecret;
+
+    private String allowUrl;
+
+    private Long createAt;
+
+}

+ 14 - 0
model/src/main/java/cc/iotkit/model/Role.java

@@ -0,0 +1,14 @@
+package cc.iotkit.model;
+
+import lombok.Data;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Data
+@Document
+public class Role {
+
+    private String id;
+
+    private String name;
+
+}

+ 11 - 1
model/src/main/java/cc/iotkit/model/ThirdUserSession.java

@@ -35,6 +35,11 @@ public class UserInfo implements Owned {
      */
      */
     private String ownerId;
     private String ownerId;
 
 
+    /**
+     * 密钥(密码加密后的内容)
+     */
+    private String secret;
+
     /**
     /**
      * 用户昵称
      * 用户昵称
      */
      */
@@ -69,7 +74,12 @@ public class UserInfo implements Owned {
     /**
     /**
      * 角色
      * 角色
      */
      */
-    private List<String> roles;
+    private List<String> roles = new ArrayList<>();
+
+    /**
+     * 权限
+     */
+    private List<String> permissions = new ArrayList<>();
 
 
     /**
     /**
      * 用户使用的平台
      * 用户使用的平台

+ 53 - 0
oauth2-server/pom.xml

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         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">
+    <parent>
+        <artifactId>iotkit-parent</artifactId>
+        <groupId>cc.iotkit</groupId>
+        <version>0.2.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>oauth2-server</artifactId>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.dev33</groupId>
+            <artifactId>sa-token-oauth2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.dev33</groupId>
+            <artifactId>sa-token-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.dev33</groupId>
+            <artifactId>sa-token-dao-redis-jackson</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.ejlchina</groupId>
+            <artifactId>okhttps</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cc.iotkit</groupId>
+            <artifactId>dao</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 131 - 0
oauth2-server/src/main/java/cc/iotkit/oauth/controller/AuthClientController.java

@@ -0,0 +1,131 @@
+package cc.iotkit.oauth.controller;
+
+import cc.iotkit.common.Constants;
+import cc.iotkit.common.utils.CodecUtil;
+import cc.iotkit.dao.OauthClientCache;
+import cc.iotkit.dao.UserInfoCache;
+import cc.iotkit.model.OauthClient;
+import cc.iotkit.model.UserInfo;
+import cc.iotkit.utils.SoMap;
+import cn.dev33.satoken.stp.SaLoginConfig;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.util.SaResult;
+import com.ejlchina.okhttps.OkHttps;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.view.RedirectView;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Slf4j
+@RestController
+@RequestMapping("/oauth2")
+public class AuthClientController {
+
+    @Value("${oauth2.auth-server-url}")
+    private String serverUrl;
+
+    @Autowired
+    private OauthClientCache oauthClientCache;
+    @Autowired
+    private UserInfoCache userInfoCache;
+
+    // 进入首页
+    @RequestMapping("/")
+    public Object index(HttpServletRequest request) {
+        request.setAttribute("uid", StpUtil.getLoginIdDefaultNull());
+        return new ModelAndView("index.html");
+    }
+
+    // 根据Code码进行登录,获取 Access-Token 和 openid
+    @RequestMapping("/codeLogin")
+    public SaResult codeLogin(String code, String clientId) {
+        OauthClient oauthClient = oauthClientCache.getClient(clientId);
+        if (oauthClient == null) {
+            return SaResult.error("clientId does not exist");
+        }
+        String clientSecret = oauthClient.getClientSecret();
+
+        // 调用Server端接口,获取 Access-Token 以及其他信息
+        String str = OkHttps.sync(serverUrl + "/oauth2/token")
+                .addBodyPara("grant_type", "authorization_code")
+                .addBodyPara("code", code)
+                .addBodyPara("client_id", clientId)
+                .addBodyPara("client_secret", clientSecret)
+                .post()
+                .getBody()
+                .toString();
+        SoMap so = SoMap.getSoMap().setJsonString(str);
+        log.info("get token by code result:{}", so);
+        // code不等于200  代表请求失败
+        if (so.getInt("code") != 200) {
+            return SaResult.error(so.getString("msg"));
+        }
+
+        // 根据openid获取其对应的userId
+        SoMap data = so.getMap("data");
+        String uid = getUserIdByOpenid(data.getString("openid"));
+        String access_token = data.getString("access_token");
+        UserInfo userInfo = userInfoCache.getUserInfo(uid);
+        data.put("name", userInfo.getNickName());
+        data.put("uid", uid);
+
+        // 返回相关参数
+        StpUtil.login(uid, SaLoginConfig.setToken(access_token));
+        return SaResult.data(data);
+    }
+
+    // 注销登录
+    @RequestMapping("/logout")
+    public RedirectView logout(String accessToken, String redirect_uri) {
+        //先注销client中cookie的token
+        StpUtil.logout();
+        //再注销web页面使用的token
+        StpUtil.logoutByTokenValue(accessToken);
+
+        return new RedirectView(redirect_uri);
+    }
+
+    // 根据 Access-Token 置换相关的资源: 获取账号昵称、头像、性别等信息
+    @RequestMapping("/getUserinfo")
+    public SaResult getUserinfo(String accessToken) {
+        // 调用Server端接口,查询开放的资源
+        String str = OkHttps.sync(serverUrl + "/oauth2/userinfo")
+                .addBodyPara("access_token", accessToken)
+                .post()
+                .getBody()
+                .toString();
+        SoMap so = SoMap.getSoMap().setJsonString(str);
+        // code不等于200  代表请求失败
+        if (so.getInt("code") != 200) {
+            return SaResult.error(so.getString("msg"));
+        }
+
+        // 返回相关参数 (data=获取到的资源 )
+        SoMap data = so.getMap("data");
+        return SaResult.data(data);
+    }
+
+    @GetMapping("/checkLogin")
+    public SaResult checkLogin() {
+        try {
+            StpUtil.checkLogin();
+        } catch (Throwable e) {
+            return SaResult.error("no login");
+        }
+        return SaResult.ok();
+    }
+
+    @SneakyThrows
+    private String getUserIdByOpenid(String openid) {
+        String clientIdLoginId = CodecUtil.aesDecrypt(openid, Constants.ACCOUNT_SECRET);
+        return clientIdLoginId.split(":")[1];
+    }
+
+}

+ 97 - 0
oauth2-server/src/main/java/cc/iotkit/oauth/controller/AuthServerController.java

@@ -0,0 +1,97 @@
+package cc.iotkit.oauth.controller;
+
+import cc.iotkit.common.Constants;
+import cc.iotkit.common.utils.CodecUtil;
+import cc.iotkit.dao.UserInfoRepository;
+import cc.iotkit.model.UserInfo;
+import cc.iotkit.oauth.service.TokenRequestHandler;
+import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
+import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.util.SaResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.ModelAndView;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+@RestController
+public class AuthServerController {
+
+    @Autowired
+    private UserInfoRepository userInfoRepository;
+
+    // 处理所有OAuth相关请求
+    @RequestMapping("/oauth2/*")
+    public Object request() {
+        return TokenRequestHandler.serverRequest();
+    }
+
+    // Sa-OAuth2 定制化配置
+    @Autowired
+    public void setSaOAuth2Config(SaOAuth2Config cfg) {
+        cfg.
+                // 未登录的视图
+                        setNotLoginView(() -> new ModelAndView("login.html")).
+                // 登录处理函数
+                        setDoLoginHandle((name, pwd) -> {
+                    try {
+                        UserInfo userInfo = userInfoRepository.findByUid(name);
+                        if (userInfo != null) {
+                            String secret = userInfo.getSecret();
+                            String encodePwd = CodecUtil.aesEncrypt(pwd, Constants.ACCOUNT_SECRET);
+                            if (encodePwd.equals(secret)) {
+                                StpUtil.login(userInfo.getId(), "PC");
+                                return SaResult.ok();
+                            }
+                        }
+                    } catch (Throwable e) {
+                        return SaResult.error("账号名或密码错误");
+                    }
+                    return SaResult.error("账号名或密码错误");
+                }).
+                // 授权确认视图
+                        setConfirmView((clientId, scope) -> {
+                    Map<String, Object> map = new HashMap<>();
+                    map.put("clientId", clientId);
+                    map.put("scope", scope);
+                    return new ModelAndView("confirm.html", map);
+                })
+        ;
+    }
+
+    // 全局异常拦截
+    @ExceptionHandler
+    public SaResult handlerException(Exception e) {
+        e.printStackTrace();
+        return SaResult.error(e.getMessage());
+    }
+
+    // ---------- 开放相关资源接口: Client端根据 Access-Token ,置换相关资源 ------------
+
+    // 获取Userinfo信息:昵称、头像、性别等等
+    @RequestMapping("/oauth2/userinfo")
+    public SaResult userinfo() {
+        // 获取 Access-Token 对应的账号id
+        String accessToken = SaHolder.getRequest().getParamNotNull("access_token");
+        Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken);
+        System.out.println("-------- 此Access-Token对应的账号id: " + loginId);
+
+        // 校验 Access-Token 是否具有权限: userinfo
+        SaOAuth2Util.checkScope(accessToken, "userinfo");
+
+        // 模拟账号信息 (真实环境需要查询数据库获取信息)
+        Map<String, Object> map = new LinkedHashMap<>();
+        map.put("nickname", "shengzhang_");
+        map.put("avatar", "http://xxx.com/1.jpg");
+        map.put("age", "18");
+        map.put("sex", "男");
+        map.put("address", "山东省 青岛市 城阳区");
+        return SaResult.data(map);
+    }
+}

+ 51 - 0
oauth2-server/src/main/java/cc/iotkit/oauth/service/SaOAuth2TemplateImpl.java

@@ -0,0 +1,51 @@
+package cc.iotkit.oauth.service;
+
+import cc.iotkit.common.Constants;
+import cc.iotkit.common.utils.CodecUtil;
+import cc.iotkit.dao.OauthClientCache;
+import cc.iotkit.model.OauthClient;
+import cn.dev33.satoken.oauth2.logic.SaOAuth2Template;
+import cn.dev33.satoken.oauth2.model.SaClientModel;
+import cn.dev33.satoken.stp.StpUtil;
+import lombok.SneakyThrows;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SaOAuth2TemplateImpl extends SaOAuth2Template {
+
+    @Autowired
+    private OauthClientCache oauthClientCache;
+
+    // 根据 id 获取 Client 信息
+    @Override
+    public SaClientModel getClientModel(String clientId) {
+        OauthClient client = oauthClientCache.getClient(clientId);
+        if (client == null) {
+            return null;
+        }
+
+
+        return new SaClientModel()
+                .setClientId(client.getClientId())
+                .setClientSecret(client.getClientSecret())
+                .setAllowUrl(client.getAllowUrl())
+                .setContractScope("userinfo")
+                .setIsAutoMode(true);
+    }
+
+    // 根据ClientId 和 LoginId 获取openid
+    @SneakyThrows
+    @Override
+    public String getOpenid(String clientId, Object loginId) {
+        // 此为模拟数据,真实环境需要从数据库查询
+        return CodecUtil.aesEncrypt(clientId + ":" + loginId, Constants.ACCOUNT_SECRET);
+    }
+
+    @Override
+    public String randomAccessToken(String clientId, Object loginId, String scope) {
+        return StpUtil.createLoginSession(loginId);
+    }
+
+
+}

+ 38 - 0
oauth2-server/src/main/java/cc/iotkit/oauth/service/StpInterfaceImpl.java

@@ -0,0 +1,38 @@
+package cc.iotkit.oauth.service;
+
+import cc.iotkit.dao.UserInfoCache;
+import cc.iotkit.model.UserInfo;
+import cn.dev33.satoken.stp.StpInterface;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class StpInterfaceImpl implements StpInterface {
+
+    @Autowired
+    private UserInfoCache userInfoCache;
+
+    /**
+     * 返回一个账号所拥有的权限码集合
+     */
+    @Override
+    public List<String> getPermissionList(Object loginId, String loginType) {
+        // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
+        List<String> list = new ArrayList<String>();
+        list.add("write");
+        return list;
+    }
+
+    /**
+     * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
+     */
+    @Override
+    public List<String> getRoleList(Object loginId, String loginType) {
+        UserInfo userInfo = userInfoCache.getUserInfo(loginId.toString());
+        return userInfo.getRoles();
+    }
+
+}

+ 62 - 0
oauth2-server/src/main/java/cc/iotkit/oauth/service/TokenRequestHandler.java

@@ -0,0 +1,62 @@
+package cc.iotkit.oauth.service;
+
+import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.context.model.SaRequest;
+import cn.dev33.satoken.context.model.SaResponse;
+import cn.dev33.satoken.oauth2.SaOAuth2Manager;
+import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
+import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
+import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts;
+import cn.dev33.satoken.oauth2.logic.SaOAuth2Handle;
+import cn.dev33.satoken.oauth2.model.SaClientModel;
+
+public class TokenRequestHandler {
+
+    public static Object serverRequest() {
+        SaRequest req = SaHolder.getRequest();
+        SaResponse res = SaHolder.getResponse();
+        SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
+        SaClientModel cm;
+        if (req.isPath(SaOAuth2Consts.Api.authorize) && req.isParam(SaOAuth2Consts.Param.response_type, SaOAuth2Consts.ResponseType.code)) {
+            cm = SaOAuth2Handle.currClientModel();
+            if (!cfg.getIsCode() || !cm.isCode && !cm.isAutoMode) {
+                throw new SaOAuth2Exception("暂未开放的授权模式");
+            } else {
+                return SaOAuth2Handle.authorize(req, res, cfg);
+            }
+        } else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.authorization_code)) {
+            return SaOAuth2Handle.token(req, res, cfg);
+        } else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.refresh_token)) {
+            return SaOAuth2Handle.refreshToken(req);
+        } else if (req.isPath(SaOAuth2Consts.Api.revoke)) {
+            return SaOAuth2Handle.revokeToken(req);
+        } else if (req.isPath(SaOAuth2Consts.Api.doLogin)) {
+            return SaOAuth2Handle.doLogin(req, res, cfg);
+        } else if (req.isPath(SaOAuth2Consts.Api.doConfirm)) {
+            return SaOAuth2Handle.doConfirm(req);
+        } else if (req.isPath(SaOAuth2Consts.Api.authorize) && req.isParam(SaOAuth2Consts.Param.response_type, SaOAuth2Consts.ResponseType.token)) {
+            cm = SaOAuth2Handle.currClientModel();
+            if (!cfg.getIsImplicit() || !cm.isImplicit && !cm.isAutoMode) {
+                throw new SaOAuth2Exception("暂未开放的授权模式");
+            } else {
+                return SaOAuth2Handle.authorize(req, res, cfg);
+            }
+        } else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.password)) {
+            cm = SaOAuth2Handle.currClientModel();
+            if (!cfg.getIsPassword() || !cm.isPassword && !cm.isAutoMode) {
+                throw new SaOAuth2Exception("暂未开放的授权模式");
+            } else {
+                return SaOAuth2Handle.password(req, res, cfg);
+            }
+        } else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.client_credentials)) {
+            cm = SaOAuth2Handle.currClientModel();
+            if (!cfg.getIsClient() || !cm.isClient && !cm.isAutoMode) {
+                throw new SaOAuth2Exception("暂未开放的授权模式");
+            } else {
+                return SaOAuth2Handle.clientToken(req, res, cfg);
+            }
+        } else {
+            return "{\"msg\": \"not handle\"}";
+        }
+    }
+}

+ 737 - 0
oauth2-server/src/main/java/cc/iotkit/utils/SoMap.java

@@ -0,0 +1,737 @@
+package cc.iotkit.utils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Map< String, Object> 是最常用的一种Map类型,但是它写着麻烦 
+ * <p>所以特封装此类,继承Map,进行一些扩展,可以让Map更灵活使用 
+ * <p>最新:2020-12-10 新增部分构造方法
+ * @author kong
+ */
+public class SoMap extends LinkedHashMap<String, Object> {
+
+	private static final long serialVersionUID = 1L;
+
+	public SoMap() {
+	}
+	
+	/** 以下元素会在isNull函数中被判定为Null, */
+	public static final Object[] NULL_ELEMENT_ARRAY = {null, ""};
+	public static final List<Object> NULL_ELEMENT_LIST;
+
+	static {
+		NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY);
+	}
+
+	// ============================= 读值 =============================
+
+	/** 获取一个值 */
+	@Override
+	public Object get(Object key) {
+		if("this".equals(key)) {
+			return this;
+		}
+		return super.get(key);
+	}
+
+	/** 如果为空,则返回默认值 */
+	public Object get(Object key, Object defaultValue) {
+		Object value = get(key);
+		if(valueIsNull(value)) {
+			return defaultValue;
+		}
+		return value;
+	}
+	
+	/** 转为String并返回 */
+	public String getString(String key) {
+		Object value = get(key);
+		if(value == null) {
+			return null;
+		}
+		return String.valueOf(value);
+	}
+
+	/** 如果为空,则返回默认值 */
+	public String getString(String key, String defaultValue) {
+		Object value = get(key);
+		if(valueIsNull(value)) {
+			return defaultValue;
+		}
+		return String.valueOf(value);
+	}
+
+	/** 转为int并返回 */
+	public int getInt(String key) {
+		Object value = get(key);
+		if(valueIsNull(value)) {
+			return 0;
+		}
+		return Integer.valueOf(String.valueOf(value));
+	}
+	/** 转为int并返回,同时指定默认值 */
+	public int getInt(String key, int defaultValue) {
+		Object value = get(key);
+		if(valueIsNull(value)) {
+			return defaultValue;
+		}
+		return Integer.valueOf(String.valueOf(value));
+	}
+
+	/** 转为long并返回 */
+	public long getLong(String key) {
+		Object value = get(key);
+		if(valueIsNull(value)) {
+			return 0;
+		}
+		return Long.valueOf(String.valueOf(value));
+	}
+
+	/** 转为double并返回 */
+	public double getDouble(String key) {
+		Object value = get(key);
+		if(valueIsNull(value)) {
+			return 0.0;
+		}
+		return Double.valueOf(String.valueOf(value));
+	}
+
+	/** 转为boolean并返回 */
+	public boolean getBoolean(String key) {
+		Object value = get(key);
+		if(valueIsNull(value)) {
+			return false;
+		}
+		return Boolean.valueOf(String.valueOf(value));
+	}
+
+	/** 转为Date并返回,根据自定义格式 */
+	public Date getDateByFormat(String key, String format) {
+		try {
+			return new SimpleDateFormat(format).parse(getString(key));
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/** 转为Date并返回,根据格式: yyyy-MM-dd */
+	public Date getDate(String key) {
+		return getDateByFormat(key, "yyyy-MM-dd");
+	}
+
+	/** 转为Date并返回,根据格式: yyyy-MM-dd HH:mm:ss */
+	public Date getDateTime(String key) {
+		return getDateByFormat(key, "yyyy-MM-dd HH:mm:ss");
+	}
+
+	/** 转为Map并返回 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public SoMap getMap(String key) {
+		Object value = get(key);
+		if(value == null) {
+			return SoMap.getSoMap();
+		}
+		if(value instanceof Map) {
+			return SoMap.getSoMap((Map)value);
+		}
+		if(value instanceof String) {
+			return SoMap.getSoMap().setJsonString((String)value);
+		}
+		throw new RuntimeException("值无法转化为SoMap: " + value);
+	}
+
+	/** 获取集合(必须原先就是个集合,否则会创建个新集合并返回) */
+	@SuppressWarnings("unchecked")
+	public List<Object> getList(String key) {
+		Object value = get(key);
+		List<Object> list = null;
+		if(value == null || value.equals("")) {
+			list = new ArrayList<Object>();
+		}
+		else if(value instanceof List) {
+			list = (List<Object>)value;
+		} else {
+			list = new ArrayList<Object>();
+			list.add(value);
+		}
+		return list;
+	}
+
+	/** 获取集合 (指定泛型类型) */
+	public <T> List<T> getList(String key, Class<T> cs) {
+		List<Object> list = getList(key);
+		List<T> list2 = new ArrayList<T>();
+		for (Object obj : list) {
+			T objC = getValueByClass(obj, cs);
+			list2.add(objC);
+		}
+		return list2;
+	}
+
+	/** 获取集合(逗号分隔式),(指定类型) */
+	public <T> List<T> getListByComma(String key, Class<T> cs) {
+		String listStr = getString(key);
+		if(listStr == null || listStr.equals("")) {
+			return new ArrayList<>();
+		}
+		// 开始转化
+		String [] arr = listStr.split(",");
+		List<T> list = new ArrayList<T>();
+		for (String str : arr) {
+			if(cs == int.class || cs == Integer.class || cs == long.class || cs == Long.class) {
+				str = str.trim();
+			}
+			T objC = getValueByClass(str, cs);
+			list.add(objC);
+		}
+		return list;
+	}
+
+
+	/** 根据指定类型从map中取值,返回实体对象 */
+	public <T> T getModel(Class<T> cs) {
+		try {
+			return getModelByObject(cs.newInstance());
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+	/** 从map中取值,塞到一个对象中 */
+	public <T> T getModelByObject(T obj) {
+		// 获取类型 
+		Class<?> cs = obj.getClass();
+		// 循环复制  
+		for (Field field : cs.getDeclaredFields()) {
+			try {
+				// 获取对象 
+				Object value = this.get(field.getName());	
+				if(value == null) {
+					continue;
+				}
+				field.setAccessible(true);	
+				Object valueConvert = getValueByClass(value, field.getType());
+				field.set(obj, valueConvert);
+			} catch (IllegalArgumentException | IllegalAccessException e) {
+				throw new RuntimeException("属性取值出错:" + field.getName(), e);
+			}
+		}
+		return obj;
+	}
+
+	
+
+	/**
+	 * 将指定值转化为指定类型并返回
+	 * @param obj
+	 * @param cs
+	 * @param <T>
+	 * @return
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> T getValueByClass(Object obj, Class<T> cs) {
+		String obj2 = String.valueOf(obj);
+		Object obj3 = null;
+		if (cs.equals(String.class)) {
+			obj3 = obj2;
+		} else if (cs.equals(int.class) || cs.equals(Integer.class)) {
+			obj3 = Integer.valueOf(obj2);
+		} else if (cs.equals(long.class) || cs.equals(Long.class)) {
+			obj3 = Long.valueOf(obj2);
+		} else if (cs.equals(short.class) || cs.equals(Short.class)) {
+			obj3 = Short.valueOf(obj2);
+		} else if (cs.equals(byte.class) || cs.equals(Byte.class)) {
+			obj3 = Byte.valueOf(obj2);
+		} else if (cs.equals(float.class) || cs.equals(Float.class)) {
+			obj3 = Float.valueOf(obj2);
+		} else if (cs.equals(double.class) || cs.equals(Double.class)) {
+			obj3 = Double.valueOf(obj2);
+		} else if (cs.equals(boolean.class) || cs.equals(Boolean.class)) {
+			obj3 = Boolean.valueOf(obj2);
+		} else {
+			obj3 = (T)obj;
+		}
+		return (T)obj3;
+	}
+
+	
+	// ============================= 写值 =============================
+
+	/**
+	 * 给指定key添加一个默认值(只有在这个key原来无值的情况先才会set进去)
+	 */
+	public void setDefaultValue(String key, Object defaultValue) {
+		if(isNull(key)) {
+			set(key, defaultValue);
+		}
+	}
+
+	/** set一个值,连缀风格 */
+	public SoMap set(String key, Object value) {
+		// 防止敏感key 
+		if(key.toLowerCase().equals("this")) {		
+			return this;
+		}
+		put(key, value);
+		return this;
+	}
+
+	/** 将一个Map塞进SoMap */
+	public SoMap setMap(Map<String, ?> map) {
+		if(map != null) {
+			for (String key : map.keySet()) {
+				this.set(key, map.get(key));
+			}
+		}
+		return this;
+	}
+
+	/** 将一个对象解析塞进SoMap */
+	public SoMap setModel(Object model) {
+		if(model == null) {
+			return this;
+		}
+		Field[] fields = model.getClass().getDeclaredFields();
+	    for (Field field : fields) {
+	        try{
+	            field.setAccessible(true);
+	            boolean isStatic = Modifier.isStatic(field.getModifiers());
+	            if(!isStatic) {
+		            this.set(field.getName(), field.get(model));
+	            }
+	        }catch (Exception e){
+	        	throw new RuntimeException(e);
+	        }
+	    }
+		return this;
+	}
+
+	/** 将json字符串解析后塞进SoMap */
+	public SoMap setJsonString(String jsonString) {
+		try {
+			@SuppressWarnings("unchecked")
+			Map<String, Object> map = new ObjectMapper().readValue(jsonString, Map.class);
+			return this.setMap(map);
+		} catch (JsonProcessingException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	
+	// ============================= 删值 =============================
+
+	/** delete一个值,连缀风格 */
+	public SoMap delete(String key) {
+		remove(key);
+		return this;
+	}
+
+	/** 清理所有value为null的字段 */
+	public SoMap clearNull() {
+		Iterator<String> iterator = this.keySet().iterator();
+		while(iterator.hasNext()) {
+			String key = iterator.next();
+			if(this.isNull(key)) {
+				iterator.remove();
+				this.remove(key);
+			}
+
+		}
+		return this;
+	}
+	/** 清理指定key */
+	public SoMap clearIn(String ...keys) {
+		List<String> keys2 = Arrays.asList(keys);
+		Iterator<String> iterator = this.keySet().iterator();
+		while(iterator.hasNext()) {
+			String key = iterator.next();
+			if(keys2.contains(key) == true) {
+				iterator.remove();
+				this.remove(key);
+			}
+		}
+		return this;
+	}
+	/** 清理掉不在列表中的key */
+	public SoMap clearNotIn(String ...keys) {
+		List<String> keys2 = Arrays.asList(keys);
+		Iterator<String> iterator = this.keySet().iterator();
+		while(iterator.hasNext()) {
+			String key = iterator.next();
+			if(keys2.contains(key) == false) {
+				iterator.remove();
+				this.remove(key);
+			}
+
+		}
+		return this;
+	}
+	/** 清理掉所有key */
+	public SoMap clearAll() {
+		clear();
+		return this;
+	}
+	
+
+	// ============================= 快速构建 ============================= 
+
+	/** 构建一个SoMap并返回 */
+	public static SoMap getSoMap() {
+		return new SoMap();
+	}
+	/** 构建一个SoMap并返回 */
+	public static SoMap getSoMap(String key, Object value) {
+		return new SoMap().set(key, value);
+	}
+	/** 构建一个SoMap并返回 */
+	public static SoMap getSoMap(Map<String, ?> map) {
+		return new SoMap().setMap(map);
+	}
+
+	/** 将一个对象集合解析成为SoMap */
+	public static SoMap getSoMapByModel(Object model) {
+		return SoMap.getSoMap().setModel(model);
+	}
+	
+	/** 将一个对象集合解析成为SoMap集合 */
+	public static List<SoMap> getSoMapByList(List<?> list) {
+		List<SoMap> listMap = new ArrayList<SoMap>();
+		for (Object model : list) {
+			listMap.add(getSoMapByModel(model));
+		}
+		return listMap;
+	}
+	
+	/** 克隆指定key,返回一个新的SoMap */
+	public SoMap cloneKeys(String... keys) {
+		SoMap so = new SoMap();
+		for (String key : keys) {
+			so.set(key, this.get(key));
+		}
+		return so;
+	}
+	/** 克隆所有key,返回一个新的SoMap */
+	public SoMap cloneSoMap() {
+		SoMap so = new SoMap();
+		for (String key : this.keySet()) {
+			so.set(key, this.get(key));
+		}
+		return so;
+	}
+
+	/** 将所有key转为大写 */
+	public SoMap toUpperCase() {
+		SoMap so = new SoMap();
+		for (String key : this.keySet()) {
+			so.set(key.toUpperCase(), this.get(key));
+		}
+		this.clearAll().setMap(so);
+		return this;
+	}
+	/** 将所有key转为小写 */
+	public SoMap toLowerCase() {
+		SoMap so = new SoMap();
+		for (String key : this.keySet()) {
+			so.set(key.toLowerCase(), this.get(key));
+		}
+		this.clearAll().setMap(so);
+		return this;
+	}
+	/** 将所有key中下划线转为中划线模式 (kebab-case风格) */
+	public SoMap toKebabCase() {
+		SoMap so = new SoMap();
+		for (String key : this.keySet()) {
+			so.set(wordEachKebabCase(key), this.get(key));
+		}
+		this.clearAll().setMap(so);
+		return this;
+	}
+	/** 将所有key中下划线转为小驼峰模式 */
+	public SoMap toHumpCase() {
+		SoMap so = new SoMap();
+		for (String key : this.keySet()) {
+			so.set(wordEachBigFs(key), this.get(key));
+		}
+		this.clearAll().setMap(so);
+		return this;
+	}
+	/** 将所有key中小驼峰转为下划线模式 */
+	public SoMap humpToLineCase() {
+		SoMap so = new SoMap();
+		for (String key : this.keySet()) {
+			so.set(wordHumpToLine(key), this.get(key));
+		}
+		this.clearAll().setMap(so);
+		return this;
+	}
+	
+	
+	
+	
+	// ============================= 辅助方法 =============================
+
+
+	/** 指定key是否为null,判定标准为 NULL_ELEMENT_ARRAY 中的元素  */
+	public boolean isNull(String key) {
+		return valueIsNull(get(key));
+	}
+
+	/** 指定key列表中是否包含value为null的元素,只要有一个为null,就会返回true */
+	public boolean isContainNull(String ...keys) {
+		for (String key : keys) {
+			if(this.isNull(key)) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/** 与isNull()相反 */
+	public boolean isNotNull(String key) {
+		return !isNull(key);
+	}
+	/** 指定key的value是否为null,作用同isNotNull() */
+	public boolean has(String key) {
+		return !isNull(key);
+	}
+	
+	/** 指定value在此SoMap的判断标准中是否为null */
+	public boolean valueIsNull(Object value) {
+		return NULL_ELEMENT_LIST.contains(value);
+	}
+	
+	/** 验证指定key不为空,为空则抛出异常 */
+	public SoMap checkNull(String ...keys) {
+		for (String key : keys) {
+			if(this.isNull(key)) {
+				throw new RuntimeException("参数" + key + "不能为空");
+			}
+		}
+		return this;
+	}
+
+	static Pattern patternNumber = Pattern.compile("[0-9]*");
+	/** 指定key是否为数字 */
+	public boolean isNumber(String key) {
+		String value = getString(key);
+		if(value == null) {
+			return false;
+		}
+	    return patternNumber.matcher(value).matches();   
+	}
+
+	
+	
+	
+	/**
+	 * 转为JSON字符串
+	 */
+	public String toJsonString() {
+		try {
+//			SoMap so = SoMap.getSoMap(this);
+			return new ObjectMapper().writeValueAsString(this);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+//	/**
+//	 * 转为JSON字符串, 带格式的 
+//	 */
+//	public String toJsonFormatString() {
+//		try {
+//			return JSON.toJSONString(this, true); 
+//		} catch (Exception e) {
+//			throw new RuntimeException(e);
+//		}
+//	}
+
+	// ============================= web辅助 =============================
+
+
+	/**
+	 * 返回当前request请求的的所有参数 
+	 * @return
+	 */
+	public static SoMap getRequestSoMap() {
+		// 大善人SpringMVC提供的封装 
+		ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+		if(servletRequestAttributes == null) {
+			throw new RuntimeException("当前线程非JavaWeb环境");
+		}
+		// 当前request
+		HttpServletRequest request = servletRequestAttributes.getRequest(); 
+		if (request.getAttribute("currentSoMap") == null || request.getAttribute("currentSoMap") instanceof SoMap == false ) {
+			initRequestSoMap(request);
+		}
+		return (SoMap)request.getAttribute("currentSoMap");
+	}
+
+	/** 初始化当前request的 SoMap */
+	private static void initRequestSoMap(HttpServletRequest request) {
+		SoMap soMap = new SoMap();
+		Map<String, String[]> parameterMap = request.getParameterMap();	// 获取所有参数 
+		for (String key : parameterMap.keySet()) {
+			try {
+				String[] values = parameterMap.get(key); // 获得values 
+				if(values.length == 1) {
+					soMap.set(key, values[0]);
+				} else {
+					List<String> list = new ArrayList<String>();
+					for (String v : values) {
+						list.add(v);
+					}
+					soMap.set(key, list);
+				}
+			} catch (Exception e) {
+				throw new RuntimeException(e);
+			}
+		}
+		request.setAttribute("currentSoMap", soMap);
+	}
+	
+	/**
+	 * 验证返回当前线程是否为JavaWeb环境 
+	 * @return
+	 */
+	public static boolean isJavaWeb() {
+		ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装 
+		if(servletRequestAttributes == null) {
+			return false;
+		}
+		return true;
+	}
+	
+
+
+	// ============================= 常见key (以下key经常用,所以封装以下,方便写代码) =============================
+
+	/** get 当前页  */
+	public int getKeyPageNo() {
+		int pageNo = getInt("pageNo", 1);
+		if(pageNo <= 0) {
+			pageNo = 1;
+		}
+		return pageNo;
+	}
+	/** get 页大小  */
+	public int getKeyPageSize() {
+		int pageSize = getInt("pageSize", 10);
+		if(pageSize <= 0 || pageSize > 1000) {
+			pageSize = 10;
+		}
+		return pageSize;
+	}
+
+	/** get 排序方式 */
+	public int getKeySortType() {
+		return getInt("sortType");
+	}
+
+
+
+
+	
+
+	// ============================= 工具方法 =============================
+	
+
+	/**
+	 * 将一个一维集合转换为树形集合 
+	 * @param list         集合
+	 * @param idKey        id标识key
+	 * @param parentIdKey  父id标识key
+	 * @param childListKey 子节点标识key
+	 * @return 转换后的tree集合 
+	 */
+	public static List<SoMap> listToTree(List<SoMap> list, String idKey, String parentIdKey, String childListKey) {
+		// 声明新的集合,存储tree形数据 
+		List<SoMap> newTreeList = new ArrayList<SoMap>();
+		// 声明hash-Map,方便查找数据 
+		SoMap hash = new SoMap();
+		// 将数组转为Object的形式,key为数组中的id 
+		for (int i = 0; i < list.size(); i++) {
+			SoMap json = (SoMap) list.get(i);
+			hash.put(json.getString(idKey), json);
+		}
+		// 遍历结果集
+		for (int j = 0; j < list.size(); j++) {
+			// 单条记录
+			SoMap aVal = (SoMap) list.get(j);
+			// 在hash中取出key为单条记录中pid的值
+			SoMap hashVp = (SoMap) hash.get(aVal.get(parentIdKey, "").toString());
+			// 如果记录的pid存在,则说明它有父节点,将她添加到孩子节点的集合中
+			if (hashVp != null) {
+				// 检查是否有child属性,有则添加,没有则新建 
+				if (hashVp.get(childListKey) != null) {
+					@SuppressWarnings("unchecked")
+					List<SoMap> ch = (List<SoMap>) hashVp.get(childListKey);
+					ch.add(aVal);
+					hashVp.put(childListKey, ch);
+				} else {
+					List<SoMap> ch = new ArrayList<SoMap>();
+					ch.add(aVal);
+					hashVp.put(childListKey, ch);
+				}
+			} else {
+				newTreeList.add(aVal);
+			}
+		}
+		return newTreeList;
+	}
+	
+	
+
+	/** 指定字符串的字符串下划线转大写模式 */
+	private static String wordEachBig(String str){
+		String newStr = "";
+		for (String s : str.split("_")) {
+			newStr += wordFirstBig(s);
+		}
+		return newStr;
+	}
+	/** 返回下划线转小驼峰形式 */
+	private static String wordEachBigFs(String str){
+		return wordFirstSmall(wordEachBig(str));
+	}
+
+	/** 将指定单词首字母大写 */
+	private static String wordFirstBig(String str) {
+		return str.substring(0, 1).toUpperCase() + str.substring(1, str.length());
+	}
+
+	/** 将指定单词首字母小写 */
+	private static String wordFirstSmall(String str) {
+		return str.substring(0, 1).toLowerCase() + str.substring(1, str.length());
+	}
+
+	/** 下划线转中划线 */
+	private static String wordEachKebabCase(String str) {
+		return str.replaceAll("_", "-");
+	}
+
+	/** 驼峰转下划线  */
+	private static String wordHumpToLine(String str) {
+		return str.replaceAll("[A-Z]", "_$0").toLowerCase();
+	}
+	
+
+}

+ 117 - 0
oauth2-server/src/main/resources/templates/confirm.html

@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta charset="utf-8">
+		<title>奇特物联认证授权</title>
+		<style type="text/css">
+			body{
+				min-height: 500px;
+			    background: linear-gradient( 270deg, rgb(46 124 255) 0%, rgb(52 3 65) 100% );
+			 }
+			.bgimg{
+				position:absolute;
+				background-image:url(http://iotkit-img.oss-cn-shenzhen.aliyuncs.com/product/KdJYpTp5ywNhmrmC/cover.png?Expires=1968261336&OSSAccessKeyId=LTAI5t8UFEH5eGrBUS5zSiof&Signature=df%2F6JEcxBlXitSNIENPMYJlRE8Y%3D);
+				background-size: 100% 100%;
+				width:60%;
+				height:60%;
+				margin-top:10%;
+			}
+			*{margin: 0px; padding: 0px;}
+			.login-box{
+				position: absolute;
+				right: 100px;
+				width: 400px;
+				height: 400px;
+				margin: 20vh auto;
+				padding: 30px 50px;
+				background-color: #fff;
+				overflow: inherit;
+				border-radius: 10px;
+			}
+			.login-box .title{color:#333;font-size:22px;text-align:center;margin:20px 20px}
+			.login-box button{padding: 5px 15px; cursor: pointer; }
+		</style>
+	</head>
+	<body>
+		<div class="bgimg">
+		</div>
+		<div class="login-box">
+			<div class="title">奇特物联授权确认</div> <br>
+			<div>
+				正在确认授权...
+			</div>
+			<div id="divFailed"></div>
+		</div>
+		<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
+		<script>window.jQuery || alert('当前页面CDN服务商已宕机,请将所有js包更换为本地依赖')</script>
+		<script type="text/javascript">
+			// 同意授权
+			function yes() {
+				$.ajax({
+					url: '/oauth2/doConfirm',
+					data: {
+						client_id: getParam('client_id'),
+						scope: getParam('scope')
+					},
+					dataType: 'json',
+					success: function(res) {
+						if(res.code == 200) {
+								location.reload(true);
+						} else {
+							// 重定向至授权失败URL 
+							$("#divFailed").text('授权失败!');
+						}
+					},
+					error: function(e) {
+						console.log('error');
+					}
+				});
+			}
+			
+			// 拒绝授权
+			function no() {
+				var url = joinParam(getParam('redirect_uri'), "handle=refuse&msg=用户拒绝了授权");
+				location.href = url;
+			}
+			
+			// 从url中查询到指定名称的参数值 
+			function getParam(name, defaultValue){
+				var query = window.location.search.substring(1);
+				var vars = query.split("&");
+				for (var i=0;i<vars.length;i++) {
+					var pair = vars[i].split("=");
+					if(pair[0] == name){return pair[1];}
+				}
+				return(defaultValue == undefined ? null : defaultValue);
+			}
+			
+			// 在url上拼接上kv参数并返回 
+			function joinParam(url, parameStr) {
+				if(parameStr == null || parameStr.length == 0) {
+					return url;
+				}
+				var index = url.indexOf('?');
+				// ? 不存在
+				if(index == -1) {
+					return url + '?' + parameStr;
+				}
+				// ? 是最后一位
+				if(index == url.length - 1) {
+					return url + parameStr;
+				}
+				// ? 是其中一位
+				if(index > -1 && index < url.length - 1) {
+					// 如果最后一位是 不是&, 且 parameStr 第一位不是 &, 就增送一个 &
+					if(url.lastIndexOf('&') != url.length - 1 && parameStrindexOf('&') != 0) {
+						return url + '&' + parameStr;
+					} else {
+						return url + parameStr;
+					}
+				}
+			}
+
+			yes();
+						
+		</script>
+	</body>
+</html>

+ 123 - 0
oauth2-server/src/main/resources/templates/login.html

@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta charset="utf-8">
+		<title>奇特物联登录授权</title>
+		<style type="text/css">
+			body{
+				min-height: 500px;
+			    background: linear-gradient( 270deg, rgb(46 124 255) 0%, rgb(52 3 65) 100% );
+			}
+			.bgimg{
+				position:absolute;
+				background-image:url(http://iotkit-img.oss-cn-shenzhen.aliyuncs.com/product/KdJYpTp5ywNhmrmC/cover.png?Expires=1968261336&OSSAccessKeyId=LTAI5t8UFEH5eGrBUS5zSiof&Signature=df%2F6JEcxBlXitSNIENPMYJlRE8Y%3D);
+				background-size: 100% 100%;
+				width:60%;
+				height:60%;
+				margin-top:10%;
+			}
+			*{margin: 0px; padding: 0px;}
+			.login-box{
+				position: absolute;
+				right: 100px;
+				width: 400px;
+				height: 400px;
+				margin: 20vh auto;
+				padding: 30px 50px;
+				background-color: #fff;
+				overflow: inherit;
+				border-radius: 10px;
+			}
+			.login-box .title{color:#333;font-size:22px;text-align:center;margin:20px 20px}
+			.login-box .form{list-style:none; padding:20px 20px;}
+			.login-box li{
+				padding:10px 10px;
+				line-height:30px;
+				text-align:center;
+			}
+			.login-box input{
+				width:100%;
+				line-height: 30px;
+				margin-bottom: 10px;
+				border-radius: 5px;
+				border: 1px solid #999;
+				padding:5px 10px;
+			}
+			.login-box button{
+				padding: 5px 15px;
+				cursor: pointer;
+				width: 100%;
+				line-height:30px;
+				border: 1px solid #5e77ff;
+				background-color: #5e77ff;
+				color: #fff;
+				border-radius: 5px;
+			}
+			.login-box button:hover{
+				background-color: #2746f1;
+			}
+			.login-box .tip{
+				margin-top:20px;
+				color:#666;
+			}
+			.tip.error{
+				color:red;
+			}
+		</style>
+	</head>
+	<body>
+		<div class="bgimg">
+		</div>
+		<div class="login-box">
+			<div class="title">奇特物联登录授权</div>
+			<ul class="form">
+				<li>
+					<input name="name" placeholder="请输入账号"/>
+				</li>
+				<li>
+					<input name="pwd" type="password" placeholder="请输入密码" /> <br>
+				</li>
+				<li>
+					<button onclick="doLogin()">登录</button>
+				</li>
+				<li class="tip" id="liTip"></li>
+				<li style="color:#aaa;">
+					Copyright © 2021 - 2022 奇特物联
+				</li>
+			</ul>
+		</div>
+		<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
+		<script>window.jQuery || alert('当前页面CDN服务商已宕机,请将所有js包更换为本地依赖')</script>
+		<script type="text/javascript">
+			// 登录方法
+			function doLogin() {
+				var name=$.trim($('[name=name]').val());
+				var pwd=$.trim($('[name=pwd]').val());
+				if(!name || !pwd){
+					$("#liTip").addClass("error").text("账号或密码不能为空!");
+				}
+				$.ajax({
+					url: '/oauth2/doLogin',
+					data: {
+						name: name,
+						pwd: pwd
+					},
+					dataType: 'json', 
+					success: function(res) {
+						if(res.code == 200) {
+							$("#liTip").removeClass("error").text("登录成功!");
+							setTimeout(function() {
+								location.reload(true);
+							}, 800);
+						} else {
+							$("#liTip").addClass("error").text(res.msg);
+						}
+					},
+					error: function(e) {
+						console.log('error');
+					}
+				});
+			}
+		</script>
+	</body>
+</html>

+ 36 - 11
pom.xml

@@ -11,6 +11,7 @@
         <module>dao</module>
         <module>dao</module>
         <module>protocol-gateway</module>
         <module>protocol-gateway</module>
         <module>standalone-package</module>
         <module>standalone-package</module>
+        <module>oauth2-server</module>
     </modules>
     </modules>
     <parent>
     <parent>
         <groupId>org.springframework.boot</groupId>
         <groupId>org.springframework.boot</groupId>
@@ -21,13 +22,13 @@
 
 
     <groupId>cc.iotkit</groupId>
     <groupId>cc.iotkit</groupId>
     <artifactId>iotkit-parent</artifactId>
     <artifactId>iotkit-parent</artifactId>
-    <version>0.1.0-SNAPSHOT</version>
+    <version>0.2.0-SNAPSHOT</version>
     <name>iotkit-parent</name>
     <name>iotkit-parent</name>
     <description>iotkit parent</description>
     <description>iotkit parent</description>
     <properties>
     <properties>
         <java.version>11</java.version>
         <java.version>11</java.version>
-        <keycloak-spring.version>17.0.0</keycloak-spring.version>
         <vertx.version>4.2.2</vertx.version>
         <vertx.version>4.2.2</vertx.version>
+        <sa-token.version>1.30.0</sa-token.version>
     </properties>
     </properties>
 
 
     <dependencyManagement>
     <dependencyManagement>
@@ -96,21 +97,21 @@
             </dependency>
             </dependency>
 
 
             <dependency>
             <dependency>
-                <groupId>org.keycloak</groupId>
-                <artifactId>keycloak-spring-boot-starter</artifactId>
-                <version>${keycloak-spring.version}</version>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-oauth2</artifactId>
+                <version>${sa-token.version}</version>
             </dependency>
             </dependency>
 
 
             <dependency>
             <dependency>
-                <groupId>org.keycloak</groupId>
-                <artifactId>keycloak-spring-security-adapter</artifactId>
-                <version>${keycloak-spring.version}</version>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-spring-boot-starter</artifactId>
+                <version>${sa-token.version}</version>
             </dependency>
             </dependency>
 
 
             <dependency>
             <dependency>
-                <groupId>org.keycloak</groupId>
-                <artifactId>keycloak-admin-client</artifactId>
-                <version>${keycloak-spring.version}</version>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-dao-redis-jackson</artifactId>
+                <version>${sa-token.version}</version>
             </dependency>
             </dependency>
 
 
             <dependency>
             <dependency>
@@ -179,6 +180,24 @@
                 <version>3.0.1</version>
                 <version>3.0.1</version>
             </dependency>
             </dependency>
 
 
+            <dependency>
+                <groupId>it.ozimov</groupId>
+                <artifactId>embedded-redis</artifactId>
+                <version>0.7.3</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.logging.log4j</groupId>
+                <artifactId>log4j-slf4j-impl</artifactId>
+                <version>2.17.1</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.ejlchina</groupId>
+                <artifactId>okhttps</artifactId>
+                <version>3.1.1</version>
+            </dependency>
+
             <dependency>
             <dependency>
                 <groupId>io.vertx</groupId>
                 <groupId>io.vertx</groupId>
                 <artifactId>vertx-core</artifactId>
                 <artifactId>vertx-core</artifactId>
@@ -257,6 +276,12 @@
                 <version>${project.version}</version>
                 <version>${project.version}</version>
             </dependency>
             </dependency>
 
 
+            <dependency>
+                <groupId>cc.iotkit</groupId>
+                <artifactId>oauth2-server</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
         </dependencies>
         </dependencies>
     </dependencyManagement>
     </dependencyManagement>
 
 

+ 1 - 1
protocol-gateway/component-server/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>protocol-gateway</artifactId>
         <artifactId>protocol-gateway</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 1 - 1
protocol-gateway/component/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>protocol-gateway</artifactId>
         <artifactId>protocol-gateway</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 1 - 1
protocol-gateway/converter/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>protocol-gateway</artifactId>
         <artifactId>protocol-gateway</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 4 - 4
protocol-gateway/emqx-component/dependency-reduced-pom.xml

@@ -3,7 +3,7 @@
   <parent>
   <parent>
     <artifactId>protocol-gateway</artifactId>
     <artifactId>protocol-gateway</artifactId>
     <groupId>cc.iotkit</groupId>
     <groupId>cc.iotkit</groupId>
-    <version>0.1.0-SNAPSHOT</version>
+    <version>0.2.0-SNAPSHOT</version>
   </parent>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>emqx-component</artifactId>
   <artifactId>emqx-component</artifactId>
@@ -61,19 +61,19 @@
     <dependency>
     <dependency>
       <groupId>cc.iotkit</groupId>
       <groupId>cc.iotkit</groupId>
       <artifactId>model</artifactId>
       <artifactId>model</artifactId>
-      <version>0.1.0-SNAPSHOT</version>
+      <version>0.2.0-SNAPSHOT</version>
       <scope>compile</scope>
       <scope>compile</scope>
     </dependency>
     </dependency>
     <dependency>
     <dependency>
       <groupId>cc.iotkit</groupId>
       <groupId>cc.iotkit</groupId>
       <artifactId>common</artifactId>
       <artifactId>common</artifactId>
-      <version>0.1.0-SNAPSHOT</version>
+      <version>0.2.0-SNAPSHOT</version>
       <scope>compile</scope>
       <scope>compile</scope>
     </dependency>
     </dependency>
     <dependency>
     <dependency>
       <groupId>cc.iotkit</groupId>
       <groupId>cc.iotkit</groupId>
       <artifactId>component</artifactId>
       <artifactId>component</artifactId>
-      <version>0.1.0-SNAPSHOT</version>
+      <version>0.2.0-SNAPSHOT</version>
       <scope>compile</scope>
       <scope>compile</scope>
     </dependency>
     </dependency>
   </dependencies>
   </dependencies>

+ 1 - 1
protocol-gateway/emqx-component/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>protocol-gateway</artifactId>
         <artifactId>protocol-gateway</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 2 - 2
protocol-gateway/http-biz-component/dependency-reduced-pom.xml

@@ -3,7 +3,7 @@
   <parent>
   <parent>
     <artifactId>protocol-gateway</artifactId>
     <artifactId>protocol-gateway</artifactId>
     <groupId>cc.iotkit</groupId>
     <groupId>cc.iotkit</groupId>
-    <version>0.1.0-SNAPSHOT</version>
+    <version>0.2.0-SNAPSHOT</version>
   </parent>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>http-biz-component</artifactId>
   <artifactId>http-biz-component</artifactId>
@@ -58,7 +58,7 @@
     <dependency>
     <dependency>
       <groupId>cc.iotkit</groupId>
       <groupId>cc.iotkit</groupId>
       <artifactId>component</artifactId>
       <artifactId>component</artifactId>
-      <version>0.1.0-SNAPSHOT</version>
+      <version>0.2.0-SNAPSHOT</version>
       <scope>compile</scope>
       <scope>compile</scope>
     </dependency>
     </dependency>
   </dependencies>
   </dependencies>

+ 1 - 1
protocol-gateway/http-biz-component/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>protocol-gateway</artifactId>
         <artifactId>protocol-gateway</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 1 - 1
protocol-gateway/mqtt-client-simulator/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>protocol-gateway</artifactId>
         <artifactId>protocol-gateway</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
     <description>mqtt客户端模拟器</description>
     <description>mqtt客户端模拟器</description>

+ 2 - 2
protocol-gateway/mqtt-client-simulator/src/main/java/cc/iotkit/simulator/Application.java

@@ -11,8 +11,8 @@ public class Application {
     public static void main(String[] args) throws IOException {
     public static void main(String[] args) throws IOException {
 
 
         if (args.length == 0) {
         if (args.length == 0) {
-//            Mqtt.broker = "tcp://127.0.0.1:1883";
-            Mqtt.broker = "tcp://120.76.96.206:1883";
+            Mqtt.broker = "tcp://127.0.0.1:1883";
+//            Mqtt.broker = "tcp://120.76.96.206:1883";
         } else {
         } else {
             Mqtt.broker = args[0];
             Mqtt.broker = args[0];
         }
         }

+ 4 - 4
protocol-gateway/mqtt-component/dependency-reduced-pom.xml

@@ -3,7 +3,7 @@
   <parent>
   <parent>
     <artifactId>protocol-gateway</artifactId>
     <artifactId>protocol-gateway</artifactId>
     <groupId>cc.iotkit</groupId>
     <groupId>cc.iotkit</groupId>
-    <version>0.1.0-SNAPSHOT</version>
+    <version>0.2.0-SNAPSHOT</version>
   </parent>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>mqtt-component</artifactId>
   <artifactId>mqtt-component</artifactId>
@@ -80,19 +80,19 @@
     <dependency>
     <dependency>
       <groupId>cc.iotkit</groupId>
       <groupId>cc.iotkit</groupId>
       <artifactId>common</artifactId>
       <artifactId>common</artifactId>
-      <version>0.1.0-SNAPSHOT</version>
+      <version>0.2.0-SNAPSHOT</version>
       <scope>compile</scope>
       <scope>compile</scope>
     </dependency>
     </dependency>
     <dependency>
     <dependency>
       <groupId>cc.iotkit</groupId>
       <groupId>cc.iotkit</groupId>
       <artifactId>component</artifactId>
       <artifactId>component</artifactId>
-      <version>0.1.0-SNAPSHOT</version>
+      <version>0.2.0-SNAPSHOT</version>
       <scope>compile</scope>
       <scope>compile</scope>
     </dependency>
     </dependency>
     <dependency>
     <dependency>
       <groupId>cc.iotkit</groupId>
       <groupId>cc.iotkit</groupId>
       <artifactId>dao</artifactId>
       <artifactId>dao</artifactId>
-      <version>0.1.0-SNAPSHOT</version>
+      <version>0.2.0-SNAPSHOT</version>
       <scope>compile</scope>
       <scope>compile</scope>
     </dependency>
     </dependency>
   </dependencies>
   </dependencies>

+ 1 - 1
protocol-gateway/mqtt-component/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>protocol-gateway</artifactId>
         <artifactId>protocol-gateway</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 1 - 1
protocol-gateway/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>iotkit-parent</artifactId>
         <artifactId>iotkit-parent</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 1 - 1
rule-engine/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>iotkit-parent</artifactId>
         <artifactId>iotkit-parent</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
 
 

+ 1 - 1
standalone-package/pom.xml

@@ -5,7 +5,7 @@
     <parent>
     <parent>
         <artifactId>iotkit-parent</artifactId>
         <artifactId>iotkit-parent</artifactId>
         <groupId>cc.iotkit</groupId>
         <groupId>cc.iotkit</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>