From 9d960ed0058f9087f49e9741a9af06c3f9116eb0 Mon Sep 17 00:00:00 2001
From: baoshiwei <baoshiwei@shlanbao.cn>
Date: 星期六, 19 四月 2025 11:23:02 +0800
Subject: [PATCH] feat(auth): 添加 Keycloak 认证支持

---
 zhitan-system/src/main/java/com/zhitan/model/service/impl/EnergyIndexServiceImpl.java             |    7 
 zhitan-admin/pom.xml                                                                              |    6 
 zhitan-admin/src/main/java/com/zhitan/web/controller/system/SysLoginController.java               |   35 +
 zhitan-framework/src/main/java/com/zhitan/framework/security/handle/LogoutSuccessHandlerImpl.java |   31 +
 zhitan-vue/src/views/login.vue                                                                    |   36 
 zhitan-system/src/main/java/com/zhitan/system/service/impl/SysSocialServiceImpl.java              |   82 ++
 zhitan-admin/src/main/resources/application-dev.yml                                               |   12 
 zhitan-framework/src/main/java/com/zhitan/framework/web/service/SysLoginService.java              |   77 ++
 zhitan-admin/src/main/java/com/zhitan/web/controller/model/EnergyIndexController.java             |   17 
 zhitan-common/src/main/java/com/zhitan/common/config/keycloak/AuthKeycloakRequest.java            |  118 +++
 zhitan-common/src/main/java/com/zhitan/common/config/keycloak/SocialLoginConfigProperties.java    |   81 ++
 zhitan-framework/src/main/java/com/zhitan/framework/config/SecurityConfig.java                    |    2 
 zhitan-vue/vite.config.js                                                                         |    4 
 zhitan-system/src/main/java/com/zhitan/system/domain/vo/SysSocialVo.java                          |  139 ++++
 zhitan-system/src/main/resources/mapper/model/EnergyIndexMapper.xml                               |    5 
 zhitan-common/src/main/java/com/zhitan/common/utils/JsonUtils.java                                |  170 +++++
 zhitan-system/src/main/java/com/zhitan/system/domain/SysSocial.java                               |  133 ++++
 zhitan-vue/.env.development                                                                       |    2 
 zhitan-common/pom.xml                                                                             |   33 +
 zhitan-system/src/main/java/com/zhitan/model/service/IEnergyIndexService.java                     |    6 
 zhitan-vue/src/views/modelconfiguration/collectindicator/collectIndicator.vue                     |    1 
 zhitan-admin/src/main/resources/application.yml                                                   |   14 
 zhitan-framework/pom.xml                                                                          |    6 
 zhitan-vue/src/api/types.ts                                                                       |   59 +
 zhitan-common/src/main/java/com/zhitan/common/utils/spring/SpringUtils.java                       |   12 
 zhitan-common/src/main/java/com/zhitan/common/config/keycloak/AuthRedisStateCache.java            |   62 ++
 zhitan-common/src/main/java/com/zhitan/common/utils/SpringUtils.java                              |   63 ++
 zhitan-system/src/main/java/com/zhitan/system/domain/bo/SysSocialBo.java                          |  133 ++++
 zhitan-vue/src/utils/request.js                                                                   |    1 
 zhitan-vue/src/api/login.js                                                                       |   22 
 zhitan-common/src/main/java/com/zhitan/common/config/keycloak/AuthKeycloakSource.java             |   36 +
 zhitan-system/src/main/java/com/zhitan/model/mapper/EnergyIndexMapper.java                        |    2 
 zhitan-vue/src/layout/components/SocialCallback/index.vue                                         |   97 +++
 zhitan-common/src/main/java/com/zhitan/common/utils/MapstructUtils.java                           |   94 +++
 zhitan-common/src/main/java/com/zhitan/common/utils/SocialUtils.java                              |   47 +
 zhitan-common/src/main/java/com/zhitan/common/core/domain/model/LoginBody.java                    |   41 +
 zhitan-system/src/main/java/com/zhitan/system/mapper/SysSocialMapper.java                         |   14 
 zhitan-vue/src/permission.js                                                                      |    2 
 zhitan-vue/src/router/index.js                                                                    |    5 
 zhitan-system/src/main/java/com/zhitan/system/service/ISysSocialService.java                      |   35 +
 40 files changed, 1,714 insertions(+), 28 deletions(-)

diff --git a/zhitan-admin/pom.xml b/zhitan-admin/pom.xml
index 3690e33..d5c71d9 100644
--- a/zhitan-admin/pom.xml
+++ b/zhitan-admin/pom.xml
@@ -65,6 +65,12 @@
             <scope>system</scope>
             <systemPath>${basedir}/lib/fel.jar</systemPath>
         </dependency>
+        <dependency>
+            <groupId>me.zhyd.oauth</groupId>
+            <artifactId>JustAuth</artifactId>
+            <version>1.16.7</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/zhitan-admin/src/main/java/com/zhitan/web/controller/model/EnergyIndexController.java b/zhitan-admin/src/main/java/com/zhitan/web/controller/model/EnergyIndexController.java
index 2ec479d..f18caeb 100644
--- a/zhitan-admin/src/main/java/com/zhitan/web/controller/model/EnergyIndexController.java
+++ b/zhitan-admin/src/main/java/com/zhitan/web/controller/model/EnergyIndexController.java
@@ -134,10 +134,17 @@
         .filter(f -> StringUtils.isBlank(f.getMeterId()))
         .map(EnergyIndex::getIndexId)
         .collect(Collectors.toList());
+    /**
+     * 澶勭悊鑳芥簮鎸囨爣鍒楄〃骞剁敓鎴愰渶瑕佺Щ闄ょ殑鎸囨爣ID闆嗗悎
+     * 1. 杩囨护鍑簃eterId闈炵┖鐨勮兘婧愭寚鏍囧璞�
+     * 2. 鎻愬彇杩欎簺瀵硅薄鐨刬ndexId瀛楁
+     * 3. 灏嗘彁鍙栫殑鎸囨爣ID鏀堕泦鍒板瓧绗︿覆闆嗗悎涓�
+     */
     List<String> removeLink = energyIndexList.stream()
         .filter(f -> StringUtils.isNotBlank(f.getMeterId()))
         .map(EnergyIndex::getIndexId)
         .collect(Collectors.toList());
+
     if (!removeLink.isEmpty()) {
       energyIndexService.removeNodeIndex(nodeId, removeLink);
     }
@@ -148,6 +155,16 @@
     return AjaxResult.success();
   }
 
+  /**
+   * 鏂板閫氳繃id鍒犻櫎閲囬泦鐐规帴鍙�
+   */
+  @PreAuthorize("@ss.hasPermi('energyindex:energyindex:remove')")
+  @Log(title = "鎸囨爣淇℃伅", businessType = BusinessType.DELETE)
+  @DeleteMapping("/{indexId}")
+  public AjaxResult deleteCollectIndex(@PathVariable String indexId) {
+    return toAjax(energyIndexService.deleteByIndexId(indexId));
+  }
+
   @Log(title = "澧炲姞璁¢噺鍣ㄥ叿閲囬泦鐐�", businessType = BusinessType.INSERT)
   @PostMapping("/meterIndex/{meterId}")
   public AjaxResult addCollectIndex(@PathVariable("meterId") String meterId) {
diff --git a/zhitan-admin/src/main/java/com/zhitan/web/controller/system/SysLoginController.java b/zhitan-admin/src/main/java/com/zhitan/web/controller/system/SysLoginController.java
index 3313afc..5465075 100644
--- a/zhitan-admin/src/main/java/com/zhitan/web/controller/system/SysLoginController.java
+++ b/zhitan-admin/src/main/java/com/zhitan/web/controller/system/SysLoginController.java
@@ -1,12 +1,18 @@
 package com.zhitan.web.controller.system;
 
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import javax.annotation.Resource;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RestController;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.util.ObjectUtil;
+import com.zhitan.common.utils.SocialUtils;
+import me.zhyd.oauth.request.AuthRequest;
+import me.zhyd.oauth.utils.AuthStateUtils;
+import org.springframework.web.bind.annotation.*;
 import com.zhitan.common.constant.Constants;
 import com.zhitan.common.core.domain.AjaxResult;
 import com.zhitan.common.core.domain.entity.SysMenu;
@@ -45,11 +51,17 @@
     {
         AjaxResult ajax = AjaxResult.success();
         // 鐢熸垚浠ょ墝
-        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
+        String token = "";
+        if (loginBody.getGrantType() != null && !"".equals(loginBody.getGrantType())) {
+            token = loginService.loginByCode(loginBody.getSocialCode(), loginBody.getSocialState());
+        } else {
+            token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
                 loginBody.getUuid());
+        }
         ajax.put(Constants.TOKEN, token);
         return ajax;
     }
+
 
     /**
      * 鑾峰彇鐢ㄦ埛淇℃伅
@@ -83,4 +95,17 @@
         List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
         return AjaxResult.success(menuService.buildMenus(menus));
     }
+    /**
+     * 鑾峰彇璺宠浆URL
+     *
+     * @return 缁撴灉
+     */
+    @GetMapping("/binding")
+    public AjaxResult authBinding() {
+
+        AuthRequest authRequest = SocialUtils.getAuthKeyloakRequest();
+        String authorizeUrl = authRequest.authorize(Base64.encode(AuthStateUtils.createState(), StandardCharsets.UTF_8));
+        return AjaxResult.success("鎿嶄綔鎴愬姛", authorizeUrl);
+    }
+
 }
diff --git a/zhitan-admin/src/main/resources/application-dev.yml b/zhitan-admin/src/main/resources/application-dev.yml
index fbf552a..187f8b3 100644
--- a/zhitan-admin/src/main/resources/application-dev.yml
+++ b/zhitan-admin/src/main/resources/application-dev.yml
@@ -6,8 +6,8 @@
     druid:
       # 涓诲簱鏁版嵁婧�
       master:
-        url: jdbc:postgresql://127.0.0.1:5432/energy
-        username: root
+        url: jdbc:postgresql://127.0.0.1:5432/postgres
+        username: postgres
         password: 123456
       # 浠庡簱鏁版嵁婧�
       slave:
@@ -59,3 +59,11 @@
         wall:
           config:
             multi-statement-allow: true
+keycloak:
+      # keycloak 鏈嶅姟鍣ㄥ湴鍧�
+  server-url: https://lanbaosystem.shlanbao.cn:8443
+  realm: lanbao
+  client-id: DataCapture
+  client-secret: kplisa4lJHEIM6knqefVbxln85QbA5NX
+  redirect-uri: http://192.168.12.236:80/social-callback
+  scopes: [openid, email, phone, profile]
\ No newline at end of file
diff --git a/zhitan-admin/src/main/resources/application.yml b/zhitan-admin/src/main/resources/application.yml
index ef4fb4a..91a7f23 100644
--- a/zhitan-admin/src/main/resources/application.yml
+++ b/zhitan-admin/src/main/resources/application.yml
@@ -142,24 +142,24 @@
     # 韪㈠嚭涔嬪墠鐧诲綍鐨�/涔嬪悗鐧诲綍鐨勭敤鎴凤紝榛樿韪㈠嚭涔嬪墠鐧诲綍鐨勭敤鎴�
     kickoutAfter: false
 rtdb:
-  host: http://127.0.0.1:8086
-  token: ==
-  org: org
-  bucket: bucket
+  host: http://127.0.0.1:8086  # 鐢ㄦ埛鍚峳oot 瀵嗙爜12345678
+  token: AminQagYp5rjb09mFPYvriK0T0vlF-zmwboqtUzdcq3nkXNuhnEpMuG_Ht5vtfWC4xBIVOThvoxy5reTer9XcQ==
+  org: lanbao
+  bucket: nygl
   measurement: data
 
 ###################### MQTT #################################
 mqtt:
   # 鏈嶅姟鍣ㄥ湴鍧�
-  host: tcp://127.0.0.1:1883
+  host: tcp://lanpucloud.cn:1883
   # ID鍞竴
   clientId: MQTT_WK
   # 涓婚 澶氫釜涓婚鐢ㄩ�楀彿(,)鍒嗗壊 #琛ㄧず杩欎釜涓婚涓嬮潰鎵�鏈夛紝topic1,topic2,topic2/topic22/#(榛樿浼氬彇绗竴涓富棰�)
   topics: topic1
   # 鐢ㄦ埛鍚�
-  username: admin
+  username: tongjitang
   # 瀵嗙爜
-  password: 1q2w3e4r.
+  password: 123456
   # 杩炴帴瓒呮椂
   timeout: 30
   # 蹇冭烦妫�娴�
diff --git a/zhitan-common/pom.xml b/zhitan-common/pom.xml
index 53e470c..517c61c 100644
--- a/zhitan-common/pom.xml
+++ b/zhitan-common/pom.xml
@@ -133,7 +133,40 @@
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
         </dependency>
+<!--        <dependency>-->
+<!--            <groupId>cn.hutool</groupId>-->
+<!--            <artifactId>hutool-core</artifactId>-->
+<!--            <version>5.8.21</version>-->
+<!--        </dependency>-->
 
+<!--        <dependency>-->
+<!--            <groupId>cn.hutool</groupId>-->
+<!--            <artifactId>hutool-http</artifactId>-->
+<!--            <version>5.8.21</version>-->
+<!--        </dependency>-->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.28</version>
+            <!-- 寮哄埗澹版槑鐗堟湰浼樺厛绾� -->
+            <optional>true</optional>
+        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>cn.hutool</groupId>-->
+<!--            <artifactId>hutool-extra</artifactId>-->
+<!--            <version>5.8.21</version>-->
+<!--        </dependency>-->
+
+        <dependency>
+            <groupId>me.zhyd.oauth</groupId>
+            <artifactId>JustAuth</artifactId>
+            <version>1.16.7</version>
+        </dependency>
+        <dependency>
+            <groupId>io.github.linpeilie</groupId>
+            <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
+            <version>1.4.6</version>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/zhitan-common/src/main/java/com/zhitan/common/config/keycloak/AuthKeycloakRequest.java b/zhitan-common/src/main/java/com/zhitan/common/config/keycloak/AuthKeycloakRequest.java
new file mode 100644
index 0000000..2e7b6a1
--- /dev/null
+++ b/zhitan-common/src/main/java/com/zhitan/common/config/keycloak/AuthKeycloakRequest.java
@@ -0,0 +1,118 @@
+package com.zhitan.common.config.keycloak;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.StrUtil;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import com.xkcoding.http.support.HttpHeader;
+import com.zhitan.common.utils.JsonUtils;
+import com.zhitan.common.utils.spring.SpringUtils;
+import me.zhyd.oauth.cache.AuthStateCache;
+import me.zhyd.oauth.config.AuthConfig;
+import me.zhyd.oauth.exception.AuthException;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthToken;
+import me.zhyd.oauth.model.AuthUser;
+import me.zhyd.oauth.request.AuthDefaultRequest;
+import me.zhyd.oauth.utils.HttpUtils;
+import me.zhyd.oauth.utils.UrlBuilder;
+
+
+/**
+ * Keycloak OAuth2 璁よ瘉璇锋眰
+ */
+
+public class AuthKeycloakRequest extends AuthDefaultRequest {
+
+    public static final String SERVER_URL = SpringUtils.getProperty("keycloak.server-url");
+    public static final String REALM = SpringUtils.getProperty("keycloak.realm");
+
+    public AuthKeycloakRequest(AuthConfig config) {
+        super(config, AuthKeycloakSource.KEYCLOAK);
+    }
+
+    public AuthKeycloakRequest(AuthConfig config, AuthStateCache authStateCache) {
+        super(config, AuthKeycloakSource.KEYCLOAK, authStateCache);
+    }
+
+
+
+    @Override
+    public AuthToken getAccessToken(AuthCallback authCallback) {
+        String body = doPostAuthorizationCode(authCallback.getCode());
+        Dict object = JsonUtils.parseMap(body);
+
+        if (object.containsKey("error")) {
+            throw new AuthException(object.getStr("error_description"));
+        }
+        if (object.containsKey("message")) {
+            throw new AuthException(object.getStr("message"));
+        }
+
+        return AuthToken.builder()
+            .accessToken(object.getStr("access_token"))
+            .refreshToken(object.getStr("refresh_token"))
+            .idToken(object.getStr("id_token"))
+            .tokenType(object.getStr("token_type"))
+            .expireIn(object.getInt("expires_in"))
+            .build();
+    }
+
+    @Override
+    public AuthUser getUserInfo(AuthToken authToken) {
+        String body = doGetUserInfo(authToken);
+        Dict object = JsonUtils.parseMap(body);
+
+        if (object.containsKey("error")) {
+            throw new AuthException(object.getStr("error_description"));
+        }
+        if (object.containsKey("message")) {
+            throw new AuthException(object.getStr("message"));
+        }
+
+        return AuthUser.builder()
+            .uuid(object.getStr("sub"))
+            .username(object.getStr("preferred_username"))
+            .nickname(object.getStr("name"))
+            .email(object.getStr("email"))
+            .token(authToken)
+            .source(this.source.toString())
+            .build();
+    }
+
+    @Override
+    protected String doPostAuthorizationCode(String code) {
+        HttpRequest request = HttpRequest.post(source.accessToken())
+            .header("Authorization", "Basic " + Base64.encode(config.getClientId()+":"+config.getClientSecret()))
+            .form("grant_type", "authorization_code")
+            .form("code", code)
+            .form("redirect_uri", config.getRedirectUri());
+        HttpResponse response = request.execute();
+        return response.body();
+    }
+
+    @Override
+    protected String doGetUserInfo(AuthToken authToken) {
+        try {
+            return new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, new HttpHeader()
+                .add("Content-Type", "application/json")
+                .add("Authorization", "Bearer " + authToken.getAccessToken()), false).getBody();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public String authorize(String state) {
+        return UrlBuilder.fromBaseUrl(super.authorize(state))
+            .queryParam("scope","openid")
+            .build();
+    }
+
+
+
+
+}
diff --git a/zhitan-common/src/main/java/com/zhitan/common/config/keycloak/AuthKeycloakSource.java b/zhitan-common/src/main/java/com/zhitan/common/config/keycloak/AuthKeycloakSource.java
new file mode 100644
index 0000000..101a71b
--- /dev/null
+++ b/zhitan-common/src/main/java/com/zhitan/common/config/keycloak/AuthKeycloakSource.java
@@ -0,0 +1,36 @@
+package com.zhitan.common.config.keycloak;
+
+import me.zhyd.oauth.config.AuthSource;
+import me.zhyd.oauth.request.AuthDefaultRequest;
+
+public enum AuthKeycloakSource implements AuthSource {
+    KEYCLOAK {
+        /**
+         * 鎺堟潈鐨刟pi
+         */
+        @Override
+        public String authorize() {
+            return String.format("%s/realms/%s/protocol/openid-connect/auth", AuthKeycloakRequest.SERVER_URL, AuthKeycloakRequest.REALM);
+        }
+
+        @Override
+        public String accessToken() {
+            return String.format("%s/realms/%s/protocol/openid-connect/token", AuthKeycloakRequest.SERVER_URL, AuthKeycloakRequest.REALM);
+        }
+
+        @Override
+        public String userInfo() {
+            return String.format("%s/realms/%s/protocol/openid-connect/userinfo", AuthKeycloakRequest.SERVER_URL, AuthKeycloakRequest.REALM);
+        }
+
+
+        public String logout() {
+            return String.format("%s/realms/%s/protocol/openid-connect/logout", AuthKeycloakRequest.SERVER_URL, AuthKeycloakRequest.REALM);
+        }
+
+        @Override
+        public Class<? extends AuthDefaultRequest> getTargetClass() {
+            return AuthKeycloakRequest.class;
+        }
+    }
+}
diff --git a/zhitan-common/src/main/java/com/zhitan/common/config/keycloak/AuthRedisStateCache.java b/zhitan-common/src/main/java/com/zhitan/common/config/keycloak/AuthRedisStateCache.java
new file mode 100644
index 0000000..d9f097d
--- /dev/null
+++ b/zhitan-common/src/main/java/com/zhitan/common/config/keycloak/AuthRedisStateCache.java
@@ -0,0 +1,62 @@
+package com.zhitan.common.config.keycloak;
+
+import com.zhitan.common.core.redis.RedisCache;
+import lombok.AllArgsConstructor;
+import me.zhyd.oauth.cache.AuthStateCache;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 鎺堟潈鐘舵�佺紦瀛�
+ */
+@AllArgsConstructor
+@Component
+public class AuthRedisStateCache implements AuthStateCache {
+
+    @Autowired
+    private final RedisCache redisCache;
+
+    /**
+     * 瀛樺叆缂撳瓨
+     *
+     * @param key   缂撳瓨key
+     * @param value 缂撳瓨鍐呭
+     */
+    @Override
+    public void cache(String key, String value) {
+        // 鎺堟潈瓒呮椂鏃堕棿 榛樿涓夊垎閽�
+        redisCache.setCacheObject("social_auth_codes:" + key, value, 3, TimeUnit.MINUTES);
+    }
+
+    @Override
+    public void cache(String s, String s1, long l) {
+
+    }
+
+
+
+    /**
+     * 鑾峰彇缂撳瓨鍐呭
+     *
+     * @param key 缂撳瓨key
+     * @return 缂撳瓨鍐呭
+     */
+    @Override
+    public String get(String key) {
+        return redisCache.getCacheObject("social_auth_codes:" + key);
+    }
+
+    /**
+     * 鏄惁瀛樺湪key锛屽鏋滃搴攌ey鐨剉alue鍊煎凡杩囨湡锛屼篃杩斿洖false
+     *
+     * @param key 缂撳瓨key
+     * @return true锛氬瓨鍦╧ey锛屽苟涓攙alue娌¤繃鏈燂紱false锛歬ey涓嶅瓨鍦ㄦ垨鑰呭凡杩囨湡
+     */
+    @Override
+    public boolean containsKey(String key) {
+        return redisCache.hasKey("social_auth_codes:" + key);
+    }
+}
diff --git a/zhitan-common/src/main/java/com/zhitan/common/config/keycloak/SocialLoginConfigProperties.java b/zhitan-common/src/main/java/com/zhitan/common/config/keycloak/SocialLoginConfigProperties.java
new file mode 100644
index 0000000..bb1a8cd
--- /dev/null
+++ b/zhitan-common/src/main/java/com/zhitan/common/config/keycloak/SocialLoginConfigProperties.java
@@ -0,0 +1,81 @@
+package com.zhitan.common.config.keycloak;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 绀句氦鐧诲綍閰嶇疆
+ *
+ * @author thiszhc
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "keycloak")
+public class SocialLoginConfigProperties {
+
+    /**
+     * 搴旂敤 ID
+     */
+    private String clientId;
+
+    /**
+     * 搴旂敤瀵嗛挜
+     */
+    private String clientSecret;
+
+    /**
+     * 鍥炶皟鍦板潃
+     */
+    private String redirectUri;
+
+    /**
+     * 鏄惁鑾峰彇unionId
+     */
+    private boolean unionId;
+
+    /**
+     * Coding 浼佷笟鍚嶇О
+     */
+    private String codingGroupName;
+
+    /**
+     * 鏀粯瀹濆叕閽�
+     */
+    private String alipayPublicKey;
+
+    /**
+     * 浼佷笟寰俊搴旂敤ID
+     */
+    private String agentId;
+
+    /**
+     * stackoverflow api key
+     */
+    private String stackOverflowKey;
+
+    /**
+     * 璁惧ID
+     */
+    private String deviceId;
+
+    /**
+     * 瀹㈡埛绔郴缁熺被鍨�
+     */
+    private String clientOsType;
+
+    /**
+     * maxkey 鏈嶅姟鍣ㄥ湴鍧�
+     */
+    private String serverUrl;
+
+    /**
+     * 璇锋眰鑼冨洿
+     */
+    private List<String> scopes;
+
+    private String realm;
+
+}
diff --git a/zhitan-common/src/main/java/com/zhitan/common/core/domain/model/LoginBody.java b/zhitan-common/src/main/java/com/zhitan/common/core/domain/model/LoginBody.java
index 38d62cf..aa44a00 100644
--- a/zhitan-common/src/main/java/com/zhitan/common/core/domain/model/LoginBody.java
+++ b/zhitan-common/src/main/java/com/zhitan/common/core/domain/model/LoginBody.java
@@ -27,6 +27,47 @@
      */
     private String uuid;
 
+    private String socialState;
+
+    private String socialCode;
+
+    private String source;
+
+
+    private String grantType;
+
+    public String getSocialState() {
+        return socialState;
+    }
+
+    public void setSocialState(String socialState) {
+        this.socialState = socialState;
+    }
+
+    public String getSocialCode() {
+        return socialCode;
+    }
+
+    public void setSocialCode(String socialCode) {
+        this.socialCode = socialCode;
+    }
+
+    public String getSource() {
+        return source;
+    }
+
+    public void setSource(String source) {
+        this.source = source;
+    }
+
+    public String getGrantType() {
+        return grantType;
+    }
+
+    public void setGrantType(String grantType) {
+        this.grantType = grantType;
+    }
+
     public String getUsername()
     {
         return username;
diff --git a/zhitan-common/src/main/java/com/zhitan/common/utils/JsonUtils.java b/zhitan-common/src/main/java/com/zhitan/common/utils/JsonUtils.java
new file mode 100644
index 0000000..854fed3
--- /dev/null
+++ b/zhitan-common/src/main/java/com/zhitan/common/utils/JsonUtils.java
@@ -0,0 +1,170 @@
+package com.zhitan.common.utils;
+
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+import com.zhitan.common.utils.spring.SpringUtils;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JSON 宸ュ叿绫�
+ *
+ * @author 鑺嬮亾婧愮爜
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class JsonUtils {
+
+    private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class);
+
+    public static ObjectMapper getObjectMapper() {
+        return OBJECT_MAPPER;
+    }
+
+    /**
+     * 灏嗗璞¤浆鎹负JSON鏍煎紡鐨勫瓧绗︿覆
+     *
+     * @param object 瑕佽浆鎹㈢殑瀵硅薄
+     * @return JSON鏍煎紡鐨勫瓧绗︿覆锛屽鏋滃璞′负null锛屽垯杩斿洖null
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烰SON澶勭悊寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
+    public static String toJsonString(Object object) {
+        if (ObjectUtil.isNull(object)) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.writeValueAsString(object);
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 灏咼SON鏍煎紡鐨勫瓧绗︿覆杞崲涓烘寚瀹氱被鍨嬬殑瀵硅薄
+     *
+     * @param text  JSON鏍煎紡鐨勫瓧绗︿覆
+     * @param clazz 瑕佽浆鎹㈢殑鐩爣瀵硅薄绫诲瀷
+     * @param <T>   鐩爣瀵硅薄鐨勬硾鍨嬬被鍨�
+     * @return 杞崲鍚庣殑瀵硅薄锛屽鏋滃瓧绗︿覆涓虹┖鍒欒繑鍥瀗ull
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烮O寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
+    public static <T> T parseObject(String text, Class<T> clazz) {
+        if (StringUtils.isEmpty(text)) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.readValue(text, clazz);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 灏嗗瓧鑺傛暟缁勮浆鎹负鎸囧畾绫诲瀷鐨勫璞�
+     *
+     * @param bytes 瀛楄妭鏁扮粍
+     * @param clazz 瑕佽浆鎹㈢殑鐩爣瀵硅薄绫诲瀷
+     * @param <T>   鐩爣瀵硅薄鐨勬硾鍨嬬被鍨�
+     * @return 杞崲鍚庣殑瀵硅薄锛屽鏋滃瓧鑺傛暟缁勪负绌哄垯杩斿洖null
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烮O寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
+    public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
+        if (ArrayUtil.isEmpty(bytes)) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.readValue(bytes, clazz);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 灏咼SON鏍煎紡鐨勫瓧绗︿覆杞崲涓烘寚瀹氱被鍨嬬殑瀵硅薄锛屾敮鎸佸鏉傜被鍨�
+     *
+     * @param text          JSON鏍煎紡鐨勫瓧绗︿覆
+     * @param typeReference 鎸囧畾绫诲瀷鐨凾ypeReference瀵硅薄
+     * @param <T>           鐩爣瀵硅薄鐨勬硾鍨嬬被鍨�
+     * @return 杞崲鍚庣殑瀵硅薄锛屽鏋滃瓧绗︿覆涓虹┖鍒欒繑鍥瀗ull
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烮O寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
+    public static <T> T parseObject(String text, TypeReference<T> typeReference) {
+        if (StringUtils.isBlank(text)) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.readValue(text, typeReference);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 灏咼SON鏍煎紡鐨勫瓧绗︿覆杞崲涓篋ict瀵硅薄
+     *
+     * @param text JSON鏍煎紡鐨勫瓧绗︿覆
+     * @return 杞崲鍚庣殑Dict瀵硅薄锛屽鏋滃瓧绗︿覆涓虹┖鎴栬�呬笉鏄疛SON鏍煎紡鍒欒繑鍥瀗ull
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烮O寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
+    public static Dict parseMap(String text) {
+        if (StringUtils.isBlank(text)) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class));
+        } catch (MismatchedInputException e) {
+            // 绫诲瀷涓嶅尮閰嶈鏄庝笉鏄痡son
+            return null;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 灏咼SON鏍煎紡鐨勫瓧绗︿覆杞崲涓篋ict瀵硅薄鐨勫垪琛�
+     *
+     * @param text JSON鏍煎紡鐨勫瓧绗︿覆
+     * @return 杞崲鍚庣殑Dict瀵硅薄鐨勫垪琛紝濡傛灉瀛楃涓蹭负绌哄垯杩斿洖null
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烮O寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
+    public static List<Dict> parseArrayMap(String text) {
+        if (StringUtils.isBlank(text)) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 灏咼SON鏍煎紡鐨勫瓧绗︿覆杞崲涓烘寚瀹氱被鍨嬪璞$殑鍒楄〃
+     *
+     * @param text  JSON鏍煎紡鐨勫瓧绗︿覆
+     * @param clazz 瑕佽浆鎹㈢殑鐩爣瀵硅薄绫诲瀷
+     * @param <T>   鐩爣瀵硅薄鐨勬硾鍨嬬被鍨�
+     * @return 杞崲鍚庣殑瀵硅薄鐨勫垪琛紝濡傛灉瀛楃涓蹭负绌哄垯杩斿洖绌哄垪琛�
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烮O寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
+    public static <T> List<T> parseArray(String text, Class<T> clazz) {
+        if (StringUtils.isEmpty(text)) {
+            return new ArrayList<>();
+        }
+        try {
+            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}
diff --git a/zhitan-common/src/main/java/com/zhitan/common/utils/MapstructUtils.java b/zhitan-common/src/main/java/com/zhitan/common/utils/MapstructUtils.java
new file mode 100644
index 0000000..3569d61
--- /dev/null
+++ b/zhitan-common/src/main/java/com/zhitan/common/utils/MapstructUtils.java
@@ -0,0 +1,94 @@
+package com.zhitan.common.utils;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.zhitan.common.utils.spring.SpringUtils;
+import io.github.linpeilie.Converter;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Mapstruct 宸ュ叿绫�
+ * <p>鍙傝�冩枃妗o細<a href="https://mapstruct.plus/introduction/quick-start.html">mapstruct-plus</a></p>
+ *
+ *
+ * @author Michelle.Chung
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class MapstructUtils {
+
+    private final static Converter CONVERTER = SpringUtils.getBean(Converter.class);
+
+    /**
+     * 灏� T 绫诲瀷瀵硅薄锛岃浆鎹负 desc 绫诲瀷鐨勫璞″苟杩斿洖
+     *
+     * @param source 鏁版嵁鏉ユ簮瀹炰綋
+     * @param desc   鎻忚堪瀵硅薄 杞崲鍚庣殑瀵硅薄
+     * @return desc
+     */
+    public static <T, V> V convert(T source, Class<V> desc) {
+        if (ObjectUtil.isNull(source)) {
+            return null;
+        }
+        if (ObjectUtil.isNull(desc)) {
+            return null;
+        }
+        return CONVERTER.convert(source, desc);
+    }
+
+    /**
+     * 灏� T 绫诲瀷瀵硅薄锛屾寜鐓ч厤缃殑鏄犲皠瀛楁瑙勫垯锛岀粰 desc 绫诲瀷鐨勫璞¤祴鍊煎苟杩斿洖 desc 瀵硅薄
+     *
+     * @param source 鏁版嵁鏉ユ簮瀹炰綋
+     * @param desc   杞崲鍚庣殑瀵硅薄
+     * @return desc
+     */
+    public static <T, V> V convert(T source, V desc) {
+        if (ObjectUtil.isNull(source)) {
+            return null;
+        }
+        if (ObjectUtil.isNull(desc)) {
+            return null;
+        }
+        return CONVERTER.convert(source, desc);
+    }
+
+    /**
+     * 灏� T 绫诲瀷鐨勯泦鍚堬紝杞崲涓� desc 绫诲瀷鐨勯泦鍚堝苟杩斿洖
+     *
+     * @param sourceList 鏁版嵁鏉ユ簮瀹炰綋鍒楄〃
+     * @param desc       鎻忚堪瀵硅薄 杞崲鍚庣殑瀵硅薄
+     * @return desc
+     */
+    public static <T, V> List<V> convert(List<T> sourceList, Class<V> desc) {
+        if (ObjectUtil.isNull(sourceList)) {
+            return null;
+        }
+        if (CollUtil.isEmpty(sourceList)) {
+            return CollUtil.newArrayList();
+        }
+        return CONVERTER.convert(sourceList, desc);
+    }
+
+    /**
+     * 灏� Map 杞崲涓� beanClass 绫诲瀷鐨勯泦鍚堝苟杩斿洖
+     *
+     * @param map       鏁版嵁鏉ユ簮
+     * @param beanClass bean绫�
+     * @return bean瀵硅薄
+     */
+    public static <T> T convert(Map<String, Object> map, Class<T> beanClass) {
+        if (MapUtil.isEmpty(map)) {
+            return null;
+        }
+        if (ObjectUtil.isNull(beanClass)) {
+            return null;
+        }
+        return CONVERTER.convert(map, beanClass);
+    }
+
+}
diff --git a/zhitan-common/src/main/java/com/zhitan/common/utils/SocialUtils.java b/zhitan-common/src/main/java/com/zhitan/common/utils/SocialUtils.java
new file mode 100644
index 0000000..5d02e5b
--- /dev/null
+++ b/zhitan-common/src/main/java/com/zhitan/common/utils/SocialUtils.java
@@ -0,0 +1,47 @@
+package com.zhitan.common.utils;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.zhitan.common.config.keycloak.AuthKeycloakRequest;
+import com.zhitan.common.config.keycloak.AuthRedisStateCache;
+import com.zhitan.common.config.keycloak.SocialLoginConfigProperties;
+import com.zhitan.common.utils.spring.SpringUtils;
+import me.zhyd.oauth.config.AuthConfig;
+import me.zhyd.oauth.exception.AuthException;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthResponse;
+import me.zhyd.oauth.model.AuthUser;
+import me.zhyd.oauth.request.AuthDingTalkRequest;
+import me.zhyd.oauth.request.AuthRequest;
+
+
+/**
+ * 璁よ瘉鎺堟潈宸ュ叿绫�
+ *
+ * @author thiszhc
+ */
+public class SocialUtils {
+
+    private static final AuthRedisStateCache STATE_CACHE = SpringUtils.getBean(AuthRedisStateCache.class);
+    private static final SocialLoginConfigProperties obj = SpringUtils.getBean(SocialLoginConfigProperties.class);
+    @SuppressWarnings("unchecked")
+    public static AuthResponse<AuthUser> loginAuth( String code, String state) throws AuthException {
+        AuthRequest authRequest = getAuthKeyloakRequest();
+        AuthCallback callback = new AuthCallback();
+        callback.setCode(code);
+        callback.setState(state);
+        return authRequest.login(callback);
+    }
+
+
+
+    public static AuthKeycloakRequest getAuthKeyloakRequest( ) {
+
+        AuthConfig.AuthConfigBuilder builder = AuthConfig.builder()
+            .clientId(obj.getClientId())
+            .clientSecret(obj.getClientSecret())
+            .redirectUri(obj.getRedirectUri())
+            .scopes(obj.getScopes());
+        return new AuthKeycloakRequest(builder.build(), STATE_CACHE);
+    }
+}
+
diff --git a/zhitan-common/src/main/java/com/zhitan/common/utils/SpringUtils.java b/zhitan-common/src/main/java/com/zhitan/common/utils/SpringUtils.java
new file mode 100644
index 0000000..a6b5663
--- /dev/null
+++ b/zhitan-common/src/main/java/com/zhitan/common/utils/SpringUtils.java
@@ -0,0 +1,63 @@
+//package com.zhitan.common.utils;
+//
+//import cn.hutool.extra.spring.SpringUtil;
+//import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+//import org.springframework.context.ApplicationContext;
+//import org.springframework.core.env.Environment;
+//import org.springframework.stereotype.Component;
+//
+///**
+// * spring宸ュ叿绫�
+// *
+// * @author Lion Li
+// */
+//@Component
+//public final class SpringUtils extends SpringUtil {
+//
+//    /**
+//     * 濡傛灉BeanFactory鍖呭惈涓�涓笌鎵�缁欏悕绉板尮閰嶇殑bean瀹氫箟锛屽垯杩斿洖true
+//     */
+//    public static boolean containsBean(String name) {
+//        return getBeanFactory().containsBean(name);
+//    }
+//
+//    /**
+//     * 鍒ゆ柇浠ョ粰瀹氬悕瀛楁敞鍐岀殑bean瀹氫箟鏄竴涓猻ingleton杩樻槸涓�涓猵rototype銆�
+//     * 濡傛灉涓庣粰瀹氬悕瀛楃浉搴旂殑bean瀹氫箟娌℃湁琚壘鍒帮紝灏嗕細鎶涘嚭涓�涓紓甯革紙NoSuchBeanDefinitionException锛�
+//     */
+//    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+//        return getBeanFactory().isSingleton(name);
+//    }
+//
+//    /**
+//     * @return Class 娉ㄥ唽瀵硅薄鐨勭被鍨�
+//     */
+//    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
+//        return getBeanFactory().getType(name);
+//    }
+//
+//    /**
+//     * 濡傛灉缁欏畾鐨刡ean鍚嶅瓧鍦╞ean瀹氫箟涓湁鍒悕锛屽垯杩斿洖杩欎簺鍒悕
+//     */
+//    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+//        return getBeanFactory().getAliases(name);
+//    }
+//
+//    /**
+//     * 鑾峰彇aop浠g悊瀵硅薄
+//     */
+//    @SuppressWarnings("unchecked")
+//    public static <T> T getAopProxy(T invoker) {
+//        return (T) getBean(invoker.getClass());
+//    }
+//
+//
+//    /**
+//     * 鑾峰彇spring涓婁笅鏂�
+//     */
+//    public static ApplicationContext context() {
+//        return getApplicationContext();
+//    }
+//
+//
+//}
diff --git a/zhitan-common/src/main/java/com/zhitan/common/utils/spring/SpringUtils.java b/zhitan-common/src/main/java/com/zhitan/common/utils/spring/SpringUtils.java
index 011f413..4d60e3b 100644
--- a/zhitan-common/src/main/java/com/zhitan/common/utils/spring/SpringUtils.java
+++ b/zhitan-common/src/main/java/com/zhitan/common/utils/spring/SpringUtils.java
@@ -1,12 +1,15 @@
 package com.zhitan.common.utils.spring;
 
+import cn.hutool.extra.spring.SpringUtil;
 import org.springframework.aop.framework.AopContext;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.env.Environment;
 import org.springframework.stereotype.Component;
 import com.zhitan.common.utils.StringUtils;
 
@@ -16,7 +19,7 @@
  * @author zhitan
  */
 @Component
-public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware 
+public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
 {
     /** Spring搴旂敤涓婁笅鏂囩幆澧� */
     private static ConfigurableListableBeanFactory beanFactory;
@@ -47,6 +50,11 @@
     public static <T> T getBean(String name) throws BeansException
     {
         return (T) beanFactory.getBean(name);
+    }
+
+    public static String getProperty(String key)
+    {
+        return applicationContext == null ? null : applicationContext.getEnvironment().getProperty(key);
     }
 
     /**
@@ -155,4 +163,6 @@
     {
         return applicationContext.getEnvironment().getRequiredProperty(key);
     }
+
+
 }
diff --git a/zhitan-framework/pom.xml b/zhitan-framework/pom.xml
index eb0d179..65b9005 100644
--- a/zhitan-framework/pom.xml
+++ b/zhitan-framework/pom.xml
@@ -59,6 +59,8 @@
             </exclusions>
         </dependency>
 
+
+
         <!-- 鑾峰彇绯荤粺淇℃伅 -->
         <dependency>
             <groupId>com.github.oshi</groupId>
@@ -79,6 +81,10 @@
             <groupId>org.springframework.integration</groupId>
             <artifactId>spring-integration-mqtt</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-core</artifactId>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/zhitan-framework/src/main/java/com/zhitan/framework/config/SecurityConfig.java b/zhitan-framework/src/main/java/com/zhitan/framework/config/SecurityConfig.java
index e0125a6..98791ea 100644
--- a/zhitan-framework/src/main/java/com/zhitan/framework/config/SecurityConfig.java
+++ b/zhitan-framework/src/main/java/com/zhitan/framework/config/SecurityConfig.java
@@ -115,7 +115,7 @@
                 // 杩囨护璇锋眰
                 .authorizeRequests()
                 // 瀵逛簬鐧诲綍login 娉ㄥ唽register 楠岃瘉鐮乧aptchaImage 鍏佽鍖垮悕璁块棶
-                .antMatchers("/login", "/register", "/captchaImage").permitAll()
+                .antMatchers("/login", "/register", "/captchaImage", "/binding").permitAll()
                 // 闈欐�佽祫婧愶紝鍙尶鍚嶈闂�
                 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                 .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
diff --git a/zhitan-framework/src/main/java/com/zhitan/framework/security/handle/LogoutSuccessHandlerImpl.java b/zhitan-framework/src/main/java/com/zhitan/framework/security/handle/LogoutSuccessHandlerImpl.java
index 4064d5c..ad2f9f3 100644
--- a/zhitan-framework/src/main/java/com/zhitan/framework/security/handle/LogoutSuccessHandlerImpl.java
+++ b/zhitan-framework/src/main/java/com/zhitan/framework/security/handle/LogoutSuccessHandlerImpl.java
@@ -5,6 +5,12 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.annotation.Resource;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import com.zhitan.system.domain.SysSocial;
+import com.zhitan.system.service.ISysSocialService;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
@@ -30,6 +36,16 @@
     @Resource
     private TokenService tokenService;
 
+    @Value("${keycloak.server-url}")
+    private String keycloakServerUrl;
+
+    @Value("${keycloak.realm}")
+    private String keycloakRealm;
+
+
+    @Resource
+    private ISysSocialService sysSocialService;
+
     /**
      * 閫�鍑哄鐞�
      * 
@@ -47,6 +63,21 @@
             tokenService.delLoginUser(loginUser.getToken());
             // 璁板綍鐢ㄦ埛閫�鍑烘棩蹇�
             AsyncManager.me().execute(AsyncFactory.recordLoginInfo(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success")));
+            SysSocial social = sysSocialService.selectByUserId(loginUser.getUserId());
+            if (social == null) {
+                return;
+            }
+
+            String logoutUrl = keycloakServerUrl + "/realms/" + keycloakRealm + "/protocol/openid-connect/logout";
+            HttpRequest req = HttpRequest.get(logoutUrl)
+                    .form("refresh_token", social.getRefreshToken())
+                    .form("id_token_hint", social.getIdToken());
+
+
+            HttpResponse rep = req.execute();
+            if (rep.isOk()) {
+                System.out.println("1234");
+            }
         }
         ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success(MessageUtils.message("user.logout.success"))));
     }
diff --git a/zhitan-framework/src/main/java/com/zhitan/framework/web/service/SysLoginService.java b/zhitan-framework/src/main/java/com/zhitan/framework/web/service/SysLoginService.java
index be2716a..bdb62a4 100644
--- a/zhitan-framework/src/main/java/com/zhitan/framework/web/service/SysLoginService.java
+++ b/zhitan-framework/src/main/java/com/zhitan/framework/web/service/SysLoginService.java
@@ -1,5 +1,7 @@
 package com.zhitan.framework.web.service;
 
+import cn.hutool.core.collection.CollUtil;
+import com.zhitan.common.config.keycloak.AuthKeycloakRequest;
 import com.zhitan.common.constant.CacheConstants;
 import com.zhitan.common.constant.Constants;
 import com.zhitan.common.constant.UserConstants;
@@ -10,14 +12,23 @@
 import com.zhitan.common.exception.user.*;
 import com.zhitan.common.utils.DateUtils;
 import com.zhitan.common.utils.MessageUtils;
+import com.zhitan.common.utils.SocialUtils;
 import com.zhitan.common.utils.StringUtils;
 import com.zhitan.common.utils.ip.IpUtils;
 import com.zhitan.framework.manager.AsyncManager;
 import com.zhitan.framework.manager.factory.AsyncFactory;
 import com.zhitan.framework.security.context.AuthenticationContextHolder;
 import com.zhitan.framework.security.single.SingleAuthenticationToken;
+import com.zhitan.system.domain.SysSocial;
+import com.zhitan.system.domain.bo.SysSocialBo;
+import com.zhitan.system.domain.vo.SysSocialVo;
 import com.zhitan.system.service.ISysConfigService;
+import com.zhitan.system.service.ISysSocialService;
 import com.zhitan.system.service.ISysUserService;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthResponse;
+import me.zhyd.oauth.model.AuthToken;
+import me.zhyd.oauth.model.AuthUser;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -25,6 +36,7 @@
 import org.springframework.stereotype.Component;
 
 import javax.annotation.Resource;
+import java.util.List;
 
 /**
  * 鐧诲綍鏍¢獙鏂规硶
@@ -48,6 +60,9 @@
 
     @Resource
     private ISysConfigService configService;
+
+    @Resource
+    private ISysSocialService sysSocialService;
 
     /**
      * 鐧诲綍楠岃瘉
@@ -97,6 +112,68 @@
         return tokenService.createToken(loginUser);
     }
 
+
+
+
+
+    public String loginByCode(String code,String state)
+    {
+        AuthKeycloakRequest authRequest = SocialUtils.getAuthKeyloakRequest();
+       // AuthToken accessToken = authRequest.getAccessToken(passwordLoginBody);
+        AuthCallback callback = new AuthCallback();
+        callback.setCode(code);
+        callback.setState(state);
+        AuthResponse<AuthUser> res = authRequest.login(callback);
+        AuthUser authUserData = res.getData();
+        // 鏂板KEYCLOAK鐢ㄦ埛鑷姩鍒涘缓閫昏緫
+        String authId = authUserData.getSource() + authUserData.getUuid();
+        List<SysSocial> list = sysSocialService.selectByAuthId(authId);
+        if (CollUtil.isEmpty(list)) {
+            // 鑷姩鍒涘缓鏂扮敤鎴�
+            SysUser newUser = new SysUser();
+            newUser.setUserName(authUserData.getUsername());
+            newUser.setEmail(authUserData.getEmail());
+            newUser.setNickName(authUserData.getNickname());
+            newUser.setPassword("Initial123@"); // 鍒濆瀵嗙爜闇�绗﹀悎瀹夊叏绛栫暐
+            newUser.setStatus("0");
+
+            userService.insertUser(newUser); // 鍋囪瀛樺湪鎻掑叆鏂规硶
+
+            // 鍒涘缓绀句氦缁戝畾璁板綍
+            SysSocialBo newSocial = new SysSocialBo();
+            newSocial.setUserId(newUser.getUserId());
+            newSocial.setUserName(newUser.getUserName());
+            newSocial.setAuthId(authId);
+            newSocial.setSource(authUserData.getSource());
+            newSocial.setOpenId(authUserData.getUuid());
+            newSocial.setAccessToken(authUserData.getToken().getAccessToken());
+            newSocial.setRefreshToken(authUserData.getToken().getRefreshToken());
+            newSocial.setIdToken(authUserData.getToken().getIdToken());
+            sysSocialService.insertByBo(newSocial); // 闇�纭繚鏈嶅姟鏈夋柊澧炴柟娉�
+
+            // 閲嶆柊鏌ヨ纭繚鏁版嵁鍙敤
+            list = sysSocialService.selectByAuthId(authId);
+        } else {
+            // 鏇存柊绀句氦缁戝畾璁板綍
+            SysSocialBo socialBo = new SysSocialBo();
+            socialBo.setId(list.get(0).getId());
+            socialBo.setAccessToken(authUserData.getToken().getAccessToken());
+            socialBo.setRefreshToken(authUserData.getToken().getRefreshToken());
+            socialBo.setIdToken(authUserData.getToken().getIdToken());
+            sysSocialService.updateByBo(socialBo);
+
+        }
+        list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
+        if (CollUtil.isEmpty(list)) {
+            throw new ServiceException("浣犺繕娌℃湁缁戝畾绗笁鏂硅处鍙凤紝缁戝畾鍚庢墠鍙互鐧诲綍锛�");
+        }
+        LoginUser loginUser = new LoginUser();
+        loginUser.setUser(userService.selectUserById(list.get(0).getUserId()));
+        loginUser.setUserId(list.get(0).getUserId());
+        // 鐢熸垚token
+        return tokenService.createToken(loginUser);
+    }
+
     /**
      * 鏍¢獙楠岃瘉鐮�
      *
diff --git a/zhitan-system/src/main/java/com/zhitan/model/mapper/EnergyIndexMapper.java b/zhitan-system/src/main/java/com/zhitan/model/mapper/EnergyIndexMapper.java
index 3c02acc..3fbabeb 100644
--- a/zhitan-system/src/main/java/com/zhitan/model/mapper/EnergyIndexMapper.java
+++ b/zhitan-system/src/main/java/com/zhitan/model/mapper/EnergyIndexMapper.java
@@ -102,4 +102,6 @@
   List<EnergyIndex> getIndexByCode(@Param("code")String code, @Param("nodeId")String nodeId);
 
   List<EnergyIndex> getIndexByMeterIdIndexCode(@Param("meterId") String meterId, @Param("indexCode") String indexCode, @Param("nodeId") String nodeId);
+
+  int deleteByIndexId(String indexId);
 }
diff --git a/zhitan-system/src/main/java/com/zhitan/model/service/IEnergyIndexService.java b/zhitan-system/src/main/java/com/zhitan/model/service/IEnergyIndexService.java
index 1f4c23c..28c500a 100644
--- a/zhitan-system/src/main/java/com/zhitan/model/service/IEnergyIndexService.java
+++ b/zhitan-system/src/main/java/com/zhitan/model/service/IEnergyIndexService.java
@@ -134,4 +134,10 @@
      * @return
      */
     List<EnergyIndex> listDeviceIndex(String nodeId, String meterId);
+
+    /**
+     * 閫氳繃id鍒犻櫎閲囬泦鐐�
+     */
+    boolean deleteByIndexId(String indexId);
+
 }
diff --git a/zhitan-system/src/main/java/com/zhitan/model/service/impl/EnergyIndexServiceImpl.java b/zhitan-system/src/main/java/com/zhitan/model/service/impl/EnergyIndexServiceImpl.java
index c519c97..abd2922 100644
--- a/zhitan-system/src/main/java/com/zhitan/model/service/impl/EnergyIndexServiceImpl.java
+++ b/zhitan-system/src/main/java/com/zhitan/model/service/impl/EnergyIndexServiceImpl.java
@@ -361,4 +361,11 @@
         List<EnergyIndex> energyIndexList = energyIndexMapper.getIndexByMeterIdIndexCode(meterId,null,nodeId);
         return energyIndexList;
     }
+
+
+    @Override
+    public boolean deleteByIndexId(String indexId) {
+        return energyIndexMapper.deleteByIndexId(indexId) > 0;
+    }
+
 }
diff --git a/zhitan-system/src/main/java/com/zhitan/system/domain/SysSocial.java b/zhitan-system/src/main/java/com/zhitan/system/domain/SysSocial.java
new file mode 100644
index 0000000..4f530b6
--- /dev/null
+++ b/zhitan-system/src/main/java/com/zhitan/system/domain/SysSocial.java
@@ -0,0 +1,133 @@
+package com.zhitan.system.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 绀句細鍖栧叧绯诲璞� sys_social
+ *
+ * @author thiszhc
+ */
+@Data
+
+@TableName("sys_social")
+public class SysSocial  {
+
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 鐢ㄦ埛ID
+     */
+    private Long userId;
+
+    /**
+     * 鐨勫敮涓�ID
+     */
+    private String authId;
+
+    /**
+     * 鐢ㄦ埛鏉ユ簮
+     */
+    private String source;
+
+    /**
+     * 鐢ㄦ埛鐨勬巿鏉冧护鐗�
+     */
+    private String accessToken;
+
+    /**
+     * 鐢ㄦ埛鐨勬巿鏉冧护鐗岀殑鏈夋晥鏈燂紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private int expireIn;
+
+    /**
+     * 鍒锋柊浠ょ墝锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�
+     */
+    private String refreshToken;
+
+    /**
+     * 鐢ㄦ埛鐨� open id
+     */
+    private String openId;
+
+    /**
+     * 鎺堟潈鐨勭涓夋柟璐﹀彿
+     */
+    private String userName;
+
+    /**
+     * 鎺堟潈鐨勭涓夋柟鏄电О
+     */
+    private String nickName;
+
+    /**
+     * 鎺堟潈鐨勭涓夋柟閭
+     */
+    private String email;
+
+    /**
+     * 鎺堟潈鐨勭涓夋柟澶村儚鍦板潃
+     */
+    private String avatar;
+
+    /**
+     * 骞冲彴鐨勬巿鏉冧俊鎭紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String accessCode;
+
+    /**
+     * 鐢ㄦ埛鐨� unionid
+     */
+    private String unionId;
+
+    /**
+     * 鎺堜簣鐨勬潈闄愶紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String scope;
+
+    /**
+     * 涓埆骞冲彴鐨勬巿鏉冧俊鎭紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String tokenType;
+
+    /**
+     * id token锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�
+     */
+    private String idToken;
+
+    /**
+     * 灏忕背骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String macAlgorithm;
+
+    /**
+     * 灏忕背骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String macKey;
+
+    /**
+     * 鐢ㄦ埛鐨勬巿鏉僣ode锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�
+     */
+    private String code;
+
+    /**
+     * Twitter骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String oauthToken;
+
+    /**
+     * Twitter骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String oauthTokenSecret;
+
+
+}
diff --git a/zhitan-system/src/main/java/com/zhitan/system/domain/bo/SysSocialBo.java b/zhitan-system/src/main/java/com/zhitan/system/domain/bo/SysSocialBo.java
new file mode 100644
index 0000000..562c096
--- /dev/null
+++ b/zhitan-system/src/main/java/com/zhitan/system/domain/bo/SysSocialBo.java
@@ -0,0 +1,133 @@
+package com.zhitan.system.domain.bo;
+
+
+import com.zhitan.system.domain.SysSocial;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotNull;
+
+
+/**
+ * 绀句細鍖栧叧绯讳笟鍔″璞� sys_social
+ *
+ * @author Lion Li
+ */
+@Data
+@NoArgsConstructor
+public class SysSocialBo {
+
+    /**
+     * 涓婚敭
+     */
+    private Long id;
+
+    /**
+     * 璁よ瘉鍞竴ID
+     */
+    private String authId;
+
+    /**
+     * 鐢ㄦ埛鏉ユ簮
+     */
+    private String source;
+
+    /**
+     * 鐢ㄦ埛鐨勬巿鏉冧护鐗�
+     */
+    private String accessToken;
+
+    /**
+     * 鐢ㄦ埛鐨勬巿鏉冧护鐗岀殑鏈夋晥鏈燂紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private int expireIn;
+
+    /**
+     * 鍒锋柊浠ょ墝锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�
+     */
+    private String refreshToken;
+
+    /**
+     * 骞冲彴鍞竴id
+     */
+    private String openId;
+
+    /**
+     * 鐢ㄦ埛鐨� ID
+     */
+    private Long userId;
+
+    /**
+     * 骞冲彴鐨勬巿鏉冧俊鎭紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String accessCode;
+
+    /**
+     * 鐢ㄦ埛鐨� unionid
+     */
+    private String unionId;
+
+    /**
+     * 鎺堜簣鐨勬潈闄愶紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String scope;
+
+    /**
+     * 鎺堟潈鐨勭涓夋柟璐﹀彿
+     */
+    private String userName;
+
+    /**
+     * 鎺堟潈鐨勭涓夋柟鏄电О
+     */
+    private String nickName;
+
+    /**
+     * 鎺堟潈鐨勭涓夋柟閭
+     */
+    private String email;
+
+    /**
+     * 鎺堟潈鐨勭涓夋柟澶村儚鍦板潃
+     */
+    private String avatar;
+
+    /**
+     * 涓埆骞冲彴鐨勬巿鏉冧俊鎭紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String tokenType;
+
+    /**
+     * id token锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�
+     */
+    private String idToken;
+
+    /**
+     * 灏忕背骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String macAlgorithm;
+
+    /**
+     * 灏忕背骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String macKey;
+
+    /**
+     * 鐢ㄦ埛鐨勬巿鏉僣ode锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�
+     */
+    private String code;
+
+    /**
+     * Twitter骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String oauthToken;
+
+    /**
+     * Twitter骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String oauthTokenSecret;
+
+
+
+}
diff --git a/zhitan-system/src/main/java/com/zhitan/system/domain/vo/SysSocialVo.java b/zhitan-system/src/main/java/com/zhitan/system/domain/vo/SysSocialVo.java
new file mode 100644
index 0000000..7bfd969
--- /dev/null
+++ b/zhitan-system/src/main/java/com/zhitan/system/domain/vo/SysSocialVo.java
@@ -0,0 +1,139 @@
+package com.zhitan.system.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 绀句細鍖栧叧绯昏鍥惧璞� sys_social
+ *
+ * @author thiszhc
+ */
+@Data
+public class SysSocialVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    private Long id;
+
+    /**
+     * 鐢ㄦ埛ID
+     */
+    private Long userId;
+
+    /**
+     * 绉熸埛ID
+     */
+    private String tenantId;
+
+    /**
+     * 鐨勫敮涓�ID
+     */
+    private String authId;
+
+    /**
+     * 鐢ㄦ埛鏉ユ簮
+     */
+    private String source;
+
+    /**
+     * 鐢ㄦ埛鐨勬巿鏉冧护鐗�
+     */
+    private String accessToken;
+
+    /**
+     * 鐢ㄦ埛鐨勬巿鏉冧护鐗岀殑鏈夋晥鏈燂紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private int expireIn;
+
+    /**
+     * 鍒锋柊浠ょ墝锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�
+     */
+    private String refreshToken;
+
+    /**
+     * 鐢ㄦ埛鐨� open id
+     */
+    private String openId;
+
+    /**
+     * 鎺堟潈鐨勭涓夋柟璐﹀彿
+     */
+    private String userName;
+
+    /**
+     * 鎺堟潈鐨勭涓夋柟鏄电О
+     */
+    private String nickName;
+
+    /**
+     * 鎺堟潈鐨勭涓夋柟閭
+     */
+    private String email;
+
+    /**
+     * 鎺堟潈鐨勭涓夋柟澶村儚鍦板潃
+     */
+    private String avatar;
+
+
+    /**
+     * 骞冲彴鐨勬巿鏉冧俊鎭紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String accessCode;
+
+    /**
+     * 鐢ㄦ埛鐨� unionid
+     */
+    private String unionId;
+
+    /**
+     * 鎺堜簣鐨勬潈闄愶紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String scope;
+
+    /**
+     * 涓埆骞冲彴鐨勬巿鏉冧俊鎭紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String tokenType;
+
+    /**
+     * id token锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�
+     */
+    private String idToken;
+
+    /**
+     * 灏忕背骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String macAlgorithm;
+
+    /**
+     * 灏忕背骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String macKey;
+
+    /**
+     * 鐢ㄦ埛鐨勬巿鏉僣ode锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�
+     */
+    private String code;
+
+    /**
+     * Twitter骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String oauthToken;
+
+    /**
+     * Twitter骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁
+     */
+    private String oauthTokenSecret;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    private Date createTime;
+}
diff --git a/zhitan-system/src/main/java/com/zhitan/system/mapper/SysSocialMapper.java b/zhitan-system/src/main/java/com/zhitan/system/mapper/SysSocialMapper.java
new file mode 100644
index 0000000..e6d06cd
--- /dev/null
+++ b/zhitan-system/src/main/java/com/zhitan/system/mapper/SysSocialMapper.java
@@ -0,0 +1,14 @@
+package com.zhitan.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhitan.system.domain.SysSocial;
+import com.zhitan.system.domain.vo.SysSocialVo;
+
+/**
+ * 绀句細鍖栧叧绯籑apper鎺ュ彛
+ *
+ * @author thiszhc
+ */
+public interface SysSocialMapper extends BaseMapper<SysSocial> {
+
+}
diff --git a/zhitan-system/src/main/java/com/zhitan/system/service/ISysSocialService.java b/zhitan-system/src/main/java/com/zhitan/system/service/ISysSocialService.java
new file mode 100644
index 0000000..79c4b65
--- /dev/null
+++ b/zhitan-system/src/main/java/com/zhitan/system/service/ISysSocialService.java
@@ -0,0 +1,35 @@
+package com.zhitan.system.service;
+
+
+import com.zhitan.system.domain.SysSocial;
+import com.zhitan.system.domain.bo.SysSocialBo;
+import com.zhitan.system.domain.vo.SysSocialVo;
+
+import java.util.List;
+
+/**
+ * 绀句細鍖栧叧绯籗ervice鎺ュ彛
+ *
+ * @author thiszhc
+ */
+public interface ISysSocialService {
+
+
+    /**
+     * 鏂板鎺堟潈鍏崇郴
+     */
+    Boolean insertByBo(SysSocialBo bo);
+
+    /**
+     * 鏇存柊绀句細鍖栧叧绯�
+     */
+    Boolean updateByBo(SysSocialBo bo);
+
+    /**
+     * 鏍规嵁 authId 鏌ヨ
+     */
+    List<SysSocial> selectByAuthId(String authId);
+
+    SysSocial selectByUserId(Long userId);
+
+}
diff --git a/zhitan-system/src/main/java/com/zhitan/system/service/impl/SysSocialServiceImpl.java b/zhitan-system/src/main/java/com/zhitan/system/service/impl/SysSocialServiceImpl.java
new file mode 100644
index 0000000..579ee63
--- /dev/null
+++ b/zhitan-system/src/main/java/com/zhitan/system/service/impl/SysSocialServiceImpl.java
@@ -0,0 +1,82 @@
+package com.zhitan.system.service.impl;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.zhitan.common.utils.MapstructUtils;
+import com.zhitan.common.utils.StringUtils;
+import com.zhitan.common.utils.bean.BeanUtils;
+import com.zhitan.system.domain.SysSocial;
+import com.zhitan.system.domain.bo.SysSocialBo;
+import com.zhitan.system.domain.vo.SysSocialVo;
+import com.zhitan.system.mapper.SysSocialMapper;
+import com.zhitan.system.service.ISysSocialService;
+import lombok.RequiredArgsConstructor;
+
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 绀句細鍖栧叧绯籗ervice涓氬姟灞傚鐞�
+ *
+ * @author thiszhc
+ * @date 2023-06-12
+ */
+@RequiredArgsConstructor
+@Service
+public class SysSocialServiceImpl implements ISysSocialService {
+
+    private final SysSocialMapper baseMapper;
+
+
+    /**
+     * 鏂板绀句細鍖栧叧绯�
+     */
+    @Override
+    public Boolean insertByBo(SysSocialBo bo) {
+        SysSocial add = new SysSocial();
+        BeanUtils.copyProperties(bo, add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            if (add != null) {
+                bo.setId(add.getId());
+            } else {
+                return false;
+            }
+        }
+        return flag;
+    }
+
+    /**
+     * 鏇存柊绀句細鍖栧叧绯�
+     */
+    @Override
+    public Boolean updateByBo(SysSocialBo bo) {
+        SysSocial update = new SysSocial();
+        BeanUtils.copyProperties(bo, update);
+
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 鏍规嵁 authId 鏌ヨ鐢ㄦ埛淇℃伅
+     *
+     * @param authId 璁よ瘉id
+     * @return 鎺堟潈淇℃伅
+     */
+    @Override
+    public List<SysSocial> selectByAuthId(String authId) {
+        List<SysSocial> sysSocials = baseMapper.selectList(new LambdaQueryWrapper<SysSocial>().eq(SysSocial::getAuthId, authId));
+        // 杞崲鎴怴O
+
+        return sysSocials;
+    }
+
+    @Override
+    public SysSocial selectByUserId(Long userId) {
+        SysSocial socialVo = baseMapper.selectOne(new LambdaQueryWrapper<SysSocial>().eq(SysSocial::getUserId, userId));
+        return socialVo;
+    }
+
+
+}
diff --git a/zhitan-system/src/main/resources/mapper/model/EnergyIndexMapper.xml b/zhitan-system/src/main/resources/mapper/model/EnergyIndexMapper.xml
index 78d0144..055a0e0 100644
--- a/zhitan-system/src/main/resources/mapper/model/EnergyIndexMapper.xml
+++ b/zhitan-system/src/main/resources/mapper/model/EnergyIndexMapper.xml
@@ -309,6 +309,11 @@
       #{indexId}
     </foreach>;
   </delete>
+  <delete id="deleteByIndexId">
+    delete
+    from energy_index
+    where index_id = #{indexId}
+  </delete>
 
   <select id="getEnergyIndexMeterByCodes" resultMap="EnergyIndexResult">
     select ei.index_id,
diff --git a/zhitan-vue/.env.development b/zhitan-vue/.env.development
index ff211da..0d519a3 100644
--- a/zhitan-vue/.env.development
+++ b/zhitan-vue/.env.development
@@ -6,6 +6,6 @@
 
 # 绯荤粺/寮�鍙戠幆澧�
 # test
-VITE_APP_BASE_API = 'http://139.159.201.118:8201'
+VITE_APP_BASE_API = '/dev-api'
 # hangmingjun
 
diff --git a/zhitan-vue/src/api/login.js b/zhitan-vue/src/api/login.js
index 82250eb..18738e2 100644
--- a/zhitan-vue/src/api/login.js
+++ b/zhitan-vue/src/api/login.js
@@ -19,6 +19,28 @@
   })
 }
 
+export function loginBySocial(data) {
+  console.log("loginBySocial",data);
+  return request({
+    url: '/login',
+    headers: {
+      isToken: false,
+      repeatSubmit: false
+    },
+    method: 'post',
+    data: data
+  })
+}
+
+
+// 缁戝畾璐﹀彿
+export function authBinding() {
+  return request({
+    url: '/binding',
+    method: 'get'
+  });
+}
+
 // 娉ㄥ唽鏂规硶
 export function register(data) {
   return request({
diff --git a/zhitan-vue/src/api/types.ts b/zhitan-vue/src/api/types.ts
new file mode 100644
index 0000000..617286c
--- /dev/null
+++ b/zhitan-vue/src/api/types.ts
@@ -0,0 +1,59 @@
+/**
+ * 娉ㄥ唽
+ */
+export type RegisterForm = {
+  tenantId: string;
+  username: string;
+  password: string;
+  confirmPassword?: string;
+  code?: string;
+  uuid?: string;
+  userType?: string;
+};
+
+/**
+ * 鐧诲綍璇锋眰
+ */
+export interface LoginData {
+  tenantId?: string;
+  username?: string;
+  password?: string;
+  rememberMe?: boolean;
+  socialCode?: string;
+  socialState?: string;
+  source?: string;
+  code?: string;
+  uuid?: string;
+  clientId: string;
+  grantType: string;
+}
+
+/**
+ * 鐧诲綍鍝嶅簲
+ */
+export interface LoginResult {
+  access_token: string;
+}
+
+/**
+ * 楠岃瘉鐮佽繑鍥�
+ */
+export interface VerifyCodeResult {
+  captchaEnabled: boolean;
+  uuid?: string;
+  img?: string;
+}
+
+/**
+ * 绉熸埛
+ */
+export interface TenantVO {
+  companyName: string;
+  domain: any;
+  tenantId: string;
+}
+
+export interface TenantInfo {
+  tenantEnabled: boolean;
+  voList: TenantVO[];
+}
diff --git a/zhitan-vue/src/layout/components/SocialCallback/index.vue b/zhitan-vue/src/layout/components/SocialCallback/index.vue
new file mode 100644
index 0000000..ede6a75
--- /dev/null
+++ b/zhitan-vue/src/layout/components/SocialCallback/index.vue
@@ -0,0 +1,97 @@
+<template>
+  <div v-loading="loading" class="social-callback"></div>
+</template>
+
+<script setup lang="ts">
+import { login,loginBySocial } from '@/api/login';
+import { setToken, getToken } from '@/utils/auth';
+
+import { LoginData } from '@/api/types';
+import {ElMessage} from "element-plus";
+import {useRoute} from "vue-router";
+import useUserStore from '@/store/modules/user'
+
+const route = useRoute();
+const loading = ref(true);
+
+
+/**
+ * 鎺ユ敹Route浼犻�掔殑鍙傛暟
+ * @param {Object} route.query.
+ */
+const code = route.query.code as string;
+const state = route.query.state as string;
+console.log("state", atob(state))
+const source = route.query.source as string;
+// const stateJson = JSON.parse(atob(state));
+// const domain = stateJson.domain as string;
+console.log("code", code)
+const processResponse = async (res: any) => {
+  if (res.code !== 200) {
+    throw new Error(res.msg);
+  }
+  if (res.data !== null) {
+    setToken(res.token);
+    useUserStore().token = res.token;
+  }
+  ElMessage.success(res.msg);
+  setTimeout(() => {
+    location.href = '/index';
+  }, 2000);
+};
+
+const handleError = (error: any) => {
+  ElMessage.error(error.message);
+  setTimeout(() => {
+    location.href = '/index';
+  }, 2000);
+};
+//
+// const callbackByCode = async (data: LoginData) => {
+//   try {
+//     const res = await callback(data);
+//     await processResponse(res);
+//     loading.value = false;
+//   } catch (error) {
+//     handleError(error);
+//   }
+// };
+
+const loginByCode = async (data: LoginData) => {
+  try {
+    const res = await loginBySocial(data);
+    await processResponse(res);
+    loading.value = false;
+  } catch (error) {
+    handleError(error);
+  }
+};
+
+const init = async () => {
+  // // 濡傛灉鍩熷悕涓嶇浉绛� 鍒欓噸瀹氬悜澶勭悊
+  // let host = window.location.host;
+  // if (domain !== host) {
+  //   let urlFull = new URL(window.location.href);
+  //   urlFull.host = domain;
+  //   window.location.href = urlFull.toString();
+  //   return;
+  // }
+
+  const data: LoginData = {
+    socialCode: code,
+    socialState: state,
+    source: 'keycloak',
+    grantType: 'social'
+  };
+
+
+  await loginByCode(data);
+
+};
+
+onMounted(() => {
+  nextTick(() => {
+    init();
+  });
+});
+</script>
diff --git a/zhitan-vue/src/permission.js b/zhitan-vue/src/permission.js
index 30b428a..e7e14db 100644
--- a/zhitan-vue/src/permission.js
+++ b/zhitan-vue/src/permission.js
@@ -11,7 +11,7 @@
 
 NProgress.configure({ showSpinner: false });
 
-const whiteList = ['/login', '/register', '/energy']
+const whiteList = ['/login', '/register', '/energy','/social-callback']
 
 router.beforeEach((to, from, next) => {
   NProgress.start()
diff --git a/zhitan-vue/src/router/index.js b/zhitan-vue/src/router/index.js
index dff5256..e7ede4b 100644
--- a/zhitan-vue/src/router/index.js
+++ b/zhitan-vue/src/router/index.js
@@ -43,6 +43,11 @@
     hidden: true
   },
   {
+    path: '/social-callback',
+    component: () => import('@/layout/components/SocialCallback/index'),
+    hidden: true
+  },
+  {
     path: '/register',
     component: () => import('@/views/register'),
     hidden: true
diff --git a/zhitan-vue/src/utils/request.js b/zhitan-vue/src/utils/request.js
index 00b910f..f03bbe0 100644
--- a/zhitan-vue/src/utils/request.js
+++ b/zhitan-vue/src/utils/request.js
@@ -27,6 +27,7 @@
   // 鏄惁闇�瑕侀槻姝㈡暟鎹噸澶嶆彁浜�
   const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
   if (getToken() && !isToken) {
+    console.log('getToken', getToken())
     config.headers['Authorization'] = 'Bearer ' + getToken() // 璁╂瘡涓姹傛惡甯﹁嚜瀹氫箟token 璇锋牴鎹疄闄呮儏鍐佃嚜琛屼慨鏀�
   }
   // get璇锋眰鏄犲皠params鍙傛暟
diff --git a/zhitan-vue/src/views/login.vue b/zhitan-vue/src/views/login.vue
index 2fe18dd..f519cf7 100644
--- a/zhitan-vue/src/views/login.vue
+++ b/zhitan-vue/src/views/login.vue
@@ -83,10 +83,11 @@
 </template>
 
 <script setup>
-import { getCodeImg } from "@/api/login"
+import { getCodeImg, authBinding } from "@/api/login"
 import Cookies from "js-cookie"
 import { encrypt, decrypt } from "@/utils/jsencrypt"
 import useUserStore from "@/store/modules/user"
+import {ElMessage} from "element-plus";
 
 const userStore = useUserStore()
 const route = useRoute()
@@ -125,13 +126,13 @@
 const register = ref(false)
 const redirect = ref(undefined)
 
-watch(
-  route,
-  (newRoute) => {
-    redirect.value = newRoute.query && newRoute.query.redirect
-  },
-  { immediate: true }
-)
+// watch(
+//   route,
+//   (newRoute) => {
+//     redirect.value = newRoute.query && newRoute.query.redirect
+//   },
+//   { immediate: true }
+// )
 
 function handleLogin() {
   proxy.$refs.loginRef.validate((valid) => {
@@ -193,8 +194,27 @@
   }
 }
 
+/**
+ * 绗笁鏂圭櫥褰�
+ * @param type
+ */
+const doSocialLogin = () => {
+  console.log("doSocialLogin")
+  authBinding().then((res) => {
+    console.log(res);
+    if (res.code === 200) {
+      // 鑾峰彇鎺堟潈鍦板潃璺宠浆
+      window.location.href = res.data;
+    } else {
+      ElMessage.error(res.msg);
+    }
+  });
+};
+// 闈炲崟鐐圭櫥褰曟斁寮�涓嬭竟涓よ锛屽惁鍒欐敞閲婃帀
 getCode()
 getCookie()
+// 鍗曠偣鐧诲綍鏀惧紑涓嬭竟涓�琛岋紝鍚﹀垯娉ㄩ噴鎺�
+// doSocialLogin()
 </script>
 
 <style lang="scss" scoped>
diff --git a/zhitan-vue/src/views/modelconfiguration/collectindicator/collectIndicator.vue b/zhitan-vue/src/views/modelconfiguration/collectindicator/collectIndicator.vue
index 6b9fab2..5e4bf9e 100644
--- a/zhitan-vue/src/views/modelconfiguration/collectindicator/collectIndicator.vue
+++ b/zhitan-vue/src/views/modelconfiguration/collectindicator/collectIndicator.vue
@@ -171,6 +171,7 @@
 function handleDialog(type) {
   if (type == "add") {
     dialogTitle = "鏂板閲囬泦鍙傛暟妯$増"
+    form.value = {}
   } else {
     dialogTitle = "淇敼閲囬泦鍙傛暟妯$増"
   }
diff --git a/zhitan-vue/vite.config.js b/zhitan-vue/vite.config.js
index a4c5dbc..f1a1f6e 100644
--- a/zhitan-vue/vite.config.js
+++ b/zhitan-vue/vite.config.js
@@ -4,7 +4,7 @@
 
 // https://vitejs.dev/config/
 export default defineConfig(({ mode, command }) => {
-  mode = "production"
+  mode = "development"
   const env = loadEnv(mode, process.cwd())
   console.log(mode, "==========env")
   const { VITE_APP_ENV } = env
@@ -33,7 +33,7 @@
       proxy: {
         // https://cn.vitejs.dev/config/#server-proxy
         "/dev-api": {
-          target: "http://localhost",
+          target: "http://localhost:8080",
           changeOrigin: true,
           rewrite: (p) => p.replace(/^\/dev-api/, ""),
         },

--
Gitblit v1.9.3