Ver código fonte

feat:oauth相关逻辑接口、前端处理oauth相关逻辑

韩帛霖 1 ano atrás
pai
commit
2d86d1bee5
75 arquivos alterados com 5760 adições e 88 exclusões
  1. 47 13
      zkqy-admin/src/main/java/com/zkqy/web/controller/system/SysLoginController.java
  2. 2 0
      zkqy-admin/src/main/java/com/zkqy/web/controller/system/SysMenuController.java
  3. 173 25
      zkqy-admin/src/main/java/com/zkqy/web/ljj.java
  4. 59 1
      zkqy-admin/src/main/resources/application.yml
  5. 5 0
      zkqy-common/pom.xml
  6. 39 0
      zkqy-common/src/main/java/com/zkqy/common/enums/sso/ErrorCodeEnum.java
  7. 35 0
      zkqy-common/src/main/java/com/zkqy/common/enums/sso/ExpireEnum.java
  8. 18 0
      zkqy-common/src/main/java/com/zkqy/common/enums/sso/GrantTypeEnum.java
  9. 34 0
      zkqy-common/src/main/java/com/zkqy/common/enums/sso/ScopeEnum.java
  10. 324 0
      zkqy-common/src/main/java/com/zkqy/common/utils/http/HttpUtilsOauth2.java
  11. 197 0
      zkqy-common/src/main/java/com/zkqy/common/utils/sso/AesUtils.java
  12. 22 0
      zkqy-common/src/main/java/com/zkqy/common/utils/sso/Constants.java
  13. 166 0
      zkqy-common/src/main/java/com/zkqy/common/utils/sso/DateUtils.java
  14. 188 0
      zkqy-common/src/main/java/com/zkqy/common/utils/sso/EncryptUtils.java
  15. 30 0
      zkqy-common/src/main/java/com/zkqy/common/utils/sso/JsonUtils.java
  16. 133 0
      zkqy-common/src/main/java/com/zkqy/common/utils/sso/SpringContextUtils.java
  17. 13 1
      zkqy-framework/pom.xml
  18. 4 1
      zkqy-framework/src/main/java/com/zkqy/framework/config/SecurityConfig.java
  19. 83 0
      zkqy-framework/src/main/java/com/zkqy/framework/config/sso/RestTemplateConfig.java
  20. 64 0
      zkqy-framework/src/main/java/com/zkqy/framework/config/sso/WebMvcConfig.java
  21. 4 0
      zkqy-framework/src/main/java/com/zkqy/framework/security/handle/LogoutSuccessHandlerImpl.java
  22. 325 0
      zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/controller/OauthController.java
  23. 110 0
      zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/controller/ThirdPartLoginController.java
  24. 71 0
      zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/interceptor/AuthAccessTokenInterceptor.java
  25. 39 0
      zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/interceptor/LoginInterceptor.java
  26. 104 0
      zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/interceptor/OauthInterceptor.java
  27. 56 0
      zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/interceptor/SsoAccessDomainInterceptor.java
  28. 66 0
      zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/interceptor/SsoAccessTokenInterceptor.java
  29. 42 0
      zkqy-framework/src/main/java/com/zkqy/framework/web/service/SysLoginService.java
  30. 148 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/AuthAccessToken.java
  31. 49 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/AuthClientDetails.java
  32. 43 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/AuthClientUser.java
  33. 96 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/AuthRefreshToken.java
  34. 35 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/AuthScope.java
  35. 84 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/Func.java
  36. 59 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/Role.java
  37. 33 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/RoleFunc.java
  38. 127 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/SsoAccessToken.java
  39. 70 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/SsoClientDetails.java
  40. 85 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/SsoRefreshToken.java
  41. 99 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/User.java
  42. 41 0
      zkqy-system/src/main/java/com/zkqy/system/domain/sso/UserRole.java
  43. 9 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/SysUserMapper.java
  44. 23 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/sso/AuthAccessTokenMapper.java
  45. 21 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/sso/AuthClientDetailsMapper.java
  46. 20 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/sso/AuthClientUserMapper.java
  47. 26 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/sso/AuthRefreshTokenMapper.java
  48. 20 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/sso/AuthScopeMapper.java
  49. 22 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/sso/SsoAccessTokenMapper.java
  50. 21 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/sso/SsoClientDetailsMapper.java
  51. 24 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/sso/SsoRefreshTokenMapper.java
  52. 19 1
      zkqy-system/src/main/java/com/zkqy/system/service/ISysUserService.java
  53. 24 5
      zkqy-system/src/main/java/com/zkqy/system/service/impl/SysUserServiceImpl.java
  54. 40 0
      zkqy-system/src/main/java/com/zkqy/system/service/sso/AuthorizationService.java
  55. 14 0
      zkqy-system/src/main/java/com/zkqy/system/service/sso/RedisService.java
  56. 32 0
      zkqy-system/src/main/java/com/zkqy/system/service/sso/SsoService.java
  57. 214 0
      zkqy-system/src/main/java/com/zkqy/system/service/sso/impl/AuthorizationServiceImpl.java
  58. 42 0
      zkqy-system/src/main/java/com/zkqy/system/service/sso/impl/RedisServiceImpl.java
  59. 139 0
      zkqy-system/src/main/java/com/zkqy/system/service/sso/impl/SsoServiceImpl.java
  60. 6 0
      zkqy-system/src/main/resources/mapper/system/SysUserMapper.xml
  61. 199 0
      zkqy-system/src/main/resources/mapper/system/sso/AuthAccessTokenMapper.xml
  62. 170 0
      zkqy-system/src/main/resources/mapper/system/sso/AuthClientDetailsMapper.xml
  63. 90 0
      zkqy-system/src/main/resources/mapper/system/sso/AuthClientUserMapper.xml
  64. 147 0
      zkqy-system/src/main/resources/mapper/system/sso/AuthRefreshTokenMapper.xml
  65. 77 0
      zkqy-system/src/main/resources/mapper/system/sso/AuthScopeMapper.xml
  66. 189 0
      zkqy-system/src/main/resources/mapper/system/sso/SsoAccessTokenMapper.xml
  67. 112 0
      zkqy-system/src/main/resources/mapper/system/sso/SsoClientDetailsMapper.xml
  68. 140 0
      zkqy-system/src/main/resources/mapper/system/sso/SsoRefreshTokenMapper.xml
  69. 36 0
      zkqy-ui/src/api/login.js
  70. 33 7
      zkqy-ui/src/layout/components/Sidebar/SidebarItem.vue
  71. 1 1
      zkqy-ui/src/permission.js
  72. 5 0
      zkqy-ui/src/router/index.js
  73. 19 1
      zkqy-ui/src/store/modules/user.js
  74. 348 0
      zkqy-ui/src/views/loading.vue
  75. 56 32
      zkqy-ui/src/views/login.vue

+ 47 - 13
zkqy-admin/src/main/java/com/zkqy/web/controller/system/SysLoginController.java

@@ -1,5 +1,7 @@
 package com.zkqy.web.controller.system;
 
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
 import java.util.List;
 import java.util.Set;
 
@@ -7,6 +9,8 @@ import com.zkqy.business.entity.MobilePageData;
 import com.zkqy.business.service.IMobilePageDataService;
 import com.zkqy.common.core.domain.entity.DataSource;
 import com.zkqy.common.core.domain.entity.SysTenant;
+import com.zkqy.common.core.domain.model.LoginUser;
+import com.zkqy.common.utils.StringUtils;
 import com.zkqy.framework.web.service.TokenService;
 import com.zkqy.system.domain.LoginPageConfiguration;
 import com.zkqy.system.service.*;
@@ -15,10 +19,7 @@ import com.zkqy.system.service.impl.SysTenantServiceImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.validation.BindingResult;
-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 org.springframework.web.bind.annotation.*;
 import com.zkqy.common.constant.Constants;
 import com.zkqy.common.core.domain.AjaxResult;
 import com.zkqy.common.core.domain.entity.SysMenu;
@@ -46,7 +47,7 @@ public class SysLoginController {
 
     @Autowired
     private ISysUserService userService;
-    
+
     @Autowired
     private ISysMenuService menuService;
 
@@ -161,6 +162,42 @@ public class SysLoginController {
         return ajax;
     }
 
+    /**
+     * 单点登录调用模拟登录方法
+     *
+     * @param loginInfo 登录信息
+     * @return 结果
+     */
+    @GetMapping("/Oauth2Login/{loginInfo}")
+    public AjaxResult Oauth2Login(@PathVariable String loginInfo) {
+        if (StringUtils.isEmpty(loginInfo)) {
+            return AjaxResult.error("用户不存在!");
+        }
+        // 恢复(解码)数据
+        byte[] decodedBytes = Base64.getDecoder().decode(loginInfo);
+        String decodedCredentials = new String(decodedBytes, StandardCharsets.UTF_8);
+        String[] code = decodedCredentials.split("\\^\\_\\^");
+        if (code.length != 3) {
+            return AjaxResult.error("用户不存在!");
+        }
+        LoginBody loginBody = new LoginBody();
+        loginBody.setTenantID(code[2]);
+        loginBody.setUsername(code[1]);
+        AjaxResult ajax = AjaxResult.success();
+        // 生成令牌
+        String token = loginService.loginOauth(code[0], code[1]);
+        if (tokenService.getLoginUserIsAdminByToken(token)) {
+            return AjaxResult.error("用户不存在!");
+        }
+        // 校验租户是否过期
+        String checkTenantExpirationTimeMsg = loginService.checkTenantExpirationTime(loginBody.getTenantID() + "¥¥¥" + loginBody.getUsername());
+        if (!checkTenantExpirationTimeMsg.isEmpty()) {
+            return AjaxResult.error(checkTenantExpirationTimeMsg);
+        }
+        ajax.put(Constants.TOKEN, token);
+        return ajax;
+    }
+
     /**
      * 验证租户i是否存在有效
      *
@@ -171,7 +208,7 @@ public class SysLoginController {
     public AjaxResult isTenantExist(String tenantCode) {
         SysTenant sysTenantInfo = iSysTenantService.selectSysTenantByTenantCode(tenantCode);
         if (sysTenantInfo != null) {
-            sysTenantInfo.setLoginPageConfiguration(loginPageConfigurationService.selectLoginPageConfigurationByLoginPageNumber(tenantCode,"client"));
+            sysTenantInfo.setLoginPageConfiguration(loginPageConfigurationService.selectLoginPageConfigurationByLoginPageNumber(tenantCode, "client"));
             return AjaxResult.success(sysTenantInfo);
         }
         return AjaxResult.error("租户不存在");
@@ -198,7 +235,7 @@ public class SysLoginController {
             return AjaxResult.error("用户不存在!");
         }
         //检查租户过期时间
-        String checkTenantExpirationTimeMsg = loginService.checkTenantExpirationTime(loginBody.getTenantID() + "¥¥¥" +loginBody.getUsername());
+        String checkTenantExpirationTimeMsg = loginService.checkTenantExpirationTime(loginBody.getTenantID() + "¥¥¥" + loginBody.getUsername());
         if (!checkTenantExpirationTimeMsg.isEmpty()) {
             return AjaxResult.error(checkTenantExpirationTimeMsg);
         }
@@ -209,7 +246,6 @@ public class SysLoginController {
     }
 
 
-
     /**
      * 平台移动端通用登录方法
      */
@@ -221,7 +257,7 @@ public class SysLoginController {
         if (loginBody.getUsername().equals("admin")) {
             return AjaxResult.error("未有此用户信息");
         }
-        if(loginBody.getUsername().indexOf("@")==-1){
+        if (loginBody.getUsername().indexOf("@") == -1) {
             return AjaxResult.error("请输入正确的用户名");
         }
         // 获取登录用户名
@@ -238,7 +274,7 @@ public class SysLoginController {
             return AjaxResult.error("用户不存在!");
         }
         //检查租户过期时间
-        String checkTenantExpirationTimeMsg = loginService.checkTenantExpirationTime(loginBody.getTenantID() + "¥¥¥" +loginBody.getUsername());
+        String checkTenantExpirationTimeMsg = loginService.checkTenantExpirationTime(loginBody.getTenantID() + "¥¥¥" + loginBody.getUsername());
         if (!checkTenantExpirationTimeMsg.isEmpty()) {
             return AjaxResult.error(checkTenantExpirationTimeMsg);
         }
@@ -249,8 +285,6 @@ public class SysLoginController {
     }
 
 
-
-
     /**
      * 登录方法
      *
@@ -315,7 +349,7 @@ public class SysLoginController {
      */
     @GetMapping("getRouters")
     public AjaxResult getRouters() {
-        Long userId = SecurityUtils.getUserId();
+        Long userId = SecurityUtils.getUserId() == null ? SecurityUtils.getLoginUser().getUser().getUserId() : SecurityUtils.getUserId();
         List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
         return AjaxResult.success(menuService.buildMenus(menus));
     }

+ 2 - 0
zkqy-admin/src/main/java/com/zkqy/web/controller/system/SysMenuController.java

@@ -2,6 +2,8 @@ package com.zkqy.web.controller.system;
 
 import java.util.List;
 
+import com.zkqy.common.core.domain.entity.SysUser;
+import com.zkqy.common.core.domain.model.LoginUser;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;

+ 173 - 25
zkqy-admin/src/main/java/com/zkqy/web/ljj.java

@@ -2,30 +2,22 @@ package com.zkqy.web;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 
+import java.time.*;
+
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Map;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
 
-import com.alibaba.fastjson2.JSON;
-import com.alibaba.fastjson2.JSONArray;
-import com.alibaba.fastjson2.JSONObject;
 import com.zkqy.common.annotation.Anonymous;
-import com.zkqy.common.exception.file.FileException;
-import lombok.val;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
-import springfox.documentation.spring.web.json.Json;
 
-import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 
@@ -36,6 +28,55 @@ import java.util.concurrent.CompletableFuture;
 public class ljj {
     @Autowired
     private ThreadPoolTaskExecutor threadPoolTaskExecutor;
+    private boolean isnot;
+
+    public boolean isIsnot() {
+        return isnot;
+    }
+
+    public void setIsnot(boolean isnot) {
+        this.isnot = isnot;
+    }
+
+    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMdd");
+    private static final DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("HH:mm:ss");
+
+    @Test
+    public void asd() {
+
+//        LocalDate now = LocalDate.now(ZoneId.systemDefault());
+//        StringBuilder sb = new StringBuilder(now.format(formatter));
+//        StringBuilder sb1 = new StringBuilder(now.format(formatter1));
+//        System.out.println(sb1);
+//        sb.append("00001");
+//        String nowCodeList = sb.toString();
+//        System.out.println(nowCodeList);
+
+//        LocalDateTime now = LocalDateTime.now(); // 获取当前日期和时间
+//        LocalTime time = now.toLocalTime(); // 只获取时间部分
+//
+//        int minute = time.getMinute(); // 获取分钟
+//        int second = time.getSecond(); // 获取秒
+//        int nano = time.getNano(); // 获取纳秒(注意:不是毫秒)
+//        int millis = now.toLocalTime().toSecondOfDay() * 1000 + time.getNano() / 1_000_000; // 计算毫秒
+//
+//        System.out.println("分钟: " + minute);
+//        System.out.println("秒: " + second);
+//        System.out.println("毫秒: " + millis); // 注意:这种方法得到的毫秒可能不完全准确,因为它依赖于纳秒的舍入
+//        System.out.println("纳秒:" + nano);
+
+//        System.out.println(isnot);
+//
+//        String aa = "2024-05-21T00:00";
+//        System.out.println(aa.split("T")[0]);
+//        System.out.println(aa.split("U")[0]);
+
+//        DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+//
+//        LocalDateTime dateTime = LocalDateTime.parse(aa);
+//        String formattedDate = dateTime.format(outputFormatter);
+//        System.err.println(formattedDate);
+    }
 
     public static Map<String, Object> readJsonFileToMap(String filePath) throws IOException {
         ObjectMapper objectMapper = new ObjectMapper(); // 创建一个ObjectMapper实例
@@ -53,21 +94,112 @@ public class ljj {
     }
 
     public static void main(String[] args) throws IOException {
-        String filePath = "/Users/zrwj/Desktop/ZKQY_LJJ/Intelligent_manufacturing/MEC_CLIENT/zkqy-admin/src/main/resources/asd.json";
-        Map<String, Object> jsonDataAsMap = readJsonFileToMap(filePath);
-
-
-        ((Map) jsonDataAsMap.get("mainForm")).get("formKey");
-
-
-        System.out.println(((Map) jsonDataAsMap.get("mainForm")).get("formKey"));
-
-        ((ArrayList)jsonDataAsMap.get("subFormList")).forEach(item->{
-            System.out.println(((Map)item).get("formKey"));
-            System.out.println(((Map)item).put("123123","123123"));
-            System.out.println(item);
+        List<LinkedHashMap<String, String>> linkedHashMapList = new ArrayList<>();
+        LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
+        linkedHashMap.put("qwe", "测试1");
+        linkedHashMap.put("asd", "测试2");
+        linkedHashMap.put("zxc", "测试3");
+        linkedHashMapList.add(linkedHashMap);
+        LinkedHashMap<String, String> linkedHashMap1 = new LinkedHashMap<>();
+        linkedHashMap1.put("asd", "测试2");
+        linkedHashMap1.put("qwe", "测试1");
+        linkedHashMap1.put("zxc", "测试3");
+        linkedHashMapList.add(linkedHashMap1);
+
+        List<String> sequenceMap = new ArrayList<>(linkedHashMap.keySet());
+
+        // 交换键值对位置
+//        for (LinkedHashMap<String, String> ltem : linkedHashMapList) {
+//            for (Map.Entry<String, String> entry : ltem.entrySet()) {
+//
+//                String key = entry.getKey();
+//                String value = entry.getValue();
+//                ltem.put(key, value + "_swapped");
+//            }
+//            System.out.println(ltem);
+//        }
+
+
+        for (int e = 0; e < sequenceMap.size(); e++) {
+            for (int i = 0; i < linkedHashMapList.size(); i++) {
+                String entry = sequenceMap.get(e);
+                String val = linkedHashMapList.get(i).get(entry);
+                linkedHashMapList.get(i).remove(entry);
+                linkedHashMapList.get(i).put(entry, val);
+            }
+        }
+//            for (LinkedHashMap<String, String> ltem : linkedHashMapList) {
+//                ltem.get(entry);
+//                String val = ltem.get(entry);
+//                ltem.remove(entry);
+//                ltem.put(entry, val);
+//                System.out.println(ltem);
+//            }
+
+//        sequenceMap.forEach(item -> {
+//            System.out.println(item);
+//            linkedHashMapList.forEach(ltem -> {
+//                ltem.get(item);
+//                String val = ltem.get(item);
+//                ltem.remove(item);
+//                ltem.put(item, val);
+//                System.out.println(ltem);
+//            });
+//        });
+        linkedHashMapList.forEach(ltem -> {
+            System.out.println(ltem);
         });
 
+//        String where = "id=qwe";
+//
+//        String endWhere = where.split("=")[0] + "=\'" + where.split("=")[1] + "\'";
+//        System.out.println(endWhere);
+
+//        SysTenant sysTenant = new SysTenant();
+//        SysTenant sysTenant1 = null;
+//        Assert.notNull(sysTenant1, "测试1");
+//        Assert.isNull(sysTenant, "测试2");
+
+
+//        String json = "{\"insertCommonEntityList\":[{\"basicMap\":{\"tableName\":\"product_table\"},\"addListMap\":[{\"remark\":\"11lph\",\"order_id\":\"2b14c598d0e4-4d1f-9de7-a1196e9c839a\",\"task_node_key\":\"\",\"product_name\":\"11\",\"task_process_key\":\"\",\"process_key\":\"\"}]}],\"updateCommonEntityList\":[{\"basicMap\":{\"tableName\":\"order_table\"},\"commMap\":[{\"order_num\":\"2b14c598d0e4-4d1f-9de7-a1196e9c839a\",\"name\":\"张三多个产品lph\",\"remark\":\"备注lph\"}],\"conditionMap\":{\"order_num\":\"2b14c598d0e4-4d1f-9de7-a1196e9c839a\"}}],\"deleteCommonEntityList\":[{\"basicMap\":{\"tableName\":\"product_table\"},\"conditionMap\":{\"id\":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]}}]}";
+//        Gson gson = new Gson();
+//        BpmRunNodeFormDateVo bpmRunNodeFormDateVo = gson.fromJson(json, BpmRunNodeFormDateVo.class);
+
+//        String asd = "{\"xzgylc\":[{\"label\":\"生产执行流程-纺丝\",\"value\":\"Process_1706665793030\"},{\"label\":\"生产执行流程-加弹\",\"value\":\"Process_1709533458552\"},{\"label\":\"生产执行流程-调丝\",\"value\":\"Process_1709535753931\"}],\"xzcx\":[{\"label\":\"一号产线\",\"value\":\"001\"},{\"label\":\"二号产线\",\"value\":\"002\"},{\"label\":\"三号产线\",\"value\":\"003\"}],\"hpmc\":[{\"label\":\"硬纤维\",\"value\":\"001\"},{\"label\":\"尼龙\",\"value\":\"002\"},{\"label\":\"纤维\",\"value\":\"003\"}],\"qpxh\":[{\"label\":\"A母粒\",\"value\":\"ML_001\"},{\"label\":\"油剂\",\"value\":\"YJ_001\"},{\"label\":\"切片\",\"value\":\"022\"},{\"label\":\"半透明\",\"value\":\"qp_001\"},{\"label\":\"母粒123\",\"value\":\"ML_003\"},{\"label\":\"再生切片\",\"value\":\"qp_002\"}],\"sh\":[{\"label\":\"蓝色\",\"value\":\"ML_001\"},{\"value\":\"YJ_001\"},{\"value\":\"022\"},{\"label\":\"\",\"value\":\"qp_001\"},{\"label\":\"浅蓝\",\"value\":\"ML_003\"},{\"label\":\"\",\"value\":\"qp_002\"}]}";
+//
+//        Map<String, Object> map = (Map<String, Object>) JSON.parse(asd);
+//        System.out.println(map);
+//        Map<String, Object> map = new HashMap<String, Object>();
+//        map.put("val1", "vvval1");
+//        map.put("val2", "vvval2");
+//        map.put("val3", "vvval3");
+//        map.put("val5", "vvval4");
+//        map.put("sqlkey", "val4");
+//        map.put("sqlkey1", "val4");
+//
+//        map.remove("sqlkey123123");
+//        map.remove("sqlkey1");
+//
+//        map.forEach((k, v) -> {
+//            System.out.println(k + v);
+//        });
+
+
+//        String filePath = "/Users/zrwj/Desktop/ZKQY_LJJ/Intelligent_manufacturing/MEC_CLIENT/zkqy-admin/src/main/resources/asd.json";
+//        Map<String, Object> jsonDataAsMap = readJsonFileToMap(filePath);
+//
+//
+//        ((Map) jsonDataAsMap.get("mainForm")).get("formKey");
+//
+//
+//        System.out.println(((Map) jsonDataAsMap.get("mainForm")).get("formKey"));
+//
+//        ((ArrayList)jsonDataAsMap.get("subFormList")).forEach(item->{
+//            System.out.println(((Map)item).get("formKey"));
+//            System.out.println(((Map)item).put("123123","123123"));
+//            System.out.println(item);
+//        });
+
     }
 
     @GetMapping("/asd")
@@ -97,3 +229,19 @@ public class ljj {
 
 
 }
+
+
+
+
+/*
+恒泰生产协同管理系统移动端:销售管理下创建销售单、销售单查询、客户管理;采购管理下创建采购单、查询采购单;库房管理下入库日志、出库日志、出库单;生产管理下生产计划、生产任务、生产日志、
+
+司机端下我的订单
+
+
+恒泰生产协同管理系统PC端实现了:销售管理下订单管理、发货单、客户管理;生产管理下生产计划、生产执行、生产投料;产品管理下产品维护、包装维护;原材料管理下采购管理、供应商管理、原材料维护、入库单、原材料库存;设备管理下设备维护;资源管理下耗材管理、耗材入库明细、耗材出库明细;质量管理下质量检验;库房管理下综合管理、出库管理、入库管理、盘点管理;看板管理下工厂纵览、生产纵览;报表导出下有出库明细、销售日报、采购报表、客户报表、销售员报表
+
+恒泰生产协同管理系统手持机(PDA)实现了:扫码入库、入库日志、扫码拆包、出库单、数据统计、消息通知
+
+
+ */

+ 59 - 1
zkqy-admin/src/main/resources/application.yml

@@ -123,6 +123,45 @@ pagehelper:
   supportMethodsArguments: true
   params: count=countSql
 
+# Shiro
+shiro:
+  user:
+    # 登录地址
+    loginUrl: /login
+    # 权限认证失败地址
+    unauthorizedUrl: /unauth
+    # 首页地址
+    indexUrl: /index
+    # 验证码开关
+    captchaEnabled: true
+    # 验证码类型 math 数组计算 char 字符
+    captchaType: math
+  cookie:
+    # 设置Cookie的域名 默认空,即当前访问的域名
+    domain:
+    # 设置cookie的有效访问路径
+    path: /
+    # 设置HttpOnly属性
+    httpOnly: true
+    # 设置Cookie的过期时间,天为单位
+    maxAge: 30
+    # 设置密钥,务必保持唯一性(生成方式,直接拷贝到main运行即可)Base64.encodeToString(CipherUtils.generateNewKey(128, "AES").getEncoded()) (默认启动生成随机秘钥,随机秘钥会导致之前客户端RememberMe Cookie无效,如设置固定秘钥RememberMe Cookie则有效)
+    cipherKey:
+  session:
+    # Session超时时间,-1代表永不过期(默认30分钟)
+    expireTime: 30
+    # 同步session到数据库的周期(默认1分钟)
+    dbSyncPeriod: 1
+    # 相隔多久检查一次session的有效性,默认就是10分钟
+    validationInterval: 10
+    # 同一个用户最大会话数,比如2的意思是同一个账号允许最多同时两个人登录(默认-1不限制)
+    maxSession: -1
+    # 踢出之前登录的/之后登录的用户,默认踢出之前登录的用户
+    kickoutAfter: false
+  rememberMe:
+    # 是否开启记住我
+    enabled: true
+
 # Swagger配置
 swagger:
   # 是否开启swagger
@@ -139,5 +178,24 @@ xss:
   # 匹配链接
   urlPatterns: /system/*,/monitor/*,/tool/*
 
+# cas配置
+cas:
+  client-name: CasClient
+  server:
+    url: http://localhost:8080/cas
+  project:
+    url: http://localhost:80
+
 projectDownloadZip:
-  mysql: /usr/local/mysql/bin/mysqldump
+  mysql: /usr/local/mysql/bin/mysqldump
+
+OpenAuthorization2:
+  MES:
+    # 单点获取code、token、userinfo主机地址
+    URL: http://192.168.110.59:8066/oauth2
+    # 系统标识
+    BASIC: mes
+    # 重定向本系统主机地址
+    REDIRECT_URL: http://192.168.110.59:1025
+    # 回调地址
+    CALLBACK: http://192.168.110.59:8066/oauth/callback

+ 5 - 0
zkqy-common/pom.xml

@@ -169,6 +169,11 @@
             <artifactId>ant</artifactId>
             <version>1.9.1</version>
         </dependency>
+        <!--HTTP-->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 39 - 0
zkqy-common/src/main/java/com/zkqy/common/enums/sso/ErrorCodeEnum.java

@@ -0,0 +1,39 @@
+package com.zkqy.common.enums.sso;
+
+/**
+ * 错误码相关枚举类
+ *
+ */
+public enum  ErrorCodeEnum {
+    INVALID_REQUEST("invalid_request","请求缺少某个必需参数,包含一个不支持的参数或参数值,或者格式不正确。")
+    ,INVALID_CLIENT("invalid_client","请求的client_id或client_secret参数无效。")
+    ,INVALID_GRANT("invalid_grant","请求的Authorization Code、Access Token、Refresh Token等信息是无效的。")
+    ,UNSUPPORTED_GRANT_TYPE("unsupported_grant_type","不支持的grant_type。")
+    ,INVALID_SCOPE("invalid_scope","请求的scope参数是无效的、未知的、格式不正确的,或所请求的权限范围超过了数据拥有者所授予的权限范围。")
+    ,EXPIRED_TOKEN("expired_token","请求的Access Token或Refresh Token已过期。")
+    ,REDIRECT_URI_MISMATCH("redirect_uri_mismatch","请求的redirect_uri所在的域名与开发者注册应用时所填写的域名不匹配。")
+    ,INVALID_REDIRECT_URI("invalid_redirect_uri","请求的回调URL不在白名单中。")
+    ,UNKNOWN_ERROR("unknown_error","程序发生未知异常,请联系管理员解决。");
+
+    /**
+     * 错误码
+     */
+    private String error;
+    /**
+     * 错误描述信息
+     */
+    private String errorDescription;
+
+    ErrorCodeEnum(String error, String errorDescription) {
+        this.error = error;
+        this.errorDescription = errorDescription;
+    }
+
+    public String getError() {
+        return error;
+    }
+
+    public String getErrorDescription() {
+        return errorDescription;
+    }
+}

+ 35 - 0
zkqy-common/src/main/java/com/zkqy/common/enums/sso/ExpireEnum.java

@@ -0,0 +1,35 @@
+package com.zkqy.common.enums.sso;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 过期时间相关枚举
+ */
+public enum ExpireEnum {
+  AUTHORIZATION_CODE(10L, TimeUnit.MINUTES),
+  //Access Token的有效期为30天
+  ACCESS_TOKEN(30L, TimeUnit.DAYS),
+  REFRESH_TOKEN(365L, TimeUnit.DAYS);
+
+  /**
+   * 过期时间
+   */
+  private Long time;
+  /**
+   * 时间单位
+   */
+  private TimeUnit timeUnit;
+
+  ExpireEnum(Long time, TimeUnit timeUnit) {
+    this.time = time;
+    this.timeUnit = timeUnit;
+  }
+
+  public Long getTime() {
+    return time;
+  }
+
+  public TimeUnit getTimeUnit() {
+    return timeUnit;
+  }
+}

+ 18 - 0
zkqy-common/src/main/java/com/zkqy/common/enums/sso/GrantTypeEnum.java

@@ -0,0 +1,18 @@
+package com.zkqy.common.enums.sso;
+
+/**
+ * 授权方式.
+ */
+public enum GrantTypeEnum {
+  AUTHORIZATION_CODE("authorization_code");
+
+  private String type;
+
+  GrantTypeEnum(String type) {
+    this.type = type;
+  }
+
+  public String getType() {
+    return type;
+  }
+}

+ 34 - 0
zkqy-common/src/main/java/com/zkqy/common/enums/sso/ScopeEnum.java

@@ -0,0 +1,34 @@
+package com.zkqy.common.enums.sso;
+
+/**
+ * 权限范围
+ *
+ */
+public enum ScopeEnum {
+  BASIC("basic", "基础权限"),
+  SUPER("super", "所有权限");
+
+  private String code;
+  private String description;
+
+  ScopeEnum(String code, String description) {
+    this.code = code;
+    this.description = description;
+  }
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+}

+ 324 - 0
zkqy-common/src/main/java/com/zkqy/common/utils/http/HttpUtilsOauth2.java

@@ -0,0 +1,324 @@
+package com.zkqy.common.utils.http;
+
+import com.zkqy.common.utils.StringUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author: monkey
+ * @createTime: 2023-08-20 09:33
+ **/
+public class HttpUtilsOauth2 {
+
+    /**
+     * get
+     *
+     * @param host
+     * @param path
+     * @param method
+     * @param headers
+     * @param querys
+     * @return
+     * @throws Exception
+     */
+    public static HttpResponse doGet(String host, String path, String method,
+                                     Map<String, String> headers,
+                                     Map<String, String> querys)
+            throws Exception {
+        HttpClient httpClient = wrapClient(host);
+
+        HttpGet request = new HttpGet(buildUrl(host, path, querys));
+        for (Map.Entry<String, String> e : headers.entrySet()) {
+            request.addHeader(e.getKey(), e.getValue());
+        }
+
+        return httpClient.execute(request);
+    }
+
+    /**
+     * post form
+     *
+     * @param host
+     * @param path
+     * @param method
+     * @param headers
+     * @param querys
+     * @param bodys
+     * @return
+     * @throws Exception
+     */
+    public static HttpResponse doPost(String host, String path, String method,
+                                      Map<String, String> headers,
+                                      Map<String, String> querys,
+                                      Map<String, String> bodys)
+            throws Exception {
+        HttpClient httpClient = wrapClient(host);
+
+        HttpPost request = new HttpPost(buildUrl(host, path, querys));
+        for (Map.Entry<String, String> e : headers.entrySet()) {
+            request.addHeader(e.getKey(), e.getValue());
+        }
+
+        if (bodys.size()!=0&&bodys != null) {
+            List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
+
+            for (String key : bodys.keySet()) {
+                nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key)));
+            }
+            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8");
+//            formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
+            request.setEntity(formEntity);
+        }
+
+        return httpClient.execute(request);
+    }
+
+    /**
+     * Post String
+     *
+     * @param host
+     * @param path
+     * @param method
+     * @param headers
+     * @param querys
+     * @param body
+     * @return
+     * @throws Exception
+     */
+    public static HttpResponse doPost(String host, String path, String method,
+                                      Map<String, String> headers,
+                                      Map<String, String> querys,
+                                      String body)
+            throws Exception {
+        HttpClient httpClient = wrapClient(host);
+
+        HttpPost request = new HttpPost(buildUrl(host, path, querys));
+        for (Map.Entry<String, String> e : headers.entrySet()) {
+            request.addHeader(e.getKey(), e.getValue());
+        }
+
+        if (StringUtils.isNotNull(body)) {
+            request.setEntity(new StringEntity(body, "utf-8"));
+        }
+
+        return httpClient.execute(request);
+    }
+
+    /**
+     * Post stream
+     *
+     * @param host
+     * @param path
+     * @param method
+     * @param headers
+     * @param querys
+     * @param body
+     * @return
+     * @throws Exception
+     */
+    public static HttpResponse doPost(String host, String path, String method,
+                                      Map<String, String> headers,
+                                      Map<String, String> querys,
+                                      byte[] body)
+            throws Exception {
+        HttpClient httpClient = wrapClient(host);
+
+        HttpPost request = new HttpPost(buildUrl(host, path, querys));
+        for (Map.Entry<String, String> e : headers.entrySet()) {
+            request.addHeader(e.getKey(), e.getValue());
+        }
+
+        if (body != null) {
+            request.setEntity(new ByteArrayEntity(body));
+        }
+
+        return httpClient.execute(request);
+    }
+
+    /**
+     * Put String
+     *
+     * @param host
+     * @param path
+     * @param method
+     * @param headers
+     * @param querys
+     * @param body
+     * @return
+     * @throws Exception
+     */
+    public static HttpResponse doPut(String host, String path, String method,
+                                     Map<String, String> headers,
+                                     Map<String, String> querys,
+                                     String body)
+            throws Exception {
+        HttpClient httpClient = wrapClient(host);
+
+        HttpPut request = new HttpPut(buildUrl(host, path, querys));
+        for (Map.Entry<String, String> e : headers.entrySet()) {
+            request.addHeader(e.getKey(), e.getValue());
+        }
+
+        if (StringUtils.isNotBlank(body)) {
+            request.setEntity(new StringEntity(body, "utf-8"));
+        }
+
+        return httpClient.execute(request);
+    }
+
+    /**
+     * Put stream
+     *
+     * @param host
+     * @param path
+     * @param method
+     * @param headers
+     * @param querys
+     * @param body
+     * @return
+     * @throws Exception
+     */
+    public static HttpResponse doPut(String host, String path, String method,
+                                     Map<String, String> headers,
+                                     Map<String, String> querys,
+                                     byte[] body)
+            throws Exception {
+        HttpClient httpClient = wrapClient(host);
+
+        HttpPut request = new HttpPut(buildUrl(host, path, querys));
+        for (Map.Entry<String, String> e : headers.entrySet()) {
+            request.addHeader(e.getKey(), e.getValue());
+        }
+
+        if (body != null) {
+            request.setEntity(new ByteArrayEntity(body));
+        }
+
+        return httpClient.execute(request);
+    }
+
+    /**
+     * Delete
+     *
+     * @param host
+     * @param path
+     * @param method
+     * @param headers
+     * @param querys
+     * @return
+     * @throws Exception
+     */
+    public static HttpResponse doDelete(String host, String path, String method,
+                                        Map<String, String> headers,
+                                        Map<String, String> querys)
+            throws Exception {
+        HttpClient httpClient = wrapClient(host);
+
+        HttpDelete request = new HttpDelete(buildUrl(host, path, querys));
+        for (Map.Entry<String, String> e : headers.entrySet()) {
+            request.addHeader(e.getKey(), e.getValue());
+        }
+
+        return httpClient.execute(request);
+    }
+
+    private static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException {
+        StringBuilder sbUrl = new StringBuilder();
+        sbUrl.append(host);
+        if (!StringUtils.isBlank(path)) {
+            sbUrl.append(path);
+        }
+        if (null != querys) {
+            StringBuilder sbQuery = new StringBuilder();
+            for (Map.Entry<String, String> query : querys.entrySet()) {
+                if (0 < sbQuery.length()) {
+                    sbQuery.append("&");
+                }
+                if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
+                    sbQuery.append(query.getValue());
+                }
+                if (!StringUtils.isBlank(query.getKey())) {
+                    sbQuery.append(query.getKey());
+                    if (!StringUtils.isBlank(query.getValue())) {
+                        sbQuery.append("=");
+                        sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8"));
+                    }
+                }
+            }
+            if (0 < sbQuery.length()) {
+                sbUrl.append("?").append(sbQuery);
+            }
+        }
+
+        return sbUrl.toString();
+    }
+
+    private static HttpClient wrapClient(String host) {
+        HttpClient httpClient = new DefaultHttpClient();
+        if (host.startsWith("https://")) {
+            sslClient(httpClient);
+        }
+
+        return httpClient;
+    }
+
+    private static void sslClient(HttpClient httpClient) {
+        try {
+            SSLContext ctx = SSLContext.getInstance("TLS");
+            X509TrustManager tm = new X509TrustManager() {
+                @Override
+                public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+
+                @Override
+                public void checkClientTrusted(X509Certificate[] xcs, String str) {
+
+                }
+
+                @Override
+                public void checkServerTrusted(X509Certificate[] xcs, String str) {
+
+                }
+            };
+            ctx.init(null, new TrustManager[]{tm}, null);
+            SSLSocketFactory ssf = new SSLSocketFactory(ctx);
+            ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+            ClientConnectionManager ccm = httpClient.getConnectionManager();
+            SchemeRegistry registry = ccm.getSchemeRegistry();
+            registry.register(new Scheme("https", 443, ssf));
+        } catch (KeyManagementException ex) {
+            throw new RuntimeException(ex);
+        } catch (NoSuchAlgorithmException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+
+}

+ 197 - 0
zkqy-common/src/main/java/com/zkqy/common/utils/sso/AesUtils.java

@@ -0,0 +1,197 @@
+package com.zkqy.common.utils.sso;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.text.MessageFormat;
+
+
+public class AesUtils {
+
+  /**
+   * AES加解密
+   */
+  private static final String ALGORITHM = "AES";
+  /**
+   * 默认的初始化向量值
+   */
+  private static final String IV_DEFAULT = "g8v20drvOmIx2PuR";
+  /**
+   * 默认加密的KEY
+   */
+  private static final String KEY_DEFAULT = "8G5M4Ff9hel8fUA9";
+  /**
+   * 工作模式:CBC
+   */
+  private static final String TRANSFORM_CBC_PKCS5 = "AES/CBC/PKCS5Padding";
+
+  /**
+   * 工作模式:ECB
+   */
+  private static final String TRANSFORM_ECB_PKCS5 = "AES/ECB/PKCS5Padding";
+
+
+  public static String encryptCbcMode(final String value, String key, String iv) {
+    if (StringUtils.isNoneBlank(value)) {
+      //如果key或iv为空,则使用默认值
+      if (key == null || key.length() != 16) {
+        key = KEY_DEFAULT;
+      }
+      if (iv == null || iv.length() != 16) {
+        iv = IV_DEFAULT;
+      }
+
+      final SecretKeySpec keySpec = getSecretKey(key);
+
+      //初始化向量器
+      final IvParameterSpec ivParameterSpec = new IvParameterSpec(getUTF8Bytes(iv));
+
+      try {
+        Cipher encipher = Cipher.getInstance(TRANSFORM_CBC_PKCS5);
+
+        //加密模式
+        encipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec);
+        //使用AES加密
+        byte[] encrypted = encipher.doFinal(getUTF8Bytes(value));
+        //然后转成BASE64返回
+        return Base64.encodeBase64String(encrypted);
+      } catch (Exception e) {
+        System.out.println(
+            MessageFormat.format("基于CBC工作模式的AES加密失败,VALUE:{0},KEY:{1}", value, key));
+        e.printStackTrace();
+      }
+    }
+
+    return null;
+  }
+
+
+  public static String decryptCbcMode(final String encryptedStr, String key, String iv) {
+    if (StringUtils.isNoneBlank(encryptedStr)) {
+      //如果key或iv为空,则使用默认值
+      if (key == null || key.length() != 16) {
+        key = KEY_DEFAULT;
+      }
+      if (iv == null || iv.length() != 16) {
+        iv = IV_DEFAULT;
+      }
+
+      final SecretKeySpec keySpec = getSecretKey(key);
+      final IvParameterSpec ivParameterSpec = new IvParameterSpec(getUTF8Bytes(iv));
+
+      try {
+        Cipher encipher = Cipher.getInstance(TRANSFORM_CBC_PKCS5);
+        encipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
+        //先用BASE64解密
+        byte[] encryptedBytes = Base64.decodeBase64(encryptedStr);
+        //然后再AES解密
+        byte[] originalBytes = encipher.doFinal(encryptedBytes);
+        //返回字符串
+        return new String(originalBytes);
+      } catch (Exception e) {
+        System.out.println(
+            MessageFormat.format("基于CBC工作模式的AES解密失败,encryptedStr:{0},KEY:{1}",
+                encryptedStr, key));
+        e.printStackTrace();
+      }
+    }
+
+    return null;
+  }
+
+  public static String encryptEcbMode(final String value, String key) {
+    if (StringUtils.isNoneBlank(value)) {
+      if (key == null || key.length() != 16) {
+        key = KEY_DEFAULT;
+      }
+
+      //密码
+      final SecretKeySpec keySpec = getSecretKey(key);
+
+      try {
+        Cipher encipher = Cipher.getInstance(TRANSFORM_ECB_PKCS5);
+
+        //加密模式
+        encipher.init(Cipher.ENCRYPT_MODE, keySpec);
+        //使用AES加密
+        byte[] encrypted = encipher.doFinal(getUTF8Bytes(value));
+        //然后转成BASE64返回
+        return Base64.encodeBase64String(encrypted);
+      } catch (Exception e) {
+        System.out.println(
+            MessageFormat.format("基于ECB工作模式的AES加密失败,VALUE:{0},KEY:{1}", value, key));
+        e.printStackTrace();
+      }
+    }
+
+    return null;
+  }
+
+  public static String decryptEcbMode(final String encryptedStr, String key) {
+    if (StringUtils.isNoneBlank(encryptedStr)) {
+      //如果key为空,则使用默认值
+      if (key == null || key.length() != 16) {
+        key = KEY_DEFAULT;
+      }
+
+      //密码
+      final SecretKeySpec keySpec = getSecretKey(key);
+
+      try {
+        Cipher encipher = Cipher.getInstance(TRANSFORM_ECB_PKCS5);
+
+        //加密模式
+        encipher.init(Cipher.DECRYPT_MODE, keySpec);
+        //先用BASE64解密
+        byte[] encryptedBytes = Base64.decodeBase64(encryptedStr);
+        //然后再AES解密
+        byte[] originalBytes = encipher.doFinal(encryptedBytes);
+        //返回字符串
+        return new String(originalBytes);
+      } catch (Exception e) {
+        System.out.println(
+            MessageFormat.format("基于ECB工作模式的AES解密失败,encryptedStr:{0},KEY:{1}",
+                encryptedStr, key));
+        e.printStackTrace();
+      }
+    }
+
+    return null;
+  }
+
+  private static byte[] getUTF8Bytes(String input) {
+    return input.getBytes(StandardCharsets.UTF_8);
+  }
+
+  private static SecretKeySpec getSecretKey(final String KEY) {
+    //生成指定算法密钥
+    KeyGenerator generator = null;
+
+    try {
+      generator = KeyGenerator.getInstance(ALGORITHM);
+
+      //指定AES密钥长度(128、192、256)
+      generator.init(256, new SecureRandom(getUTF8Bytes(KEY)));
+
+      //生成一个密钥
+      SecretKey secretKey = generator.generateKey();
+      //转换为AES专用密钥
+      return new SecretKeySpec(secretKey.getEncoded(), ALGORITHM);
+    } catch (NoSuchAlgorithmException ex) {
+      System.out.println(MessageFormat.format("生成加密秘钥失败,KEY:{0}", KEY));
+      ex.printStackTrace();
+    }
+
+    return null;
+  }
+
+
+}

+ 22 - 0
zkqy-common/src/main/java/com/zkqy/common/utils/sso/Constants.java

@@ -0,0 +1,22 @@
+package com.zkqy.common.utils.sso;
+
+/**
+ * 公共常量类
+ *
+ */
+public class Constants {
+    /**
+     * 用户信息在session中存储的变量名
+     */
+    public static final String SESSION_USER = "SESSION_USER";
+
+    /**
+     * 登录页面的回调地址在session中存储的变量名
+     */
+    public static final String SESSION_LOGIN_REDIRECT_URL = "LOGIN_REDIRECT_URL";
+
+    /**
+     * 授权页面的回调地址在session中存储的变量名
+     */
+    public static final String SESSION_AUTH_REDIRECT_URL = "SESSION_AUTH_REDIRECT_URL";
+}

+ 166 - 0
zkqy-common/src/main/java/com/zkqy/common/utils/sso/DateUtils.java

@@ -0,0 +1,166 @@
+package com.zkqy.common.utils.sso;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+/**
+ * Date相关公共方法
+ */
+public class DateUtils {
+
+  private static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
+  private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
+
+
+  public static LocalDateTime now() {
+    return LocalDateTime.now();
+  }
+
+  /**
+   * 返回当前时间字符串(格式化表达式:yyyy-MM-dd HH:mm:ss)
+   */
+  public static String nowStr() {
+    return toDateTimeStr(now(), DEFAULT_DATE_TIME_FORMAT);
+  }
+
+  /**
+   * 返回当前时间字符串
+   */
+  public static String nowStr(String pattern) {
+    return toDateTimeStr(now(), pattern);
+  }
+
+  /**
+   * 返回当前精确到秒的时间戳
+   */
+  public static Long nowLong(ZoneOffset zoneOffset) {
+    LocalDateTime dateTime = now();
+
+    if (zoneOffset == null) {
+      return dateTime.toEpochSecond(ZoneOffset.ofHours(8));
+    } else {
+      return dateTime.toEpochSecond(zoneOffset);
+    }
+  }
+
+  /**
+   * 返回当前精确到毫秒的时间戳
+   */
+  public static Long currentTimeMillis() {
+    return System.currentTimeMillis();
+  }
+
+  /**
+   * 将时间戳转换为LocalDateTime
+   */
+  public static LocalDateTime ofEpochSecond(Long second, ZoneOffset zoneOffset) {
+    if (zoneOffset == null) {
+      return LocalDateTime.ofEpochSecond(second, 0, ZoneOffset.ofHours(8));
+    } else {
+      return LocalDateTime.ofEpochSecond(second, 0, zoneOffset);
+    }
+  }
+
+  /**
+   * 格式化LocalDateTime(格式化表达式:yyyy-MM-dd HH:mm:ss)
+   */
+  public static String toDateTimeStr(LocalDateTime dateTime) {
+    return toDateTimeStr(dateTime, DEFAULT_DATE_TIME_FORMAT);
+  }
+
+  /**
+   * 格式化LocalDateTime
+   */
+  public static String toDateTimeStr(LocalDateTime dateTime, String pattern) {
+    return dateTime.format(DateTimeFormatter.ofPattern(pattern, Locale.SIMPLIFIED_CHINESE));
+  }
+
+  /**
+   * 将时间字符串转化为LocalDateTime
+   */
+  public static LocalDateTime toDateTime(String dateTimeStr) {
+    return toDateTime(dateTimeStr, DEFAULT_DATE_TIME_FORMAT);
+  }
+
+  /**
+   * 将时间字符串转化为LocalDateTime
+   */
+  public static LocalDateTime toDateTime(String dateTimeStr, String pattern) {
+    return LocalDateTime.parse(dateTimeStr,
+        DateTimeFormatter.ofPattern(pattern, Locale.SIMPLIFIED_CHINESE));
+  }
+
+  /**
+   * 返回当前日期的LocalDate
+   */
+  public static LocalDate currentDate() {
+    return LocalDate.now();
+  }
+
+  /**
+   * 返回当前日期字符串(格式化表达式:yyyy-MM-dd)
+   */
+  public static String currentDateStr() {
+    return toDateStr(currentDate());
+  }
+
+  /**
+   * 格式化LocalDate
+   */
+  public static String toDateStr(LocalDate date) {
+    return toDateStr(date, DEFAULT_DATE_FORMAT);
+  }
+
+  /**
+   * 格式化LocalDate
+   */
+  public static String toDateStr(LocalDate date, String pattern) {
+    return date.format(DateTimeFormatter.ofPattern(pattern, Locale.SIMPLIFIED_CHINESE));
+  }
+
+  /**
+   * 将日期字符串转化为LocalDate
+   */
+  public static LocalDate toDate(String dateStr) {
+    return toDate(dateStr, DEFAULT_DATE_FORMAT);
+  }
+
+  /**
+   * 将日期字符串转化为LocalDate
+   */
+  public static LocalDate toDate(String dateStr, String pattern) {
+    return LocalDate.parse(dateStr,
+        DateTimeFormatter.ofPattern(pattern, Locale.SIMPLIFIED_CHINESE));
+  }
+
+  /**
+   * 返回几天之后的时间
+   */
+  public static LocalDateTime nextDays(Long days) {
+    return now().plusDays(days);
+  }
+
+  /**
+   * 返回几天之后的时间(精确到秒的时间戳)
+   */
+  public static Long nextDaysSecond(Long days, ZoneOffset zoneOffset) {
+    LocalDateTime dateTime = nextDays(days);
+
+    if (zoneOffset == null) {
+      return dateTime.toEpochSecond(ZoneOffset.ofHours(8));
+    } else {
+      return dateTime.toEpochSecond(zoneOffset);
+    }
+  }
+
+  /**
+   * 将天数转化为秒数
+   */
+  public static Long dayToSecond(Long days) {
+    return days * 86400;
+  }
+
+}

+ 188 - 0
zkqy-common/src/main/java/com/zkqy/common/utils/sso/EncryptUtils.java

@@ -0,0 +1,188 @@
+package com.zkqy.common.utils.sso;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.*;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Random;
+
+/**
+ * 加密相关公共方法
+ */
+public class EncryptUtils {
+
+    private static final String CHARS_1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+    private static final String CHARS_2 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+    /**
+     * 获取长度为num的随机数(0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz)
+     * @param num 生成的字符串长度
+     * */
+    public static String getRandomStr1(final int num) {
+        final StringBuilder stringBuilder = new StringBuilder();
+        for (int i = 1; i <= num; i++) {
+            stringBuilder.append(CHARS_1.charAt(new Random().nextInt(CHARS_1.length())));
+        }
+        return stringBuilder.toString();
+    }
+
+    /**
+     * 获取长度为num的随机数(./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz)
+     * @param num 生成的字符串长度
+     * */
+    public static String getRandomStr2(final int num) {
+        final StringBuilder stringBuilder = new StringBuilder();
+        for (int i = 1; i <= num; i++) {
+            stringBuilder.append(CHARS_2.charAt(new Random().nextInt(CHARS_2.length())));
+        }
+        return stringBuilder.toString();
+    }
+
+    /**
+     * Base64 encode
+     * */
+    public static String base64Encode(final String data){
+        return Base64.encodeBase64String(data.getBytes());
+    }
+
+    /**
+     * Base64 decode
+     * @throws UnsupportedEncodingException
+     * */
+    public static String base64Decode(final String data) throws UnsupportedEncodingException {
+        return new String(Base64.decodeBase64(data.getBytes()),"utf-8");
+    }
+
+    /**
+     * md5
+     * */
+    public static String md5Hex(final String data){
+        return DigestUtils.md5Hex(data);
+    }
+
+    /**
+     * sha1
+     * */
+    public static String sha1Hex(final String data){
+        return DigestUtils.sha1Hex(data);
+    }
+
+    /**
+     * sha256
+     * */
+    public static String sha256Hex(final String data){
+        return DigestUtils.sha256Hex(data);
+    }
+
+    /**
+     * sha512Hex
+     * */
+    public static String sha512Hex(final String data){
+        return DigestUtils.sha512Hex(data);
+    }
+
+    /**
+     * <p><b>基于哈希的消息验证代码(HMAC_MD5)</b></p>
+     * <p>在发送方和接收方共享密钥的前提下,HMAC可用于确定通过不安全信道发送的消息是否已被篡改。</p>
+     * <p>发送方计算原始数据的哈希值,并将原始数据和哈希值放在一个消息中同时传送。接收方重新计算所接收消息的哈希值,并检查计算所得的 HMAC 是否与传送的 HMAC 匹配。
+     * 因为更改消息和重新生成正确的哈希值需要密钥,所以对数据或哈希值的任何更改都会导致不匹配。因此,如果原始的哈希值与计算得出的哈希值相匹配,则消息通过身份验证。</p>
+     * @param key 秘钥
+     * @param value 待加密的数据
+     * @return java.lang.String
+     */
+    public static String hmacMd5Hex(final String key, final String value){
+        return new HmacUtils(HmacAlgorithms.HMAC_MD5, key).hmacHex(value);
+    }
+
+    /**
+     * HMAC_SHA_1
+     * */
+    public static String hmacSha1(final String key, final String value){
+        return new HmacUtils(HmacAlgorithms.HMAC_SHA_1, key).hmacHex(value);
+    }
+
+    /**
+     * HMAC_SHA_256
+     * */
+    public static String hmacSha256(final String key, final String value){
+        return new HmacUtils(HmacAlgorithms.HMAC_SHA_256, key).hmacHex(value);
+    }
+
+    /**
+     * HMAC_SHA_512
+     * */
+    public static String hmacSha512(final String key, final String value){
+        return new HmacUtils(HmacAlgorithms.HMAC_SHA_512, key).hmacHex(value);
+    }
+
+    /**
+     * Md5Crypt加密,使用示例:
+     * <p>String encrypted1 = Md5Crypt.md5Crypt(key.getBytes());</p>
+     * <p>String encrypted2 = Md5Crypt.md5Crypt(key.getBytes(),encrypted1);</p>
+     * <p>System.out.println(encrypted1.equals(encrypted2)); //true</p>
+     * @param value 原始数据
+     * @param salt 盐,如果为空则会生成一个随机的8位字符串
+     * @return java.lang.String
+     * */
+    public static String md5Crypt(final String value, final String salt){
+        return Md5Crypt.md5Crypt(value.getBytes(),salt);
+    }
+
+    /**
+     * 用于检验使用Md5Crypt.md5Crypt之后的数据是否正确(一般用于登录校验用户密码)
+     * @param value 待校验的原始数据
+     * @param encryptedStr 已有的加密之后的字符串
+     * @return boolean 待校验的原始数据和加密字符串的原始数据是否相同
+     */
+    public static boolean checkMd5Crypt(final String value,final String encryptedStr){
+        String newEncryptedStr = md5Crypt(value,encryptedStr);
+
+        return newEncryptedStr.equals(encryptedStr);
+    }
+
+    /**
+     * Sha2Crypt加密,类似于Md5Crypt
+     * @param value 原始数据
+     * @param salt 盐,如果为空则会生成一个随机的8位字符串
+     * @return java.lang.String
+     * */
+    public static String sha256Crypt(final String value, final String salt){
+        return Sha2Crypt.sha256Crypt(value.getBytes(),salt);
+    }
+
+    /**
+     * 用于检验使用Sha2Crypt.sha256Crypt之后的数据是否正确(一般用于登录校验用户密码)
+     * @param value 待校验的原始数据
+     * @param encryptedStr 已有的加密之后的字符串
+     * @return boolean 待校验的原始数据和加密字符串的原始数据是否相同
+     */
+    public static boolean checkSha256Crypt(final String value,final String encryptedStr){
+        String newEncryptedStr = sha256Crypt(value,encryptedStr);
+
+        return newEncryptedStr.equals(encryptedStr);
+    }
+
+    /**
+     * Sha2Crypt加密,类似于Md5Crypt
+     * @param value 原始数据
+     * @param salt 盐,如果为空则会生成一个随机的8位字符串
+     * @return java.lang.String
+     * */
+    public static String sha512Crypt(final String value, final String salt){
+        return Sha2Crypt.sha512Crypt(value.getBytes(),salt);
+    }
+
+    /**
+     * 用于检验使用Sha2Crypt.sha512Crypt之后的数据是否正确(一般用于登录校验用户密码)
+     * @param value 待校验的原始数据
+     * @param encryptedStr 已有的加密之后的字符串
+     * @return boolean 待校验的原始数据和加密字符串的原始数据是否相同
+     */
+    public static boolean checkSha512Crypt(final String value,final String encryptedStr){
+        String newEncryptedStr = sha512Crypt(value,encryptedStr);
+
+        return newEncryptedStr.equals(encryptedStr);
+    }
+
+
+}

+ 30 - 0
zkqy-common/src/main/java/com/zkqy/common/utils/sso/JsonUtils.java

@@ -0,0 +1,30 @@
+package com.zkqy.common.utils.sso;
+
+import com.alibaba.fastjson2.JSON;
+
+/**
+ * JSON相关公共方法(通过Fastjson实现)
+ */
+public class JsonUtils {
+
+    /**
+     * 将对象转化为json字符串
+     *
+     * @param source Java对象
+     * @return java.lang.String
+     */
+    public static <K> String toJson(K source) {
+        return JSON.toJSON(source).toString();
+    }
+
+    /**
+     * 将json字符串还原为目标对象
+     *
+     * @param source json字符串
+     * @return K
+     */
+    public static <T> T fromJson(String source, Class<T> clazz) {
+        return JSON.parseObject(source, clazz);
+    }
+
+}

+ 133 - 0
zkqy-common/src/main/java/com/zkqy/common/utils/sso/SpringContextUtils.java

@@ -0,0 +1,133 @@
+package com.zkqy.common.utils.sso;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Map;
+
+/**
+ * 自定义Spring工具类
+ */
+@Component
+public class SpringContextUtils implements ApplicationContextAware {
+	private static ConfigurableApplicationContext applicationContext;
+
+	@Override
+	public void setApplicationContext(ApplicationContext context) throws BeansException {
+		applicationContext = (ConfigurableApplicationContext) context;
+	}
+
+	/**
+	 * 获取ApplicationContext对象
+	 */
+	public static ApplicationContext getApplicationContext(){
+		return applicationContext;
+	}
+
+    /**
+     * 停止应用程序
+     */
+    public static void close(){
+        if(applicationContext != null){
+            applicationContext.close();
+        }
+    }
+
+	/**
+	 * 根据bean的名称获取bean
+	 */
+	public static Object getBeanByName(String name){
+		return applicationContext.getBean(name);
+	}
+
+	/**
+	 * 根据bean的class来查找对象
+	 */
+	public static <T> T getBeanByClass(Class<T> clazz){
+		return applicationContext.getBean(clazz);
+	}
+
+	/**
+	 * 根据bean的class来查找所有的对象(包括子类)
+	 */
+	public static <T> Map<String, T> getBeansByClass(Class<T> c){
+		return applicationContext.getBeansOfType(c);
+	}
+
+	/**
+	 * 获取HttpServletRequest
+	 */
+	public static HttpServletRequest getRequest() {
+		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
+		return attributes.getRequest();
+	}
+
+	/**
+	 * 获取HttpSession
+	 */
+	public static HttpSession getSession() {
+		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
+		return attributes.getRequest().getSession();
+	}
+
+    /**
+     * 获取完整的请求URL
+     */
+    public static String getRequestUrl(){
+        return getRequestUrl(getRequest());
+    }
+
+    /**
+     * 获取完整的请求URL
+     */
+	public static String getRequestUrl(HttpServletRequest request){
+        //当前请求路径
+        String currentUrl = request.getRequestURL().toString();
+        //请求参数
+        String queryString = request.getQueryString();
+        if(!StringUtils.isEmpty(queryString)){
+            currentUrl = currentUrl + "?" + queryString;
+        }
+
+        String result = "";
+        try {
+            result = URLEncoder.encode(currentUrl,"UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            //ignore
+        }
+
+        return result;
+    }
+
+    /**
+     * 获取请求的客户端IP
+     */
+	public static String getRequestIp(HttpServletRequest request) {
+		String ip = request.getHeader("X-Forwarded-For");
+		if(StringUtils.isNoneBlank(ip) && !"unKnown".equalsIgnoreCase(ip)){
+			//多次反向代理后会有多个ip值,第一个ip才是真实ip
+			int index = ip.indexOf(",");
+			if(index != -1){
+				return ip.substring(0,index);
+			}else{
+				return ip;
+			}
+		}
+		ip = request.getHeader("X-Real-IP");
+		if(StringUtils.isNoneBlank(ip) && !"unKnown".equalsIgnoreCase(ip)){
+			return ip;
+		}
+		return request.getRemoteAddr();
+	}
+
+}

+ 13 - 1
zkqy-framework/pom.xml

@@ -18,7 +18,7 @@
     <dependencies>
 
         <!-- SpringBoot Web容器 -->
-         <dependency>
+        <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
@@ -74,6 +74,18 @@
             <artifactId>zkqy-common</artifactId>
             <version>3.8.5</version>
         </dependency>
+
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.security.oauth</groupId>-->
+<!--            <artifactId>spring-security-oauth2</artifactId>-->
+<!--            <version>2.3.4.RELEASE</version>-->
+<!--        </dependency>-->
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.5</version>
+        </dependency>
     </dependencies>
 
 </project>

+ 4 - 1
zkqy-framework/src/main/java/com/zkqy/framework/config/SecurityConfig.java

@@ -108,10 +108,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
                 // 过滤请求
                 .authorizeRequests()
                 // 对于登录login 注册register 验证码captchaImage 允许匿名访问
-                .antMatchers("/login", "/register", "/captchaImage", "/uniappLogin", "/commonUniappLogin","/isTenantExist").permitAll()
+                .antMatchers("/login", "/register", "/captchaImage", "/uniappLogin", "/commonUniappLogin", "/isTenantExist").permitAll()
                 // 静态资源,可匿名访问
                 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                 .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
+                // 单点授权
+                .antMatchers("/oauth2/**", "/Oauth2Login/**","/oauth/callback","/authorize").permitAll()
+
                 // 除上面外的所有请求全部需要鉴权认证
                 .anyRequest().authenticated()
                 .and()

+ 83 - 0
zkqy-framework/src/main/java/com/zkqy/framework/config/sso/RestTemplateConfig.java

@@ -0,0 +1,83 @@
+package com.zkqy.framework.config.sso;
+
+import org.apache.http.Header;
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
+import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.message.BasicHeader;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Configuration
+public class RestTemplateConfig {
+
+  /**
+   * 返回RestTemplate
+   *
+   * @param factory ClientHttpRequestFactory
+   * @return RestTemplate
+   */
+  @Bean
+  public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
+    return new RestTemplate(factory);
+  }
+
+
+  /**
+   * ClientHttpRequestFactory接口的另一种实现方式(推荐使用),即:
+   * HttpComponentsClientHttpRequestFactory:底层使用Httpclient连接池的方式创建Http连接请求
+   *
+   * @return HttpComponentsClientHttpRequestFactory
+   */
+  @Bean
+  public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory() {
+    //Httpclient连接池,长连接保持30秒
+    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(
+        30, TimeUnit.SECONDS);
+
+    //设置总连接数
+    connectionManager.setMaxTotal(1000);
+    //设置同路由的并发数
+    connectionManager.setDefaultMaxPerRoute(1000);
+
+    //设置header
+    List<Header> headers = new ArrayList<Header>();
+    headers.add(new BasicHeader("User-Agent",
+        "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04"));
+    headers.add(new BasicHeader("Accept-Encoding", "gzip, deflate"));
+    headers.add(new BasicHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3"));
+    headers.add(new BasicHeader("Connection", "keep-alive"));
+
+    //创建HttpClient
+    HttpClient httpClient = HttpClientBuilder.create()
+        .setConnectionManager(connectionManager)
+        .setDefaultHeaders(headers)
+        .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) //设置重试次数
+        .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()) //设置保持长连接
+        .build();
+
+    HttpComponentsClientHttpRequestFactory requestFactory =
+        new HttpComponentsClientHttpRequestFactory(httpClient);
+
+    //设置客户端和服务端建立连接的超时时间
+    requestFactory.setConnectTimeout(5000);
+    //设置客户端从服务端读取数据的超时时间
+    requestFactory.setReadTimeout(5000);
+    //设置从连接池获取连接的超时时间,不宜过长
+    requestFactory.setConnectionRequestTimeout(200);
+    //缓冲请求数据,默认为true。通过POST或者PUT大量发送数据时,建议将此更改为false,以免耗尽内存
+    requestFactory.setBufferRequestBody(false);
+
+    return requestFactory;
+  }
+
+}

+ 64 - 0
zkqy-framework/src/main/java/com/zkqy/framework/config/sso/WebMvcConfig.java

@@ -0,0 +1,64 @@
+package com.zkqy.framework.config.sso;
+
+import com.zkqy.framework.sso_oauth2.interceptor.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * Web相关配置
+ *
+ */
+@Configuration
+public class WebMvcConfig implements WebMvcConfigurer {
+
+  @Autowired
+  private LoginInterceptor loginInterceptor;
+
+  /**
+   * 视图控制器
+   */
+  @Override
+  public void addViewControllers(ViewControllerRegistry registry) {
+    registry.addViewController("/index").setViewName("index");
+  }
+
+  /**
+   * 添加拦截器
+   */
+  @Override
+  public void addInterceptors(InterceptorRegistry registry) {
+    registry.addInterceptor(this.loginInterceptor)
+        .addPathPatterns("/user/**", "/oauth2.0/authorizePage",
+            "/sso/token").excludePathPatterns("/oauth2.0/authorize");
+    registry.addInterceptor(oauthInterceptor()).addPathPatterns("/oauth/authorize");
+    registry.addInterceptor(oauthInterceptor()).addPathPatterns("/oauth/authorize");
+    registry.addInterceptor(accessTokenInterceptor()).addPathPatterns("/api/**");
+    registry.addInterceptor(ssoAccessDomainInterceptor()).addPathPatterns("/sso/token");
+    registry.addInterceptor(ssoAccessTokenInterceptor()).addPathPatterns("/sso/verify");
+  }
+
+  @Bean
+  public OauthInterceptor oauthInterceptor() {
+    return new OauthInterceptor();
+  }
+
+  @Bean
+  public AuthAccessTokenInterceptor accessTokenInterceptor() {
+    return new AuthAccessTokenInterceptor();
+  }
+
+  @Bean
+  public SsoAccessTokenInterceptor ssoAccessTokenInterceptor() {
+    return new SsoAccessTokenInterceptor();
+  }
+
+  @Bean
+  public SsoAccessDomainInterceptor ssoAccessDomainInterceptor() {
+    return new SsoAccessDomainInterceptor();
+  }
+
+}

+ 4 - 0
zkqy-framework/src/main/java/com/zkqy/framework/security/handle/LogoutSuccessHandlerImpl.java

@@ -5,6 +5,7 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import com.zkqy.system.service.sso.AuthorizationService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.core.Authentication;
@@ -28,6 +29,8 @@ import com.zkqy.framework.web.service.TokenService;
 public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
     @Autowired
     private TokenService tokenService;
+    @Autowired
+    private AuthorizationService authorizationService;
 
     /**
      * 退出处理
@@ -42,6 +45,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
             String userName = loginUser.getUsername();
             // 删除用户缓存记录
             tokenService.delLoginUser(loginUser.getToken());
+            authorizationService.delTokenByUserId(loginUser.getUserId());
             Long tenantId = loginUser.getTenantId() == null ? 0L : loginUser.getTenantId();
             // 记录用户退出日志
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(tenantId, userName, Constants.LOGOUT, "退出成功"));

+ 325 - 0
zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/controller/OauthController.java

@@ -0,0 +1,325 @@
+package com.zkqy.framework.sso_oauth2.controller;
+
+import com.zkqy.common.constant.CacheConstants;
+import com.zkqy.common.core.domain.AjaxResult;
+import com.zkqy.common.core.domain.entity.SysUser;
+import com.zkqy.common.core.redis.RedisCache;
+import com.zkqy.common.enums.sso.ErrorCodeEnum;
+import com.zkqy.common.enums.sso.ExpireEnum;
+import com.zkqy.common.enums.sso.GrantTypeEnum;
+import com.zkqy.common.utils.StringUtils;
+import com.zkqy.common.utils.sso.DateUtils;
+import com.zkqy.framework.security.context.AuthenticationContextHolder;
+import com.zkqy.system.domain.sso.AuthAccessToken;
+import com.zkqy.system.domain.sso.AuthClientDetails;
+import com.zkqy.system.domain.sso.AuthRefreshToken;
+import com.zkqy.system.service.ISysUserService;
+import com.zkqy.system.service.sso.AuthorizationService;
+import com.zkqy.system.service.sso.RedisService;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.MediaType;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 基于oauth2.0相关的授权相关操作
+ */
+@Controller
+@RequestMapping("/oauth2")
+public class OauthController {
+
+    @Autowired
+    private RedisService redisService;
+
+    @Autowired
+    private AuthorizationService authorizationService;
+
+    @Autowired
+    private ISysUserService userService;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @Resource
+    private AuthenticationManager authenticationManager;
+
+    @Value("${OpenAuthorization2.MES.REDIRECT_URL}")
+    private String REDIRECT_URL;
+
+    /**
+     * 获取Authorization Code
+     */
+    @PostMapping("/authorize")
+    @ResponseBody
+    public Map<String, Object> authorize(HttpServletRequest request) {
+        String tenantID = request.getParameter("tenantID");
+        String username = request.getParameter("username");
+        String password = request.getParameter("password");
+        String code = request.getParameter("code");
+        String uuid = request.getParameter("uuid");
+        String verifyKey =
+                CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
+        String captcha = redisCache.getCacheObject(verifyKey);
+        if (captcha == null || !captcha.equals(code))
+//        if (false)
+        {
+            return AjaxResult.warn("验证码错误");
+        } else {
+            redisCache.deleteObject(verifyKey);
+        }
+        String endUsername;
+        // 单点登录
+        if (tenantID != null && !tenantID.isEmpty()) {
+            endUsername = tenantID + "¥¥¥" + username;
+        } else {
+            endUsername = username;
+        }
+        SysUser sysUser = userService.queryUserInfo(endUsername, password);
+        // 用户验证
+        Authentication authentication = null;
+        try {
+            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(endUsername, password);
+            AuthenticationContextHolder.setContext(authenticationToken);
+            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
+            authentication = authenticationManager.authenticate(authenticationToken);
+        } catch (AuthenticationException e) {
+            return AjaxResult.warn("用户名或密码错误");
+            // return "用户名或密码错误";
+        }
+        String clientIdStr = request.getParameter("client_id");
+        String scopeStr = request.getParameter("scope");
+        String redirectUri = request.getParameter("redirect_uri");
+        String status = request.getParameter("status");
+        String authorizationCode = authorizationService.createAuthorizationCode(clientIdStr, scopeStr, sysUser);
+        String params =
+                redirectUri + "?code=" + authorizationCode;
+        if (StringUtils.isNoneBlank(status)) {
+            params = params + "&status=" + status;
+        }
+        // request.getSession().setAttribute(Constants.SESSION_USER, sysUser);
+        // 存放redis用户信息数据
+        redisCache.setCacheObject(authorizationCode, sysUser);
+        return AjaxResult.success(authorizationCode, params);
+    }
+
+
+    /**
+     * 获取Authorization Code
+     */
+    @GetMapping("/authorize")
+    public String getAuthorize(HttpServletRequest request) {
+        String key = request.getParameter("key");
+        String tenantCode = request.getParameter("tenantCode");
+        if (key != null && !key.isEmpty()) {
+            SysUser sysUser = redisService.get(key);
+            String clientIdStr = request.getParameter("client_id");
+            String scopeStr = request.getParameter("scope");
+            String redirectUri = request.getParameter("redirect_uri");
+            String status = request.getParameter("status");
+            String authorizationCode = authorizationService.createAuthorizationCode(clientIdStr, scopeStr, sysUser);
+            String params =
+                    redirectUri + "?code=" + authorizationCode;
+            if (StringUtils.isNoneBlank(status)) {
+                params = params + "&status=" + status;
+            }
+            // redisService.delete(key);
+            return "redirect:" + params;
+        } else {
+            return "redirect:" + REDIRECT_URL + "/login?tenantCode=" + tenantCode;
+        }
+    }
+
+
+    /**
+     * 通过Authorization Code获取Access Token.
+     */
+    @PostMapping(value = "/access-token", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    @ResponseBody
+    public Map<String, Object> token(HttpServletRequest request) {
+        Map<String, Object> result = new HashMap<>(8);
+        String grantType = request.getParameter("grant_type");
+        String code = request.getParameter("code");
+        String clientIdStr = request.getParameter("client_id");
+        String clientSecret = request.getParameter("client_secret");
+        String redirectUri = request.getParameter("redirect_uri");
+        System.out.println(grantType);
+        System.out.println(GrantTypeEnum.AUTHORIZATION_CODE.getType());
+        //校验授权方式
+        if (!GrantTypeEnum.AUTHORIZATION_CODE.getType().equals(grantType)) {
+            this.generateErrorResponse(result, ErrorCodeEnum.UNSUPPORTED_GRANT_TYPE);
+            return result;
+        }
+        try {
+            AuthClientDetails savedClientDetails = authorizationService.selectClientDetailsByClientId(
+                    clientIdStr);
+            if (!(savedClientDetails != null && savedClientDetails.getClientSecret()
+                    .equals(clientSecret))) {
+                this.generateErrorResponse(result, ErrorCodeEnum.INVALID_CLIENT);
+                return result;
+            }
+            //校验回调URL
+            if (!savedClientDetails.getRedirectUri().equals(redirectUri)) {
+                this.generateErrorResponse(result, ErrorCodeEnum.REDIRECT_URI_MISMATCH);
+                return result;
+            }
+
+            //从Redis获取允许访问的用户权限范围
+            String scope = redisService.get(code + ":scope");
+            //从Redis获取对应的用户信息
+            SysUser sysUser = redisService.get(code + ":user");
+
+            //如果能够通过Authorization Code获取到对应的用户信息,则说明该Authorization Code有效
+            if (StringUtils.isNoneBlank(scope) && sysUser != null) {
+                // 过期时间
+                Long expiresIn = DateUtils.dayToSecond(ExpireEnum.ACCESS_TOKEN.getTime());
+                // 生成Access Token
+                String accessTokenStr = authorizationService.createAccessToken(sysUser, savedClientDetails,
+                        grantType, scope, expiresIn);
+                // 查询已经插入到数据库的Access Token
+                AuthAccessToken authAccessToken = authorizationService.selectByAccessToken(accessTokenStr);
+                // 生成Refresh Token
+                String refreshTokenStr = authorizationService.createRefreshToken(sysUser, authAccessToken);
+                //返回数据
+                result.put("access_token", authAccessToken.getAccessToken());
+                result.put("refresh_token", refreshTokenStr);
+                result.put("expires_in", expiresIn);
+                result.put("scope", scope);
+            } else {
+                this.generateErrorResponse(result, ErrorCodeEnum.INVALID_GRANT);
+            }
+            return result;
+        } catch (Exception e) {
+            this.generateErrorResponse(result, ErrorCodeEnum.UNKNOWN_ERROR);
+            return result;
+        }
+    }
+
+    /**
+     * 通过Refresh Token刷新Access Token.
+     */
+    @RequestMapping(value = "/refresh-token", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    @ResponseBody
+    public Map<String, Object> refreshToken(HttpServletRequest request) {
+        Map<String, Object> result = new HashMap<>();
+        //获取Refresh Token
+        String refreshTokenStr = request.getParameter("refresh_token");
+        try {
+            AuthRefreshToken authRefreshToken = authorizationService.selectByRefreshToken(
+                    refreshTokenStr);
+            if (authRefreshToken != null) {
+                Long savedExpiresAt = authRefreshToken.getExpiresIn();
+                //过期日期
+                LocalDateTime expiresDateTime = DateUtils.ofEpochSecond(savedExpiresAt, null);
+                //当前日期
+                LocalDateTime nowDateTime = DateUtils.now();
+                //如果Refresh Token已经失效,则需要重新生成
+                if (expiresDateTime.isBefore(nowDateTime)) {
+                    this.generateErrorResponse(result, ErrorCodeEnum.EXPIRED_TOKEN);
+                } else {
+                    //获取存储的Access Token
+                    AuthAccessToken authAccessToken = authorizationService.selectByAccessId(
+                            authRefreshToken.getTokenId());
+                    //获取对应的客户端信息
+                    AuthClientDetails savedClientDetails = authorizationService.selectClientDetailsById(
+                            authAccessToken.getClientId());
+                    //获取对应的用户信息
+                    SysUser sysUser = userService.selectUserById(Long.valueOf(authAccessToken.getUserId().toString()));
+
+                    //新的过期时间
+                    Long expiresIn = DateUtils.dayToSecond(ExpireEnum.ACCESS_TOKEN.getTime());
+                    //生成新的Access Token
+                    String newAccessTokenStr = authorizationService.createAccessToken(sysUser, savedClientDetails
+                            , authAccessToken.getGrantType(), authAccessToken.getScope(), expiresIn);
+                    //返回数据
+                    result.put("access_token", newAccessTokenStr);
+                    result.put("refresh_token", refreshTokenStr);
+                    result.put("expires_in", expiresIn);
+                    result.put("scope", authAccessToken.getScope());
+                }
+            } else {
+                this.generateErrorResponse(result, ErrorCodeEnum.INVALID_GRANT);
+            }
+            return result;
+        } catch (Exception e) {
+            this.generateErrorResponse(result, ErrorCodeEnum.UNKNOWN_ERROR);
+            return result;
+        }
+    }
+
+
+    /**
+     * 校验Access Token
+     */
+    @RequestMapping(value = "/verify", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    @ResponseBody
+    public Map<String, Object> verify(HttpServletRequest request) {
+        Map<String, Object> result = new HashMap<>(8);
+
+        //获取Access Token
+        String accessToken = request.getParameter("access_token");
+        //获取Access Token
+        String refreshToken = request.getParameter("refresh_token");
+        try {
+            //过期时间
+            Long expiresIn = DateUtils.dayToSecond(ExpireEnum.ACCESS_TOKEN.getTime());
+            //查询Access Token
+            AuthAccessToken authAccessToken = authorizationService.selectByAccessToken(accessToken);
+            //查询Refresh Token
+            AuthRefreshToken ssoRefreshToken = authorizationService.selectByRefreshToken(refreshToken);
+            //组装返回信息
+            result.put("access_token", authAccessToken.getAccessToken());
+            result.put("refresh_token", ssoRefreshToken.getRefreshToken());
+            result.put("expires_in", expiresIn);
+            return result;
+        } catch (Exception e) {
+            this.generateErrorResponse(result, ErrorCodeEnum.UNKNOWN_ERROR);
+            return result;
+        }
+    }
+
+
+    /**
+     * 校验Access Token,并返回用户信息
+     */
+    @GetMapping(value = "/user-info", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    @ResponseBody
+    public Map<String, Object> getUserInfo(HttpServletRequest request) {
+        Map<String, Object> result = new HashMap<>(8);
+        //获取Access Token
+        String accessToken = request.getParameter("access_token");
+        try {
+            AuthAccessToken authAccessToken = authorizationService.selectByAccessToken(accessToken);
+            //查询用户信息
+            SysUser sysUser = userService.selectUserById(authAccessToken.getUserId());
+            //组装返回信息
+            result.put("access_token", authAccessToken.getAccessToken());
+            result.put("user_info", sysUser);
+            return result;
+        } catch (Exception e) {
+            this.generateErrorResponse(result, ErrorCodeEnum.UNKNOWN_ERROR);
+            return result;
+        }
+    }
+
+
+    /**
+     * 组装错误请求的返回
+     */
+    private void generateErrorResponse(Map<String, Object> result, ErrorCodeEnum errorCodeEnum) {
+        result.put("error", errorCodeEnum.getError());
+        result.put("error_description", errorCodeEnum.getErrorDescription());
+    }
+
+}

+ 110 - 0
zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/controller/ThirdPartLoginController.java

@@ -0,0 +1,110 @@
+package com.zkqy.framework.sso_oauth2.controller;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.zkqy.common.core.domain.AjaxResult;
+import com.zkqy.common.core.domain.entity.SysUser;
+import com.zkqy.common.utils.http.HttpUtilsOauth2;
+import org.apache.http.HttpResponse;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * 第三方登录验证
+ *
+ * @author hzh
+ */
+@Controller
+public class ThirdPartLoginController {
+
+    @Value("${OpenAuthorization2.MES.URL}")
+    private String URL;
+
+    @Value("${OpenAuthorization2.MES.BASIC}")
+    private String BASIC;
+
+    @Value("${OpenAuthorization2.MES.REDIRECT_URL}")
+    private String REDIRECT_URL;
+
+    @Value("${OpenAuthorization2.MES.CALLBACK}")
+    private String CALLBACK;
+
+    @GetMapping("oauth/callback")
+    public String callback(@RequestParam("code") String code, HttpSession session, HttpServletRequest request) {
+        try {
+            // 封装获取token请求参数
+            Map<String, String> map = new HashMap<>();
+            map.put("grant_type", "authorization_code");
+            map.put("redirect_uri", CALLBACK);
+            map.put("code", code);
+            map.put("client_id", "mestools");
+            map.put("client_secret", "zkqy8888");
+            // 获取token请求头
+            Map<String, String> headers = new HashMap<>();
+            // 转换编码凭证
+            byte[] encodedBytes = Base64.getEncoder().encode(BASIC.getBytes(StandardCharsets.UTF_8));
+            String encodedCredentials = new String(encodedBytes, StandardCharsets.UTF_8);
+            headers.put("Authorization", "Basic " + encodedCredentials);
+            headers.put("Content-Type", "application/json;charset=UTF-8");
+            HttpResponse response = HttpUtilsOauth2.doPost(URL, "/access-token", "post", headers, map, new HashMap<>());
+            //处理
+            if (response.getStatusLine().getStatusCode() == 200) {
+                // 得到code换取token结果
+                String tokenJSON = EntityUtils.toString(response.getEntity());
+                System.out.println("获取token" + tokenJSON);
+                Map<String, Object> tokenMap = JSONObject.parse(tokenJSON);
+                // 封装请求用户信息请求头
+                Map<String, String> getUserHeaders = new HashMap<>();
+                getUserHeaders.put("Authorization", "Bearer " + tokenMap.get("access_token"));
+                getUserHeaders.put("Content-Type", "application/json");
+
+                Map<String, String> token = new HashMap<>();
+                token.put("access_token", tokenMap.get("access_token").toString());
+                HttpResponse userResponse = HttpUtilsOauth2.doGet(URL, "/user-info", "get", getUserHeaders, token);
+                if (userResponse.getStatusLine().getStatusCode() == 200) {
+                    // 得到当前登录用户信息
+                    String userJson = EntityUtils.toString(userResponse.getEntity());
+                    Map<String, Object> retUserMap = JSONObject.parse(userJson);
+                    System.out.println("用户json:" + userJson);
+                    JSONObject jsonObject = JSONObject.parseObject(retUserMap.get("user_info").toString());
+                    SysUser sysUser = JSON.toJavaObject(jsonObject, SysUser.class);
+                    if (null != sysUser) {
+                        System.out.println("用户名:" + (sysUser.getTenant().getTenantCode() + "^_^" + sysUser.getUserName() + "^_^" + System.currentTimeMillis() / 1000));
+                        byte[] userInfo = Base64.getEncoder().encode((sysUser.getTenant().getTenantCode() + "^_^" + sysUser.getUserName() + "^_^" + System.currentTimeMillis() / 1000).getBytes(StandardCharsets.UTF_8));
+                        String retUserInfo = new String(userInfo, StandardCharsets.UTF_8);
+                        System.out.println("正常跳转");
+                        System.err.println("redirect:" + REDIRECT_URL + "/redirectAuth?bWVz=" + retUserInfo);
+                        return "redirect:" + REDIRECT_URL + "/redirectAuth?bWVz=" + retUserInfo;
+                    }
+                }
+            }
+        } catch (Exception e) {
+            System.out.println(e);
+            System.out.println("最外层跳转1");
+            // 异常处理
+            return "redirect:" + REDIRECT_URL + "/401";
+        }
+        System.out.println("最外层跳转2");
+        // 授权失败处理
+        return "redirect:" + REDIRECT_URL + "/401";
+    }
+
+    @ResponseBody
+    @GetMapping("/authorize")
+    public AjaxResult authorize() {
+        return AjaxResult.success("认证中心", URL + "/oauth2/authorize?" + "response_type=code" + "&client_id=toolmes" + "&scope=openid" + "&redirect_uri=" + CALLBACK);
+    }
+}
+

+ 71 - 0
zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/interceptor/AuthAccessTokenInterceptor.java

@@ -0,0 +1,71 @@
+package com.zkqy.framework.sso_oauth2.interceptor;
+
+import com.zkqy.common.enums.sso.ErrorCodeEnum;
+import com.zkqy.common.utils.sso.DateUtils;
+import com.zkqy.common.utils.sso.JsonUtils;
+import com.zkqy.system.domain.sso.AuthAccessToken;
+import com.zkqy.system.service.sso.AuthorizationService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 用于校验Access Token是否为空以及Access Token是否已经失效.
+ */
+public class AuthAccessTokenInterceptor extends HandlerInterceptorAdapter {
+    @Autowired
+    private AuthorizationService authorizationService;
+
+    /**
+     * 检查Access Token是否已经失效
+     */
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+            throws Exception {
+        String accessToken = request.getParameter("access_token");
+
+        if (StringUtils.isNoneBlank(accessToken)) {
+            //查询数据库中的Access Token
+            AuthAccessToken authAccessToken = authorizationService.selectByAccessToken(accessToken);
+
+            if (authAccessToken != null) {
+                Long savedExpiresAt = authAccessToken.getExpiresIn();
+                //过期日期
+                LocalDateTime expiresDateTime = DateUtils.ofEpochSecond(savedExpiresAt, null);
+                //当前日期
+                LocalDateTime nowDateTime = DateUtils.now();
+
+                //如果Access Token已经失效,则返回错误提示
+                return expiresDateTime.isAfter(nowDateTime) || this.generateErrorResponse(response,
+                        ErrorCodeEnum.EXPIRED_TOKEN);
+            } else {
+                return this.generateErrorResponse(response, ErrorCodeEnum.INVALID_GRANT);
+            }
+        } else {
+            return this.generateErrorResponse(response, ErrorCodeEnum.INVALID_REQUEST);
+        }
+    }
+
+    /**
+     * 组装错误请求的返回
+     */
+    private boolean generateErrorResponse(HttpServletResponse response, ErrorCodeEnum errorCodeEnum)
+            throws Exception {
+        response.setCharacterEncoding("UTF-8");
+        response.setHeader("Content-type", "application/json;charset=UTF-8");
+        Map<String, String> result = new HashMap<>(2);
+        result.put("error", errorCodeEnum.getError());
+        result.put("error_description", errorCodeEnum.getErrorDescription());
+
+        response.getWriter().write(JsonUtils.toJson(result));
+        return false;
+    }
+
+}

+ 39 - 0
zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/interceptor/LoginInterceptor.java

@@ -0,0 +1,39 @@
+package com.zkqy.framework.sso_oauth2.interceptor;
+
+
+import com.zkqy.common.core.domain.entity.SysUser;
+import com.zkqy.common.utils.sso.Constants;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+/**
+ * 定义一些页面需要做登录检查
+ */
+@Component
+public class LoginInterceptor extends HandlerInterceptorAdapter {
+
+  /**
+   * 检查是否已经登录
+   */
+  @Override
+  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+      throws Exception {
+    HttpSession session = request.getSession();
+
+    //获取session中存储的token
+    SysUser user = (SysUser) session.getAttribute(Constants.SESSION_USER);
+    if (user != null) {
+      return true;
+    } else {
+      //如果token不存在,则跳转到登录页面
+//      response.sendRedirect(
+//          request.getContextPath() + "/login?redirectUri=" + SpringContextUtils.getRequestUrl(
+//              request));
+      return false;
+    }
+  }
+}

+ 104 - 0
zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/interceptor/OauthInterceptor.java

@@ -0,0 +1,104 @@
+package com.zkqy.framework.sso_oauth2.interceptor;
+
+import com.zkqy.common.core.domain.entity.SysUser;
+import com.zkqy.common.enums.sso.ErrorCodeEnum;
+import com.zkqy.common.utils.sso.Constants;
+import com.zkqy.common.utils.sso.JsonUtils;
+import com.zkqy.common.utils.sso.SpringContextUtils;
+import com.zkqy.system.domain.sso.AuthClientDetails;
+import com.zkqy.system.domain.sso.AuthScope;
+import com.zkqy.system.mapper.sso.AuthClientDetailsMapper;
+import com.zkqy.system.mapper.sso.AuthClientUserMapper;
+import com.zkqy.system.mapper.sso.AuthScopeMapper;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 检查是否已经存在授权
+ */
+public class OauthInterceptor extends HandlerInterceptorAdapter {
+
+    @Autowired
+    private AuthClientUserMapper authClientUserMapper;
+    @Autowired
+    private AuthClientDetailsMapper authClientDetailsMapper;
+    @Autowired
+    private AuthScopeMapper authScopeMapper;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+            throws Exception {
+        HttpSession session = request.getSession();
+        //参数信息
+        String params = "?redirectUri=" + SpringContextUtils.getRequestUrl(request);
+
+        //客户端ID
+        String clientIdStr = request.getParameter("client_id");
+        //权限范围
+        String scopeStr = request.getParameter("scope");
+        //回调URL
+        String redirectUri = request.getParameter("redirect_uri");
+        //返回形式
+        String responseType = request.getParameter("response_type");
+
+        //获取session中存储的token
+        SysUser sysUser = (SysUser) session.getAttribute(Constants.SESSION_USER);
+
+        if (StringUtils.isNoneBlank(clientIdStr) && StringUtils.isNoneBlank(scopeStr)
+                && StringUtils.isNoneBlank(redirectUri) && "code".equals(responseType)) {
+            params = params + "&client_id=" + clientIdStr + "&scope=" + scopeStr;
+
+            //1. 查询是否存在授权信息
+            AuthClientDetails clientDetails = authClientDetailsMapper.selectByClientId(clientIdStr);
+            AuthScope scope = authScopeMapper.selectByScopeName(scopeStr);
+
+            if (clientDetails == null) {
+                return this.generateErrorResponse(response, ErrorCodeEnum.INVALID_CLIENT);
+            }
+
+            if (scope == null) {
+                return this.generateErrorResponse(response, ErrorCodeEnum.INVALID_SCOPE);
+            }
+
+            if (!clientDetails.getRedirectUri().equals(redirectUri)) {
+                return this.generateErrorResponse(response, ErrorCodeEnum.REDIRECT_URI_MISMATCH);
+            }
+
+//      //2. 查询用户给接入的客户端是否已经授权
+//      AuthClientUser clientUser = authClientUserMapper.selectByClientId(clientDetails.getId(),
+//          user.getId(), scope.getId());
+//      if (clientUser != null) {
+            return true;
+//      } else {
+//        //如果没有授权,则跳转到授权页面
+//        response.sendRedirect(request.getContextPath() + "/oauth2.0/authorizePage" + params);
+//        return false;
+//      }
+        } else {
+            return this.generateErrorResponse(response, ErrorCodeEnum.INVALID_REQUEST);
+        }
+    }
+
+    /**
+     * 组装错误请求的返回
+     */
+    private boolean generateErrorResponse(HttpServletResponse response, ErrorCodeEnum errorCodeEnum)
+            throws Exception {
+        response.setCharacterEncoding("UTF-8");
+        response.setHeader("Content-type", "application/json;charset=UTF-8");
+        Map<String, String> result = new HashMap<>(2);
+        result.put("error", errorCodeEnum.getError());
+        result.put("error_description", errorCodeEnum.getErrorDescription());
+
+        response.getWriter().write(JsonUtils.toJson(result));
+        return false;
+    }
+
+}

+ 56 - 0
zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/interceptor/SsoAccessDomainInterceptor.java

@@ -0,0 +1,56 @@
+package com.zkqy.framework.sso_oauth2.interceptor;
+
+import com.zkqy.common.enums.sso.ErrorCodeEnum;
+import com.zkqy.common.utils.sso.JsonUtils;
+import com.zkqy.system.domain.sso.SsoClientDetails;
+import com.zkqy.system.mapper.sso.SsoClientDetailsMapper;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 用于校验请求获取Token的回调URL是否在白名单中
+ */
+public class SsoAccessDomainInterceptor extends HandlerInterceptorAdapter{
+    @Autowired
+    private SsoClientDetailsMapper ssoClientDetailsMapper;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        String redirectUri = request.getParameter("redirect_uri");
+
+        if(StringUtils.isNoneBlank(redirectUri)){
+            //查询数据库中的回调地址的白名单
+            SsoClientDetails ssoClientDetails = ssoClientDetailsMapper.selectByRedirectUrl(redirectUri);
+
+            if(ssoClientDetails != null){
+                return true;
+            }else{
+                //如果回调URL不在白名单中,则返回错误提示
+                return this.generateErrorResponse(response, ErrorCodeEnum.INVALID_REDIRECT_URI);
+            }
+        }else{
+            return this.generateErrorResponse(response, ErrorCodeEnum.INVALID_REQUEST);
+        }
+    }
+
+    /**
+     * 组装错误请求的返回
+     */
+    private boolean generateErrorResponse(HttpServletResponse response,ErrorCodeEnum errorCodeEnum) throws Exception {
+        response.setCharacterEncoding("UTF-8");
+        response.setHeader("Content-type", "application/json;charset=UTF-8");
+        Map<String,String> result = new HashMap<>(2);
+        result.put("error", errorCodeEnum.getError());
+        result.put("error_description",errorCodeEnum.getErrorDescription());
+
+        response.getWriter().write(JsonUtils.toJson(result));
+        return false;
+    }
+
+}

+ 66 - 0
zkqy-framework/src/main/java/com/zkqy/framework/sso_oauth2/interceptor/SsoAccessTokenInterceptor.java

@@ -0,0 +1,66 @@
+package com.zkqy.framework.sso_oauth2.interceptor;
+
+import com.zkqy.common.enums.sso.ErrorCodeEnum;
+import com.zkqy.common.utils.sso.DateUtils;
+import com.zkqy.common.utils.sso.JsonUtils;
+import com.zkqy.system.domain.sso.SsoAccessToken;
+import com.zkqy.system.service.sso.SsoService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 用于校验Access Token是否为空以及Access Token是否已经失效
+ */
+public class SsoAccessTokenInterceptor extends HandlerInterceptorAdapter{
+
+    private SsoService ssoService;
+
+    /**
+     * 检查Access Token是否已经失效
+     */
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        String accessToken = request.getParameter("access_token");
+
+        if(StringUtils.isNoneBlank(accessToken)){
+            //查询数据库中的Access Token
+            SsoAccessToken ssoAccessToken = ssoService.selectByAccessToken(accessToken);
+
+            if(ssoAccessToken != null){
+                Long savedExpiresAt = ssoAccessToken.getExpiresIn();
+                //过期日期
+                LocalDateTime expiresDateTime = DateUtils.ofEpochSecond(savedExpiresAt, null);
+                //当前日期
+                LocalDateTime nowDateTime = DateUtils.now();
+
+                //如果Access Token已经失效,则返回错误提示
+                return expiresDateTime.isAfter(nowDateTime) || this.generateErrorResponse(response, ErrorCodeEnum.EXPIRED_TOKEN);
+            }else{
+                return this.generateErrorResponse(response, ErrorCodeEnum.INVALID_GRANT);
+            }
+        }else{
+            return this.generateErrorResponse(response, ErrorCodeEnum.INVALID_REQUEST);
+        }
+    }
+
+    /**
+     * 组装错误请求的返回
+     */
+    private boolean generateErrorResponse(HttpServletResponse response,ErrorCodeEnum errorCodeEnum) throws Exception {
+        response.setCharacterEncoding("UTF-8");
+        response.setHeader("Content-type", "application/json;charset=UTF-8");
+        Map<String,String> result = new HashMap<>(2);
+        result.put("error", errorCodeEnum.getError());
+        result.put("error_description",errorCodeEnum.getErrorDescription());
+
+        response.getWriter().write(JsonUtils.toJson(result));
+        return false;
+    }
+
+}

+ 42 - 0
zkqy-framework/src/main/java/com/zkqy/framework/web/service/SysLoginService.java

@@ -5,15 +5,19 @@ import javax.annotation.Resource;
 import cn.hutool.core.util.CharsetUtil;
 import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
 import cn.hutool.crypto.symmetric.SymmetricCrypto;
+import com.zkqy.common.core.domain.entity.DataSource;
 import com.zkqy.common.core.domain.entity.SysTenant;
 import com.zkqy.common.core.domain.model.LoginBody;
 import com.zkqy.framework.tenantLogin.CustomUserAuthenticationToken;
+import com.zkqy.system.service.IDataSourceService;
 import com.zkqy.system.service.impl.SysTenantServiceImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.stereotype.Component;
 import com.zkqy.common.constant.CacheConstants;
 import com.zkqy.common.constant.Constants;
@@ -39,6 +43,7 @@ import com.zkqy.system.service.ISysUserService;
 
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
+import java.util.Collections;
 
 /**
  * 登录校验方法
@@ -65,6 +70,13 @@ public class SysLoginService {
     @Autowired
     private SysTenantServiceImpl sysTenantService;
 
+
+    @Autowired
+    private IDataSourceService iDataSourceService;
+
+    @Autowired
+    private SysPermissionService permissionService;
+
     /**
      * 首先验证当前登录的账号是否有效
      */
@@ -128,6 +140,36 @@ public class SysLoginService {
         return tokenService.createToken(loginUser);
     }
 
+    /**
+     * 登录验证
+     *
+     * @param tenantCode code
+     * @param userName 用户名
+     * @return 结果
+     */
+    public String loginOauth(String tenantCode, String userName) {
+
+        // 获取当前租户信息
+        SysTenant sysTenant = sysTenantService.selectSysTenantByTenantCode(tenantCode);
+        // 获取当前租户数据源信息
+        DataSource dataSource = iDataSourceService.selectById(sysTenant.getDatasourceId());
+        sysTenant.setDataSource(dataSource);
+        // 根据租户编号、用户名查询详细信息
+        SysUser sysUser = userService.selectUserInfoByTenantCode(tenantCode, userName);
+        sysUser.setTenant(sysTenant);
+        // 生成token对象
+        LoginUser loginUser = this.createLoginUser(sysUser);
+                // 创建一个Authentication对象
+        Authentication authentication = new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList());
+        // 将Authentication对象设置到SecurityContext中
+        SecurityContextHolder.getContext().setAuthentication(authentication);
+        // 生成token
+        return tokenService.createToken(loginUser);
+    }
+
+    public LoginUser createLoginUser(SysUser user) {
+        return new LoginUser(user.getUserId(), user.getDeptId(), user.getTenantId(), user, permissionService.getMenuPermission(user));
+    }
 
     /**
      * 租户登录验证

+ 148 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/AuthAccessToken.java

@@ -0,0 +1,148 @@
+package com.zkqy.system.domain.sso;
+
+import java.util.Date;
+
+public class AuthAccessToken {
+
+    private Integer id;
+
+    /**
+     * Access Token
+     */
+    private String accessToken;
+
+    /**
+     * 关联的用户ID
+     */
+    private Long userId;
+    /**
+     * 关联的用户名
+     */
+    private String userName;
+
+    /**
+     * 接入的客户端ID
+     */
+    private Integer clientId;
+
+    /**
+     * 过期时间戳
+     */
+    private Long expiresIn;
+
+    /**
+     * 授权类型,比如:authorization_code
+     */
+    private String grantType;
+    /**
+     * 可被访问的用户的权限范围,比如:basic、super
+     */
+    private String scope;
+
+    private Long createUser;
+
+    private Date createTime;
+
+    private Long updateUser;
+
+    private Date updateTime;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken == null ? null : accessToken.trim();
+    }
+
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName == null ? null : userName.trim();
+    }
+
+    public Integer getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(Integer clientId) {
+        this.clientId = clientId;
+    }
+
+    public Long getExpiresIn() {
+        return expiresIn;
+    }
+
+    public void setExpiresIn(Long expiresIn) {
+        this.expiresIn = expiresIn;
+    }
+
+    public String getGrantType() {
+        return grantType;
+    }
+
+    public void setGrantType(String grantType) {
+        this.grantType = grantType == null ? null : grantType.trim();
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public void setScope(String scope) {
+        this.scope = scope == null ? null : scope.trim();
+    }
+
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public Long getCreateUser() {
+        return createUser;
+    }
+
+    public void setCreateUser(Long createUser) {
+        this.createUser = createUser;
+    }
+
+    public Long getUpdateUser() {
+        return updateUser;
+    }
+
+    public void setUpdateUser(Long updateUser) {
+        this.updateUser = updateUser;
+    }
+}

+ 49 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/AuthClientDetails.java

@@ -0,0 +1,49 @@
+package com.zkqy.system.domain.sso;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Builder
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class AuthClientDetails {
+    private Integer id;
+
+    /**
+     * 接入的客户端ID
+     */
+    private String clientId;
+
+    /**
+     * 接入的客户端的名称
+     */
+    private String clientName;
+
+    /**
+     * 接入的客户端的密钥
+     */
+    private String clientSecret;
+
+    /**
+     * 回调地址
+     */
+    private String redirectUri;
+
+    private String description;
+
+    private Integer createUser;
+
+    private Date createTime;
+
+    private Integer updateUser;
+
+    private Date updateTime;
+
+    private Integer status;
+
+}

+ 43 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/AuthClientUser.java

@@ -0,0 +1,43 @@
+package com.zkqy.system.domain.sso;
+
+public class AuthClientUser {
+    private Integer id;
+
+    private Integer authClientId;
+
+    private Integer userId;
+
+    private Integer authScopeId;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getAuthClientId() {
+        return authClientId;
+    }
+
+    public void setAuthClientId(Integer authClientId) {
+        this.authClientId = authClientId;
+    }
+
+    public Integer getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
+
+    public Integer getAuthScopeId() {
+        return authScopeId;
+    }
+
+    public void setAuthScopeId(Integer authScopeId) {
+        this.authScopeId = authScopeId;
+    }
+}

+ 96 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/AuthRefreshToken.java

@@ -0,0 +1,96 @@
+package com.zkqy.system.domain.sso;
+
+import java.util.Date;
+
+public class AuthRefreshToken {
+    private Integer id;
+
+    /**
+     * 关联的表auth_access_token对应的Access Token记录
+     */
+    private Integer tokenId;
+
+    /**
+     * Refresh Token
+     */
+    private String refreshToken;
+
+    /**
+     * 过期时间戳
+     */
+    private Long expiresIn;
+
+    private Long createUser;
+
+    private Date createTime;
+
+    private Long updateUser;
+
+    private Date updateTime;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getTokenId() {
+        return tokenId;
+    }
+
+    public void setTokenId(Integer tokenId) {
+        this.tokenId = tokenId;
+    }
+
+    public String getRefreshToken() {
+        return refreshToken;
+    }
+
+    public void setRefreshToken(String refreshToken) {
+        this.refreshToken = refreshToken == null ? null : refreshToken.trim();
+    }
+
+    public Long getExpiresIn() {
+        return expiresIn;
+    }
+
+    public void setExpiresIn(Long expiresIn) {
+        this.expiresIn = expiresIn;
+    }
+
+
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Long getCreateUser() {
+        return createUser;
+    }
+
+    public void setCreateUser(Long createUser) {
+        this.createUser = createUser;
+    }
+
+    public Long getUpdateUser() {
+        return updateUser;
+    }
+
+    public void setUpdateUser(Long updateUser) {
+        this.updateUser = updateUser;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+}

+ 35 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/AuthScope.java

@@ -0,0 +1,35 @@
+package com.zkqy.system.domain.sso;
+
+public class AuthScope {
+    private Integer id;
+    /**
+     * 可被访问的用户的权限范围,比如:basic、super
+     */
+    private String scopeName;
+
+    private String description;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getScopeName() {
+        return scopeName;
+    }
+
+    public void setScopeName(String scopeName) {
+        this.scopeName = scopeName == null ? null : scopeName.trim();
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description == null ? null : description.trim();
+    }
+}

+ 84 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/Func.java

@@ -0,0 +1,84 @@
+package com.zkqy.system.domain.sso;
+
+import java.util.Objects;
+
+public class Func {
+    private Integer id;
+
+    private String name;
+
+    private String description;
+
+    private String code;
+
+    private String url;
+
+    private Integer status;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name == null ? null : name.trim();
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description == null ? null : description.trim();
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code == null ? null : code.trim();
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url == null ? null : url.trim();
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Func func = (Func) o;
+        return Objects.equals(id, func.id) &&
+                Objects.equals(name, func.name) &&
+                Objects.equals(description, func.description) &&
+                Objects.equals(code, func.code) &&
+                Objects.equals(url, func.url) &&
+                Objects.equals(status, func.status);
+    }
+
+    @Override
+    public int hashCode() {
+
+        return Objects.hash(id, name, description, code, url, status);
+    }
+}

+ 59 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/Role.java

@@ -0,0 +1,59 @@
+package com.zkqy.system.domain.sso;
+
+import java.util.Objects;
+
+public class Role {
+    private Integer id;
+
+    private String roleName;
+
+    private String description;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getRoleName() {
+        return roleName;
+    }
+
+    public void setRoleName(String roleName) {
+        this.roleName = roleName == null ? null : roleName.trim();
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description == null ? null : description.trim();
+    }
+
+    @Override
+    public String toString() {
+        return "Role{" +
+                "id=" + id +
+                ", roleName='" + roleName + '\'' +
+                ", description='" + description + '\'' +
+                '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Role role = (Role) o;
+        return Objects.equals(id, role.id) &&
+                Objects.equals(roleName, role.roleName) &&
+                Objects.equals(description, role.description);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, roleName, description);
+    }
+}

+ 33 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/RoleFunc.java

@@ -0,0 +1,33 @@
+package com.zkqy.system.domain.sso;
+
+public class RoleFunc {
+    private Integer id;
+
+    private Integer roleId;
+
+    private Integer funcId;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getRoleId() {
+        return roleId;
+    }
+
+    public void setRoleId(Integer roleId) {
+        this.roleId = roleId;
+    }
+
+    public Integer getFuncId() {
+        return funcId;
+    }
+
+    public void setFuncId(Integer funcId) {
+        this.funcId = funcId;
+    }
+}

+ 127 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/SsoAccessToken.java

@@ -0,0 +1,127 @@
+package com.zkqy.system.domain.sso;
+
+import java.util.Date;
+
+public class SsoAccessToken {
+    private Integer id;
+
+    private String accessToken;
+
+    private Long userId;
+
+    private String userName;
+
+    private String ip;
+
+    private Integer clientId;
+    /**
+     * Token的使用渠道(比如APP1、APP2)
+     */
+    private String channel;
+
+    private Long expiresIn;
+
+    private Long createUser;
+
+    private Date createTime;
+
+    private Long updateUser;
+
+    private Date updateTime;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken == null ? null : accessToken.trim();
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName == null ? null : userName.trim();
+    }
+
+    public String getIp() {
+        return ip;
+    }
+
+    public void setIp(String ip) {
+        this.ip = ip == null ? null : ip.trim();
+    }
+
+    public Integer getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(Integer clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getChannel() {
+        return channel;
+    }
+
+    public void setChannel(String channel) {
+        this.channel = channel == null ? null : channel.trim();
+    }
+
+    public Long getExpiresIn() {
+        return expiresIn;
+    }
+
+    public void setExpiresIn(Long expiresIn) {
+        this.expiresIn = expiresIn;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public Long getCreateUser() {
+        return createUser;
+    }
+
+    public void setCreateUser(Long createUser) {
+        this.createUser = createUser;
+    }
+
+    public Long getUpdateUser() {
+        return updateUser;
+    }
+
+    public void setUpdateUser(Long updateUser) {
+        this.updateUser = updateUser;
+    }
+}

+ 70 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/SsoClientDetails.java

@@ -0,0 +1,70 @@
+package com.zkqy.system.domain.sso;
+
+public class SsoClientDetails {
+    private Integer id;
+
+    /**
+     * 接入单点登录的系统名称
+     */
+    private String clientName;
+
+    private String description;
+    /**
+     * 请求Token的回调URL
+     */
+    private String redirectUrl;
+    /**
+     * 注销URL
+     */
+    private String logoutUrl;
+
+    private Integer status;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getClientName() {
+        return clientName;
+    }
+
+    public void setClientName(String clientName) {
+        this.clientName = clientName == null ? null : clientName.trim();
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description == null ? null : description.trim();
+    }
+
+    public String getRedirectUrl() {
+        return redirectUrl;
+    }
+
+    public void setRedirectUrl(String redirectUrl) {
+        this.redirectUrl = redirectUrl == null ? null : redirectUrl.trim();
+    }
+
+    public String getLogoutUrl() {
+        return logoutUrl;
+    }
+
+    public void setLogoutUrl(String logoutUrl) {
+        this.logoutUrl = logoutUrl == null ? null : logoutUrl.trim();
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+}

+ 85 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/SsoRefreshToken.java

@@ -0,0 +1,85 @@
+package com.zkqy.system.domain.sso;
+
+import java.util.Date;
+
+public class SsoRefreshToken {
+    private Integer id;
+
+    private Integer tokenId;
+
+    private String refreshToken;
+
+    private Long expiresIn;
+
+    private Long createUser;
+
+    private Date createTime;
+
+    private Long updateUser;
+
+    private Date updateTime;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getTokenId() {
+        return tokenId;
+    }
+
+    public void setTokenId(Integer tokenId) {
+        this.tokenId = tokenId;
+    }
+
+    public String getRefreshToken() {
+        return refreshToken;
+    }
+
+    public void setRefreshToken(String refreshToken) {
+        this.refreshToken = refreshToken == null ? null : refreshToken.trim();
+    }
+
+    public Long getExpiresIn() {
+        return expiresIn;
+    }
+
+    public void setExpiresIn(Long expiresIn) {
+        this.expiresIn = expiresIn;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public Long getCreateUser() {
+        return createUser;
+    }
+
+    public void setCreateUser(Long createUser) {
+        this.createUser = createUser;
+    }
+
+    public Long getUpdateUser() {
+        return updateUser;
+    }
+
+    public void setUpdateUser(Long updateUser) {
+        this.updateUser = updateUser;
+    }
+}

+ 99 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/User.java

@@ -0,0 +1,99 @@
+package com.zkqy.system.domain.sso;
+
+import java.util.Date;
+
+public class User {
+    private Integer id;
+
+    private String username;
+
+    private String password;
+
+    private String mobile;
+
+    private String email;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    private Integer status;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username == null ? null : username.trim();
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password == null ? null : password.trim();
+    }
+
+    public String getMobile() {
+        return mobile;
+    }
+
+    public void setMobile(String mobile) {
+        this.mobile = mobile == null ? null : mobile.trim();
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email == null ? null : email.trim();
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    @Override
+    public String toString() {
+        return "User{" +
+                "id=" + id +
+                ", username='" + username + '\'' +
+                ", password='" + password + '\'' +
+                ", mobile='" + mobile + '\'' +
+                ", email='" + email + '\'' +
+                ", createTime=" + createTime +
+                ", updateTime=" + updateTime +
+                ", status=" + status +
+                '}';
+    }
+}

+ 41 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/sso/UserRole.java

@@ -0,0 +1,41 @@
+package com.zkqy.system.domain.sso;
+
+public class UserRole {
+    public UserRole() {
+    }
+
+    public UserRole(Integer userId, Integer roleId) {
+        this.userId = userId;
+        this.roleId = roleId;
+    }
+
+    private Integer id;
+
+    private Integer userId;
+
+    private Integer roleId;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
+
+    public Integer getRoleId() {
+        return roleId;
+    }
+
+    public void setRoleId(Integer roleId) {
+        this.roleId = roleId;
+    }
+}

+ 9 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/SysUserMapper.java

@@ -175,4 +175,13 @@ public interface SysUserMapper {
      * 根据角色key查询用户信息
      */
     List<SysUser> selectUserListByRoleKey(@Param("roleKey") String roleKey,@Param("tenantId") Long tenantId);
+
+    /**
+     * 查询当前租户用户信息
+     *
+     * @param tenantCode 租户编号
+     * @param userName   用户名
+     * @return 用户对象信息
+     */
+    public SysUser selectUserInfoByTenantCode(@Param("tenantCode") String tenantCode, @Param("userName") String userName);
 }

+ 23 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/sso/AuthAccessTokenMapper.java

@@ -0,0 +1,23 @@
+package com.zkqy.system.mapper.sso;
+
+import com.zkqy.system.domain.sso.AuthAccessToken;
+import org.apache.ibatis.annotations.Param;
+public interface AuthAccessTokenMapper {
+    int deleteByPrimaryKey(Integer id);
+
+    int insert(AuthAccessToken authAccessToken);
+
+    int insertSelective(AuthAccessToken authAccessToken);
+
+    AuthAccessToken selectByPrimaryKey(Integer id);
+
+    int updateByPrimaryKeySelective(AuthAccessToken authAccessToken);
+
+    int updateByPrimaryKey(AuthAccessToken authAccessToken);
+
+    AuthAccessToken selectByUserIdClientIdScope(@Param("userId") Long userId, @Param("clientId") Integer clientId, @Param("scope") String scope);
+
+    AuthAccessToken selectByAccessToken(@Param("accessToken") String accessToken);
+
+    int delTokenByUserId(Long userId);
+}

+ 21 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/sso/AuthClientDetailsMapper.java

@@ -0,0 +1,21 @@
+package com.zkqy.system.mapper.sso;
+
+import com.zkqy.system.domain.sso.AuthClientDetails;
+import org.apache.ibatis.annotations.Param;
+
+public interface AuthClientDetailsMapper {
+
+    int deleteByPrimaryKey(Integer id);
+
+    int insert(AuthClientDetails record);
+
+    int insertSelective(AuthClientDetails record);
+
+    AuthClientDetails selectByPrimaryKey(Integer id);
+
+    int updateByPrimaryKeySelective(AuthClientDetails record);
+
+    int updateByPrimaryKey(AuthClientDetails record);
+
+    AuthClientDetails selectByClientId(@Param("clientId") String clientId);
+}

+ 20 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/sso/AuthClientUserMapper.java

@@ -0,0 +1,20 @@
+package com.zkqy.system.mapper.sso;
+
+import com.zkqy.system.domain.sso.AuthClientUser;
+import org.apache.ibatis.annotations.Param;
+
+public interface AuthClientUserMapper {
+    int deleteByPrimaryKey(Integer id);
+
+    int insert(AuthClientUser record);
+
+    int insertSelective(AuthClientUser record);
+
+    AuthClientUser selectByPrimaryKey(Integer id);
+
+    int updateByPrimaryKeySelective(AuthClientUser record);
+
+    int updateByPrimaryKey(AuthClientUser record);
+
+    AuthClientUser selectByClientId(@Param("clientId") Integer clientId, @Param("userId") Integer userId, @Param("scopeId") Integer scopeId);
+}

+ 26 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/sso/AuthRefreshTokenMapper.java

@@ -0,0 +1,26 @@
+package com.zkqy.system.mapper.sso;
+
+import com.zkqy.system.domain.sso.AuthRefreshToken;
+import org.apache.ibatis.annotations.Param;
+
+
+public interface AuthRefreshTokenMapper {
+
+    int deleteByPrimaryKey(Integer id);
+
+    int insert(AuthRefreshToken authRefreshToken);
+
+    int insertSelective(AuthRefreshToken authRefreshToken);
+
+    AuthRefreshToken selectByPrimaryKey(Integer id);
+
+    int updateByPrimaryKeySelective(AuthRefreshToken authRefreshToken);
+
+    int updateByPrimaryKey(AuthRefreshToken authRefreshToken);
+
+    AuthRefreshToken selectByTokenId(@Param("tokenId") Integer tokenId);
+
+    AuthRefreshToken selectByRefreshToken(@Param("refreshToken") String refreshToken);
+
+    int delTokenByUserId(Long userId);
+}

+ 20 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/sso/AuthScopeMapper.java

@@ -0,0 +1,20 @@
+package com.zkqy.system.mapper.sso;
+
+import com.zkqy.system.domain.sso.AuthScope;
+import org.apache.ibatis.annotations.Param;
+
+public interface AuthScopeMapper {
+    int deleteByPrimaryKey(Integer id);
+
+    int insert(AuthScope record);
+
+    int insertSelective(AuthScope record);
+
+    AuthScope selectByPrimaryKey(Integer id);
+
+    int updateByPrimaryKeySelective(AuthScope record);
+
+    int updateByPrimaryKey(AuthScope record);
+
+    AuthScope selectByScopeName(@Param("scopeName") String scopeName);
+}

+ 22 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/sso/SsoAccessTokenMapper.java

@@ -0,0 +1,22 @@
+package com.zkqy.system.mapper.sso;
+
+import com.zkqy.system.domain.sso.SsoAccessToken;
+import org.apache.ibatis.annotations.Param;
+
+public interface SsoAccessTokenMapper {
+    int deleteByPrimaryKey(Integer id);
+
+    int insert(SsoAccessToken record);
+
+    int insertSelective(SsoAccessToken record);
+
+    SsoAccessToken selectByPrimaryKey(Integer id);
+
+    int updateByPrimaryKeySelective(SsoAccessToken record);
+
+    int updateByPrimaryKey(SsoAccessToken record);
+
+    SsoAccessToken selectByUserIdAndClientId(@Param("userId") Long userId, @Param("clientId") Integer clientId);
+
+    SsoAccessToken selectByAccessToken(@Param("accessToken") String accessToken);
+}

+ 21 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/sso/SsoClientDetailsMapper.java

@@ -0,0 +1,21 @@
+package com.zkqy.system.mapper.sso;
+
+import com.zkqy.system.domain.sso.SsoClientDetails;
+import org.apache.ibatis.annotations.Param;
+
+
+public interface SsoClientDetailsMapper {
+    int deleteByPrimaryKey(Integer id);
+
+    int insert(SsoClientDetails record);
+
+    int insertSelective(SsoClientDetails record);
+
+    SsoClientDetails selectByPrimaryKey(Integer id);
+
+    int updateByPrimaryKeySelective(SsoClientDetails record);
+
+    int updateByPrimaryKey(SsoClientDetails record);
+
+    SsoClientDetails selectByRedirectUrl(@Param("redirectUrl") String redirectUrl);
+}

+ 24 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/sso/SsoRefreshTokenMapper.java

@@ -0,0 +1,24 @@
+package com.zkqy.system.mapper.sso;
+
+
+import com.zkqy.system.domain.sso.SsoRefreshToken;
+import org.apache.ibatis.annotations.Param;
+
+public interface SsoRefreshTokenMapper {
+
+    int deleteByPrimaryKey(Integer id);
+
+    int insert(SsoRefreshToken record);
+
+    int insertSelective(SsoRefreshToken record);
+
+    SsoRefreshToken selectByPrimaryKey(Integer id);
+
+    int updateByPrimaryKeySelective(SsoRefreshToken record);
+
+    int updateByPrimaryKey(SsoRefreshToken record);
+
+    SsoRefreshToken selectByTokenId(@Param("tokenId") Integer tokenId);
+
+    SsoRefreshToken selectByRefreshToken(@Param("refreshToken") String refreshToken);
+}

+ 19 - 1
zkqy-system/src/main/java/com/zkqy/system/service/ISysUserService.java

@@ -228,9 +228,27 @@ public interface ISysUserService {
 
     /**
      * 根据角色权限字符查询用户
+     *
      * @param roleKey
      * @param tenantId
      * @return
      */
-    List<SysUser> selectUserListByRoleKey(String roleKey,Long tenantId);
+    List<SysUser> selectUserListByRoleKey(String roleKey, Long tenantId);
+
+
+    /**
+     * oauth2认证接口
+     *
+     * @return
+     */
+    SysUser queryUserInfo(String endUsername, String password);
+
+    /**
+     * 查询当前租户用户信息
+     *
+     * @param tenantCode 租户编号
+     * @param userName   用户名
+     * @return 用户对象信息
+     */
+    public SysUser selectUserInfoByTenantCode(String tenantCode, String userName);
 }

+ 24 - 5
zkqy-system/src/main/java/com/zkqy/system/service/impl/SysUserServiceImpl.java

@@ -1,11 +1,11 @@
 package com.zkqy.system.service.impl;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
+import java.text.MessageFormat;
+import java.util.*;
 import java.util.stream.Collectors;
 import javax.validation.Validator;
 
+import com.zkqy.common.utils.sso.EncryptUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -166,7 +166,7 @@ public class SysUserServiceImpl implements ISysUserService {
     @Override
     public boolean checkUserNameUnique(SysUser user) {
         Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
-        SysUser info = userMapper.checkUserNameUnique(SecurityUtils.getTenantId().toString(),user.getUserName());
+        SysUser info = userMapper.checkUserNameUnique(SecurityUtils.getTenantId().toString(), user.getUserName());
         if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
             return UserConstants.NOT_UNIQUE;
         }
@@ -466,7 +466,7 @@ public class SysUserServiceImpl implements ISysUserService {
                     user.setPassword(SecurityUtils.encryptPassword(password));
                     user.setCreateBy(operName);
                     Long userId = SecurityUtils.getUserId();
-                    if (!SecurityUtils.isAdmin(userId)){
+                    if (!SecurityUtils.isAdmin(userId)) {
                         user.setTenantId(SecurityUtils.getTenantId());
                     }
                     userMapper.insertUser(user);
@@ -531,4 +531,23 @@ public class SysUserServiceImpl implements ISysUserService {
         return userMapper.selectUserListByRoleKey(roleKey, tenantId);
     }
 
+    @Override
+    public SysUser queryUserInfo(String endUsername, String password) {
+        String end[] = endUsername.split("¥¥¥");
+        if (end.length != 2) {
+            return null;
+        }
+        String tenantCode = end[0];
+        String username = end[1];
+        Map<String, Object> result = new HashMap<>(2);
+        log.info(MessageFormat.format("单点登录:username:{0},password:{1},tenantCode:{2}", username, password, tenantCode));
+        SysUser sysUser = userMapper.selectUserByTenantInfo(tenantCode, username);
+        return sysUser;
+    }
+
+    @Override
+    public SysUser selectUserInfoByTenantCode(String tenantCode, String userName) {
+        return userMapper.selectUserInfoByTenantCode(tenantCode, userName);
+    }
+
 }

+ 40 - 0
zkqy-system/src/main/java/com/zkqy/system/service/sso/AuthorizationService.java

@@ -0,0 +1,40 @@
+package com.zkqy.system.service.sso;
+
+import com.zkqy.common.core.domain.entity.SysUser;
+import com.zkqy.system.domain.sso.AuthAccessToken;
+import com.zkqy.system.domain.sso.AuthClientDetails;
+import com.zkqy.system.domain.sso.AuthRefreshToken;
+
+
+public interface AuthorizationService {
+
+
+    String register(AuthClientDetails clientDetails);
+
+
+    AuthClientDetails selectClientDetailsById(Integer id);
+
+    AuthClientDetails selectClientDetailsByClientId(String clientId);
+
+
+    AuthAccessToken selectByAccessToken(String accessToken);
+
+    AuthAccessToken selectByAccessId(Integer id);
+
+
+    AuthRefreshToken selectByRefreshToken(String refreshToken);
+
+
+    boolean saveAuthClientUser(Integer userId, String clientIdStr, String scopeStr);
+
+
+    String createAuthorizationCode(String clientIdStr, String scopeStr, SysUser sysUser);
+
+
+    String createAccessToken(SysUser sysUser, AuthClientDetails savedClientDetails, String grantType, String scope, Long expiresIn);
+
+
+    String createRefreshToken(SysUser sysUser, AuthAccessToken authAccessToken);
+
+    int delTokenByUserId(Long userId);
+}

+ 14 - 0
zkqy-system/src/main/java/com/zkqy/system/service/sso/RedisService.java

@@ -0,0 +1,14 @@
+package com.zkqy.system.service.sso;
+
+import java.util.concurrent.TimeUnit;
+
+public interface RedisService {
+
+    void set(String key, Object value);
+
+    void setWithExpire(String key, Object value, long time, TimeUnit timeUnit);
+
+    <K> K get(String key);
+
+    boolean delete(String key);
+}

+ 32 - 0
zkqy-system/src/main/java/com/zkqy/system/service/sso/SsoService.java

@@ -0,0 +1,32 @@
+package com.zkqy.system.service.sso;
+
+
+import com.zkqy.common.core.domain.entity.SysUser;
+import com.zkqy.system.domain.sso.SsoAccessToken;
+import com.zkqy.system.domain.sso.SsoClientDetails;
+import com.zkqy.system.domain.sso.SsoRefreshToken;
+
+public interface SsoService {
+
+
+    SsoClientDetails selectByPrimaryKey(Integer id);
+
+
+    SsoClientDetails selectByRedirectUrl(String redirectUrl);
+
+    SsoAccessToken selectByAccessId(Integer id);
+
+    SsoAccessToken selectByAccessToken(String accessToken);
+
+
+    SsoRefreshToken selectByTokenId(Integer tokenId);
+
+    SsoRefreshToken selectByRefreshToken(String refreshToken);
+
+
+    String createAccessToken(SysUser sysUser, Long expiresIn, String requestIP, SsoClientDetails ssoClientDetails);
+
+
+    String createRefreshToken(SysUser sysUser, SsoAccessToken ssoAccessToken);
+
+}

+ 214 - 0
zkqy-system/src/main/java/com/zkqy/system/service/sso/impl/AuthorizationServiceImpl.java

@@ -0,0 +1,214 @@
+package com.zkqy.system.service.sso.impl;
+
+import com.zkqy.common.core.domain.entity.SysUser;
+import com.zkqy.common.enums.sso.ExpireEnum;
+import com.zkqy.common.utils.sso.Constants;
+import com.zkqy.common.utils.sso.DateUtils;
+import com.zkqy.common.utils.sso.EncryptUtils;
+import com.zkqy.common.utils.sso.SpringContextUtils;
+import com.zkqy.system.domain.sso.*;
+import com.zkqy.system.mapper.sso.*;
+import com.zkqy.system.service.sso.AuthorizationService;
+import com.zkqy.system.service.sso.RedisService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpSession;
+import java.util.Date;
+
+
+@Service
+public class AuthorizationServiceImpl implements AuthorizationService {
+
+    @Autowired
+    private RedisService redisService;
+
+    @Autowired
+    private AuthClientDetailsMapper authClientDetailsMapper;
+    @Autowired
+    private AuthScopeMapper authScopeMapper;
+    @Autowired
+    private AuthClientUserMapper authClientUserMapper;
+    @Autowired
+    private AuthAccessTokenMapper authAccessTokenMapper;
+    @Autowired
+    private AuthRefreshTokenMapper authRefreshTokenMapper;
+
+    @Override
+    public String register(AuthClientDetails clientDetails) {
+        if (StringUtils.isNoneBlank(clientDetails.getClientName()) && StringUtils.isNoneBlank(
+                clientDetails.getRedirectUri())) {
+            String clientId = EncryptUtils.getRandomStr1(24);
+            AuthClientDetails savedClientDetails = authClientDetailsMapper.selectByClientId(clientId);
+            for (int i = 0; i < 10; i++) {
+                if (savedClientDetails == null) {
+                    break;
+                } else {
+                    clientId = EncryptUtils.getRandomStr1(24);
+                    savedClientDetails = authClientDetailsMapper.selectByClientId(clientId);
+                }
+            }
+            String clientSecret = EncryptUtils.getRandomStr1(32);
+            Date current = new Date();
+            HttpSession session = SpringContextUtils.getSession();
+            User user = (User) session.getAttribute(Constants.SESSION_USER);
+            clientDetails.setClientId(clientId);
+            clientDetails.setClientSecret(clientSecret);
+            clientDetails.setCreateUser(user.getId());
+            clientDetails.setCreateTime(current);
+            clientDetails.setUpdateUser(user.getId());
+            clientDetails.setUpdateTime(current);
+            clientDetails.setStatus(1);
+            //保存到数据库
+            authClientDetailsMapper.insertSelective(clientDetails);
+            return clientId;
+        } else {
+            return "";
+        }
+    }
+
+    @Override
+    public AuthClientDetails selectClientDetailsById(Integer id) {
+        return authClientDetailsMapper.selectByPrimaryKey(id);
+    }
+
+    @Override
+    public AuthClientDetails selectClientDetailsByClientId(String clientId) {
+        return authClientDetailsMapper.selectByClientId(clientId);
+    }
+
+    @Override
+    public AuthAccessToken selectByAccessToken(String accessToken) {
+        return authAccessTokenMapper.selectByAccessToken(accessToken);
+    }
+
+    @Override
+    public AuthAccessToken selectByAccessId(Integer id) {
+        return authAccessTokenMapper.selectByPrimaryKey(id);
+    }
+
+    @Override
+    public AuthRefreshToken selectByRefreshToken(String refreshToken) {
+        return authRefreshTokenMapper.selectByRefreshToken(refreshToken);
+    }
+
+    @Override
+    public boolean saveAuthClientUser(Integer userId, String clientIdStr, String scopeStr) {
+        AuthClientDetails clientDetails = authClientDetailsMapper.selectByClientId(clientIdStr);
+        AuthScope scope = authScopeMapper.selectByScopeName(scopeStr);
+
+        if (clientDetails != null && scope != null) {
+            AuthClientUser clientUser = authClientUserMapper.selectByClientId(clientDetails.getId(),
+                    userId, scope.getId());
+            //如果数据库中不存在记录,则插入
+            if (clientUser == null) {
+                clientUser = new AuthClientUser();
+                clientUser.setUserId(userId);
+                clientUser.setAuthClientId(clientDetails.getId());
+                clientUser.setAuthScopeId(scope.getId());
+                authClientUserMapper.insert(clientUser);
+            }
+
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public String createAuthorizationCode(String clientIdStr, String scopeStr, SysUser sysUser) {
+        String str = clientIdStr + scopeStr + DateUtils.currentTimeMillis();
+        String encryptedStr = EncryptUtils.sha1Hex(str);
+        redisService.setWithExpire(encryptedStr + ":scope", scopeStr,
+                (ExpireEnum.AUTHORIZATION_CODE.getTime()), ExpireEnum.AUTHORIZATION_CODE.getTimeUnit());
+        redisService.setWithExpire(encryptedStr + ":user", sysUser,
+                (ExpireEnum.AUTHORIZATION_CODE.getTime()), ExpireEnum.AUTHORIZATION_CODE.getTimeUnit());
+        return encryptedStr;
+    }
+
+    @Override
+    public String createAccessToken(SysUser sysUser, AuthClientDetails savedClientDetails, String grantType,
+                                    String scope, Long expiresIn) {
+        Date current = new Date();
+        Long expiresAt = DateUtils.nextDaysSecond(ExpireEnum.ACCESS_TOKEN.getTime(), null);
+        String str = sysUser.getUserName() + savedClientDetails.getClientId() +
+                DateUtils.currentTimeMillis();
+        String accessTokenStr = "1." + EncryptUtils.sha1Hex(str) + "." + expiresIn + "." + expiresAt;
+        // 保存Access Token
+        AuthAccessToken savedAccessToken = authAccessTokenMapper.selectByUserIdClientIdScope(
+                sysUser.getUserId()
+                , savedClientDetails.getId(), scope);
+        //如果存在userId + clientId + scope匹配的记录,则更新原记录,否则向数据库中插入新记录
+        if (savedAccessToken != null) {
+            savedAccessToken.setAccessToken(accessTokenStr);
+            savedAccessToken.setExpiresIn(expiresAt);
+            savedAccessToken.setUpdateUser(sysUser.getUserId());
+            savedAccessToken.setUpdateTime(current);
+            authAccessTokenMapper.updateByPrimaryKeySelective(savedAccessToken);
+        } else {
+            savedAccessToken = new AuthAccessToken();
+            savedAccessToken.setAccessToken(accessTokenStr);
+            savedAccessToken.setUserId(sysUser.getUserId());
+            savedAccessToken.setUserName(sysUser.getUserName());
+            savedAccessToken.setClientId(savedClientDetails.getId());
+            savedAccessToken.setExpiresIn(expiresAt);
+            savedAccessToken.setScope(scope);
+            savedAccessToken.setGrantType(grantType);
+            savedAccessToken.setCreateUser(sysUser.getUserId());
+            savedAccessToken.setUpdateUser(sysUser.getUserId());
+            savedAccessToken.setCreateTime(current);
+            savedAccessToken.setUpdateTime(current);
+            authAccessTokenMapper.insertSelective(savedAccessToken);
+        }
+        return accessTokenStr;
+    }
+
+
+    @Override
+    public String createRefreshToken(SysUser sysUser, AuthAccessToken authAccessToken) {
+        Date current = new Date();
+        //过期时间
+        Long expiresIn = DateUtils.dayToSecond(ExpireEnum.REFRESH_TOKEN.getTime());
+        //过期的时间戳
+        Long expiresAt = DateUtils.nextDaysSecond(ExpireEnum.REFRESH_TOKEN.getTime(), null);
+        //1. 拼装待加密字符串(username + accessToken + 当前精确到毫秒的时间戳)
+        String str = sysUser.getUserName() + authAccessToken.getAccessToken() + String.valueOf(
+                DateUtils.currentTimeMillis());
+
+        //2. SHA1加密
+        String refreshTokenStr = "2." + EncryptUtils.sha1Hex(str) + "." + expiresIn + "." + expiresAt;
+
+        //3. 保存Refresh Token
+        AuthRefreshToken savedRefreshToken = authRefreshTokenMapper.selectByTokenId(
+                authAccessToken.getId());
+        //如果存在tokenId匹配的记录,则更新原记录,否则向数据库中插入新记录
+        if (savedRefreshToken != null) {
+            savedRefreshToken.setRefreshToken(refreshTokenStr);
+            savedRefreshToken.setExpiresIn(expiresAt);
+            savedRefreshToken.setUpdateUser(sysUser.getUserId());
+            savedRefreshToken.setUpdateTime(current);
+            authRefreshTokenMapper.updateByPrimaryKeySelective(savedRefreshToken);
+        } else {
+            savedRefreshToken = new AuthRefreshToken();
+            savedRefreshToken.setTokenId(authAccessToken.getId());
+            savedRefreshToken.setRefreshToken(refreshTokenStr);
+            savedRefreshToken.setExpiresIn(expiresAt);
+            savedRefreshToken.setCreateUser(sysUser.getUserId());
+            savedRefreshToken.setUpdateUser(sysUser.getUserId());
+            savedRefreshToken.setCreateTime(current);
+            savedRefreshToken.setUpdateTime(current);
+            authRefreshTokenMapper.insertSelective(savedRefreshToken);
+        }
+
+        //4. 返回Refresh Token
+        return refreshTokenStr;
+    }
+
+    @Override
+    public int delTokenByUserId(Long userId) {
+        authAccessTokenMapper.delTokenByUserId(userId);
+        return authRefreshTokenMapper.delTokenByUserId(userId);
+    }
+
+}

+ 42 - 0
zkqy-system/src/main/java/com/zkqy/system/service/sso/impl/RedisServiceImpl.java

@@ -0,0 +1,42 @@
+package com.zkqy.system.service.sso.impl;
+
+import com.zkqy.system.service.sso.RedisService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.BoundValueOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.TimeUnit;
+
+@Service
+public class RedisServiceImpl implements RedisService {
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    @Override
+    public void set(String key, Object value) {
+        ValueOperations<String, Object> valueOperation = redisTemplate.opsForValue();
+        valueOperation.set(key, value);
+    }
+
+    @Override
+    public void setWithExpire(String key, Object value, long time, TimeUnit timeUnit) {
+        BoundValueOperations<String, Object> boundValueOperations = redisTemplate.boundValueOps(key);
+        boundValueOperations.set(value);
+        boundValueOperations.expire(time, timeUnit);
+    }
+
+    @Override
+    public <K> K get(String key) {
+        ValueOperations<String, Object> valueOperation = redisTemplate.opsForValue();
+
+        return (K) valueOperation.get(key);
+    }
+
+    @Override
+    public boolean delete(String key) {
+        return redisTemplate.delete(key);
+    }
+}

+ 139 - 0
zkqy-system/src/main/java/com/zkqy/system/service/sso/impl/SsoServiceImpl.java

@@ -0,0 +1,139 @@
+package com.zkqy.system.service.sso.impl;
+
+import com.zkqy.common.core.domain.entity.SysUser;
+import com.zkqy.common.enums.sso.ExpireEnum;
+import com.zkqy.common.utils.sso.DateUtils;
+import com.zkqy.common.utils.sso.EncryptUtils;
+import com.zkqy.system.domain.sso.SsoAccessToken;
+import com.zkqy.system.domain.sso.SsoClientDetails;
+import com.zkqy.system.domain.sso.SsoRefreshToken;
+import com.zkqy.system.mapper.sso.SsoAccessTokenMapper;
+import com.zkqy.system.mapper.sso.SsoClientDetailsMapper;
+import com.zkqy.system.mapper.sso.SsoRefreshTokenMapper;
+import com.zkqy.system.service.sso.SsoService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+
+@Service
+public class SsoServiceImpl implements SsoService {
+
+    @Autowired
+    private SsoAccessTokenMapper ssoAccessTokenMapper;
+    @Autowired
+    private SsoRefreshTokenMapper ssoRefreshTokenMapper;
+    @Autowired
+    private SsoClientDetailsMapper ssoClientDetailsMapper;
+
+    @Override
+    public SsoClientDetails selectByPrimaryKey(Integer id) {
+        return ssoClientDetailsMapper.selectByPrimaryKey(id);
+    }
+
+    @Override
+    public SsoClientDetails selectByRedirectUrl(String redirectUrl) {
+        return ssoClientDetailsMapper.selectByRedirectUrl(redirectUrl);
+    }
+
+    @Override
+    public SsoAccessToken selectByAccessId(Integer id) {
+        return ssoAccessTokenMapper.selectByPrimaryKey(id);
+    }
+
+    @Override
+    public SsoAccessToken selectByAccessToken(String accessToken) {
+        return ssoAccessTokenMapper.selectByAccessToken(accessToken);
+    }
+
+    @Override
+    public SsoRefreshToken selectByTokenId(Integer tokenId) {
+        return ssoRefreshTokenMapper.selectByTokenId(tokenId);
+    }
+
+    @Override
+    public SsoRefreshToken selectByRefreshToken(String refreshToken) {
+        return ssoRefreshTokenMapper.selectByRefreshToken(refreshToken);
+    }
+
+    @Override
+    public String createAccessToken(SysUser sysUser, Long expiresIn, String requestIP, SsoClientDetails ssoClientDetails) {
+        Date current = new Date();
+        //过期的时间戳
+        Long expiresAt = DateUtils.nextDaysSecond(ExpireEnum.ACCESS_TOKEN.getTime(), null);
+
+        //1. 拼装待加密字符串(username + 渠道CODE + 当前精确到毫秒的时间戳)
+        String str = sysUser.getUserName() + ssoClientDetails.getClientName() + String.valueOf(DateUtils.currentTimeMillis());
+
+        //2. SHA1加密
+        String accessTokenStr = "11." + EncryptUtils.sha1Hex(str) + "." + expiresIn + "." + expiresAt;
+
+        //3. 保存Access Token
+        SsoAccessToken savedAccessToken = ssoAccessTokenMapper.selectByUserIdAndClientId(sysUser.getUserId(), ssoClientDetails.getId());
+        //如果存在匹配的记录,则更新原记录,否则向数据库中插入新记录
+        if (savedAccessToken != null) {
+            savedAccessToken.setAccessToken(accessTokenStr);
+            savedAccessToken.setExpiresIn(expiresAt);
+            savedAccessToken.setUpdateUser(sysUser.getUserId());
+            savedAccessToken.setUpdateTime(current);
+            ssoAccessTokenMapper.updateByPrimaryKeySelective(savedAccessToken);
+        } else {
+            savedAccessToken = new SsoAccessToken();
+            savedAccessToken.setAccessToken(accessTokenStr);
+            savedAccessToken.setUserId(sysUser.getUserId());
+            savedAccessToken.setUserName(sysUser.getUserName());
+            savedAccessToken.setIp(requestIP);
+            savedAccessToken.setClientId(ssoClientDetails.getId());
+            savedAccessToken.setChannel(ssoClientDetails.getClientName());
+            savedAccessToken.setExpiresIn(expiresAt);
+            savedAccessToken.setCreateUser(sysUser.getUserId());
+            savedAccessToken.setUpdateUser(sysUser.getUserId());
+            savedAccessToken.setCreateTime(current);
+            savedAccessToken.setUpdateTime(current);
+            ssoAccessTokenMapper.insertSelective(savedAccessToken);
+        }
+
+        //4. 返回Access Token
+        return accessTokenStr;
+    }
+
+    @Override
+    public String createRefreshToken(SysUser sysUser, SsoAccessToken ssoAccessToken) {
+        Date current = new Date();
+        //过期时间
+        Long expiresIn = DateUtils.dayToSecond(ExpireEnum.REFRESH_TOKEN.getTime());
+        //过期的时间戳
+        Long expiresAt = DateUtils.nextDaysSecond(ExpireEnum.REFRESH_TOKEN.getTime(), null);
+
+        //1. 拼装待加密字符串(username + accessToken + 当前精确到毫秒的时间戳)
+        String str = sysUser.getUserName() + ssoAccessToken.getAccessToken() + String.valueOf(DateUtils.currentTimeMillis());
+
+        //2. SHA1加密
+        String refreshTokenStr = "12." + EncryptUtils.sha1Hex(str) + "." + expiresIn + "." + expiresAt;
+
+        //3. 保存Refresh Token
+        SsoRefreshToken savedRefreshToken = ssoRefreshTokenMapper.selectByTokenId(ssoAccessToken.getId());
+        //如果存在tokenId匹配的记录,则更新原记录,否则向数据库中插入新记录
+        if (savedRefreshToken != null) {
+            savedRefreshToken.setRefreshToken(refreshTokenStr);
+            savedRefreshToken.setExpiresIn(expiresAt);
+            savedRefreshToken.setUpdateUser(sysUser.getUserId());
+            savedRefreshToken.setUpdateTime(current);
+            ssoRefreshTokenMapper.updateByPrimaryKeySelective(savedRefreshToken);
+        } else {
+            savedRefreshToken = new SsoRefreshToken();
+            savedRefreshToken.setTokenId(ssoAccessToken.getId());
+            savedRefreshToken.setRefreshToken(refreshTokenStr);
+            savedRefreshToken.setExpiresIn(expiresAt);
+            savedRefreshToken.setCreateUser(sysUser.getUserId());
+            savedRefreshToken.setUpdateUser(sysUser.getUserId());
+            savedRefreshToken.setCreateTime(current);
+            savedRefreshToken.setUpdateTime(current);
+            ssoRefreshTokenMapper.insertSelective(savedRefreshToken);
+        }
+
+        //4. 返回Refresh Tokens
+        return refreshTokenStr;
+    }
+
+}

+ 6 - 0
zkqy-system/src/main/resources/mapper/system/SysUserMapper.xml

@@ -358,4 +358,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		  AND sr.tenant_id = #{tenantId}
 	</select>
 
+
+	<select id="selectUserInfoByTenantCode" parameterType="String" resultMap="SysUserResult">
+		<include refid="selectUserVo"/>
+		where te.tenant_code = #{tenantCode} and u.user_name = #{userName} and u.del_flag = '0'
+	</select>
+
 </mapper> 

+ 199 - 0
zkqy-system/src/main/resources/mapper/system/sso/AuthAccessTokenMapper.xml

@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zkqy.system.mapper.sso.AuthAccessTokenMapper">
+    <resultMap id="BaseResultMap" type="com.zkqy.system.domain.sso.AuthAccessToken">
+        <id column="id" jdbcType="INTEGER" property="id"/>
+        <result column="access_token" jdbcType="VARCHAR" property="accessToken"/>
+        <result column="user_id" jdbcType="INTEGER" property="userId"/>
+        <result column="user_name" jdbcType="VARCHAR" property="userName"/>
+        <result column="client_id" jdbcType="INTEGER" property="clientId"/>
+        <result column="expires_in" jdbcType="BIGINT" property="expiresIn"/>
+        <result column="grant_type" jdbcType="VARCHAR" property="grantType"/>
+        <result column="scope" jdbcType="VARCHAR" property="scope"/>
+        <result column="create_user" jdbcType="INTEGER" property="createUser"/>
+        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
+        <result column="update_user" jdbcType="INTEGER" property="updateUser"/>
+        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
+    </resultMap>
+    <sql id="Base_Column_List">
+        id
+        , access_token, user_id, user_name, client_id, expires_in, grant_type, scope, create_user,
+    create_time, update_user, update_time
+    </sql>
+    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from auth_access_token
+        where id = #{id,jdbcType=INTEGER}
+    </select>
+    <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
+        delete
+        from auth_access_token
+        where id = #{id,jdbcType=INTEGER}
+    </delete>
+    <insert id="insert" parameterType="com.zkqy.system.domain.sso.AuthAccessToken">
+        insert into auth_access_token (id, access_token, user_id,
+                                       user_name, client_id, expires_in,
+                                       grant_type, scope, create_user,
+                                       create_time, update_user, update_time)
+        values (#{id,jdbcType=INTEGER}, #{accessToken,jdbcType=VARCHAR}, #{userId,jdbcType=INTEGER},
+                #{userName,jdbcType=VARCHAR}, #{clientId,jdbcType=INTEGER},
+                #{expiresIn,jdbcType=BIGINT},
+                #{grantType,jdbcType=VARCHAR}, #{scope,jdbcType=VARCHAR},
+                #{createUser,jdbcType=INTEGER},
+                #{createTime,jdbcType=TIMESTAMP}, #{updateUser,jdbcType=INTEGER},
+                #{updateTime,jdbcType=TIMESTAMP})
+    </insert>
+    <insert id="insertSelective" parameterType="com.zkqy.system.domain.sso.AuthAccessToken">
+        insert into auth_access_token
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">
+                id,
+            </if>
+            <if test="accessToken != null">
+                access_token,
+            </if>
+            <if test="userId != null">
+                user_id,
+            </if>
+            <if test="userName != null">
+                user_name,
+            </if>
+            <if test="clientId != null">
+                client_id,
+            </if>
+            <if test="expiresIn != null">
+                expires_in,
+            </if>
+            <if test="grantType != null">
+                grant_type,
+            </if>
+            <if test="scope != null">
+                scope,
+            </if>
+            <if test="createUser != null">
+                create_user,
+            </if>
+            <if test="createTime != null">
+                create_time,
+            </if>
+            <if test="updateUser != null">
+                update_user,
+            </if>
+            <if test="updateTime != null">
+                update_time,
+            </if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">
+                #{id,jdbcType=INTEGER},
+            </if>
+            <if test="accessToken != null">
+                #{accessToken,jdbcType=VARCHAR},
+            </if>
+            <if test="userId != null">
+                #{userId,jdbcType=INTEGER},
+            </if>
+            <if test="userName != null">
+                #{userName,jdbcType=VARCHAR},
+            </if>
+            <if test="clientId != null">
+                #{clientId,jdbcType=INTEGER},
+            </if>
+            <if test="expiresIn != null">
+                #{expiresIn,jdbcType=BIGINT},
+            </if>
+            <if test="grantType != null">
+                #{grantType,jdbcType=VARCHAR},
+            </if>
+            <if test="scope != null">
+                #{scope,jdbcType=VARCHAR},
+            </if>
+            <if test="createUser != null">
+                #{createUser,jdbcType=INTEGER},
+            </if>
+            <if test="createTime != null">
+                #{createTime,jdbcType=TIMESTAMP},
+            </if>
+            <if test="updateUser != null">
+                #{updateUser,jdbcType=INTEGER},
+            </if>
+            <if test="updateTime != null">
+                #{updateTime,jdbcType=TIMESTAMP},
+            </if>
+        </trim>
+    </insert>
+    <update id="updateByPrimaryKeySelective" parameterType="com.zkqy.system.domain.sso.AuthAccessToken">
+        update auth_access_token
+        <set>
+            <if test="accessToken != null">
+                access_token = #{accessToken,jdbcType=VARCHAR},
+            </if>
+            <if test="userId != null">
+                user_id = #{userId,jdbcType=INTEGER},
+            </if>
+            <if test="userName != null">
+                user_name = #{userName,jdbcType=VARCHAR},
+            </if>
+            <if test="clientId != null">
+                client_id = #{clientId,jdbcType=INTEGER},
+            </if>
+            <if test="expiresIn != null">
+                expires_in = #{expiresIn,jdbcType=BIGINT},
+            </if>
+            <if test="grantType != null">
+                grant_type = #{grantType,jdbcType=VARCHAR},
+            </if>
+            <if test="scope != null">
+                scope = #{scope,jdbcType=VARCHAR},
+            </if>
+            <if test="createUser != null">
+                create_user = #{createUser,jdbcType=INTEGER},
+            </if>
+            <if test="createTime != null">
+                create_time = #{createTime,jdbcType=TIMESTAMP},
+            </if>
+            <if test="updateUser != null">
+                update_user = #{updateUser,jdbcType=INTEGER},
+            </if>
+            <if test="updateTime != null">
+                update_time = #{updateTime,jdbcType=TIMESTAMP},
+            </if>
+        </set>
+        where id = #{id,jdbcType=INTEGER}
+    </update>
+    <update id="updateByPrimaryKey" parameterType="com.zkqy.system.domain.sso.AuthAccessToken">
+        update auth_access_token
+        set access_token = #{accessToken,jdbcType=VARCHAR},
+            user_id      = #{userId,jdbcType=INTEGER},
+            user_name    = #{userName,jdbcType=VARCHAR},
+            client_id    = #{clientId,jdbcType=INTEGER},
+            expires_in   = #{expiresIn,jdbcType=BIGINT},
+            grant_type   = #{grantType,jdbcType=VARCHAR},
+            scope        = #{scope,jdbcType=VARCHAR},
+            create_user  = #{createUser,jdbcType=INTEGER},
+            create_time  = #{createTime,jdbcType=TIMESTAMP},
+            update_user  = #{updateUser,jdbcType=INTEGER},
+            update_time  = #{updateTime,jdbcType=TIMESTAMP}
+        where id = #{id,jdbcType=INTEGER}
+    </update>
+    <select id="selectByUserIdClientIdScope" parameterType="java.util.Map" resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from auth_access_token
+        where user_id = #{userId,jdbcType=INTEGER} AND client_id = #{clientId,jdbcType=INTEGER} AND
+        scope = #{scope,jdbcType=VARCHAR}
+    </select>
+    <select id="selectByAccessToken" parameterType="java.lang.String" resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from auth_access_token
+        where access_token = #{accessToken,jdbcType=VARCHAR}
+    </select>
+    <delete id="delTokenByUserId" parameterType="java.lang.Long">
+        delete
+        from auth_access_token
+        where user_id = #{userId,jdbcType=INTEGER}
+    </delete>
+</mapper>

+ 170 - 0
zkqy-system/src/main/resources/mapper/system/sso/AuthClientDetailsMapper.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zkqy.system.mapper.sso.AuthClientDetailsMapper">
+  <resultMap id="BaseResultMap" type="com.zkqy.system.domain.sso.AuthClientDetails">
+    <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="client_id" jdbcType="VARCHAR" property="clientId" />
+    <result column="client_name" jdbcType="VARCHAR" property="clientName" />
+    <result column="client_secret" jdbcType="VARCHAR" property="clientSecret" />
+    <result column="redirect_uri" jdbcType="VARCHAR" property="redirectUri" />
+    <result column="description" jdbcType="VARCHAR" property="description" />
+    <result column="create_user" jdbcType="INTEGER" property="createUser" />
+    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
+    <result column="update_user" jdbcType="INTEGER" property="updateUser" />
+    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
+    <result column="status" jdbcType="INTEGER" property="status" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    id, client_id, client_name, client_secret, redirect_uri, description, create_user,
+    create_time, update_user, update_time, status
+  </sql>
+  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from auth_client_details
+    where id = #{id,jdbcType=INTEGER}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
+    delete from auth_client_details
+    where id = #{id,jdbcType=INTEGER}
+  </delete>
+  <insert id="insert" parameterType="com.zkqy.system.domain.sso.AuthClientDetails">
+    insert into auth_client_details (id, client_id, client_name,
+      client_secret, redirect_uri, description,
+      create_user, create_time, update_user,
+      update_time, status)
+    values (#{id,jdbcType=INTEGER}, #{clientId,jdbcType=VARCHAR}, #{clientName,jdbcType=VARCHAR},
+      #{clientSecret,jdbcType=VARCHAR}, #{redirectUri,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR},
+      #{createUser,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP}, #{updateUser,jdbcType=INTEGER},
+      #{updateTime,jdbcType=TIMESTAMP}, #{status,jdbcType=INTEGER})
+  </insert>
+  <insert id="insertSelective" parameterType="com.zkqy.system.domain.sso.AuthClientDetails">
+    insert into auth_client_details
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id,
+      </if>
+      <if test="clientId != null">
+        client_id,
+      </if>
+      <if test="clientName != null">
+        client_name,
+      </if>
+      <if test="clientSecret != null">
+        client_secret,
+      </if>
+      <if test="redirectUri != null">
+        redirect_uri,
+      </if>
+      <if test="description != null">
+        description,
+      </if>
+      <if test="createUser != null">
+        create_user,
+      </if>
+      <if test="createTime != null">
+        create_time,
+      </if>
+      <if test="updateUser != null">
+        update_user,
+      </if>
+      <if test="updateTime != null">
+        update_time,
+      </if>
+      <if test="status != null">
+        status,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=INTEGER},
+      </if>
+      <if test="clientId != null">
+        #{clientId,jdbcType=VARCHAR},
+      </if>
+      <if test="clientName != null">
+        #{clientName,jdbcType=VARCHAR},
+      </if>
+      <if test="clientSecret != null">
+        #{clientSecret,jdbcType=VARCHAR},
+      </if>
+      <if test="redirectUri != null">
+        #{redirectUri,jdbcType=VARCHAR},
+      </if>
+      <if test="description != null">
+        #{description,jdbcType=VARCHAR},
+      </if>
+      <if test="createUser != null">
+        #{createUser,jdbcType=INTEGER},
+      </if>
+      <if test="createTime != null">
+        #{createTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="updateUser != null">
+        #{updateUser,jdbcType=INTEGER},
+      </if>
+      <if test="updateTime != null">
+        #{updateTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="status != null">
+        #{status,jdbcType=INTEGER},
+      </if>
+    </trim>
+  </insert>
+  <update id="updateByPrimaryKeySelective" parameterType="com.zkqy.system.domain.sso.AuthClientDetails">
+    update auth_client_details
+    <set>
+      <if test="clientId != null">
+        client_id = #{clientId,jdbcType=VARCHAR},
+      </if>
+      <if test="clientName != null">
+        client_name = #{clientName,jdbcType=VARCHAR},
+      </if>
+      <if test="clientSecret != null">
+        client_secret = #{clientSecret,jdbcType=VARCHAR},
+      </if>
+      <if test="redirectUri != null">
+        redirect_uri = #{redirectUri,jdbcType=VARCHAR},
+      </if>
+      <if test="description != null">
+        description = #{description,jdbcType=VARCHAR},
+      </if>
+      <if test="createUser != null">
+        create_user = #{createUser,jdbcType=INTEGER},
+      </if>
+      <if test="createTime != null">
+        create_time = #{createTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="updateUser != null">
+        update_user = #{updateUser,jdbcType=INTEGER},
+      </if>
+      <if test="updateTime != null">
+        update_time = #{updateTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="status != null">
+        status = #{status,jdbcType=INTEGER},
+      </if>
+    </set>
+    where id = #{id,jdbcType=INTEGER}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.zkqy.system.domain.sso.AuthClientDetails">
+    update auth_client_details
+    set client_id = #{clientId,jdbcType=VARCHAR},
+      client_name = #{clientName,jdbcType=VARCHAR},
+      client_secret = #{clientSecret,jdbcType=VARCHAR},
+      redirect_uri = #{redirectUri,jdbcType=VARCHAR},
+      description = #{description,jdbcType=VARCHAR},
+      create_user = #{createUser,jdbcType=INTEGER},
+      create_time = #{createTime,jdbcType=TIMESTAMP},
+      update_user = #{updateUser,jdbcType=INTEGER},
+      update_time = #{updateTime,jdbcType=TIMESTAMP},
+      status = #{status,jdbcType=INTEGER}
+    where id = #{id,jdbcType=INTEGER}
+  </update>
+  <select id="selectByClientId" parameterType="java.lang.String" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from auth_client_details
+    where client_id = #{clientId,jdbcType=VARCHAR}
+  </select>
+</mapper>

+ 90 - 0
zkqy-system/src/main/resources/mapper/system/sso/AuthClientUserMapper.xml

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zkqy.system.mapper.sso.AuthClientUserMapper">
+  <resultMap id="BaseResultMap" type="com.zkqy.system.domain.sso.AuthClientUser">
+    <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="auth_client_id" jdbcType="INTEGER" property="authClientId" />
+    <result column="user_id" jdbcType="INTEGER" property="userId" />
+    <result column="auth_scope_id" jdbcType="INTEGER" property="authScopeId" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    id, auth_client_id, user_id, auth_scope_id
+  </sql>
+  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from auth_client_user
+    where id = #{id,jdbcType=INTEGER}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
+    delete from auth_client_user
+    where id = #{id,jdbcType=INTEGER}
+  </delete>
+  <insert id="insert" parameterType="com.zkqy.system.domain.sso.AuthClientUser">
+    insert into auth_client_user (id, auth_client_id, user_id,
+      auth_scope_id)
+    values (#{id,jdbcType=INTEGER}, #{authClientId,jdbcType=INTEGER}, #{userId,jdbcType=INTEGER},
+      #{authScopeId,jdbcType=INTEGER})
+  </insert>
+  <insert id="insertSelective" parameterType="com.zkqy.system.domain.sso.AuthClientUser">
+    insert into auth_client_user
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id,
+      </if>
+      <if test="authClientId != null">
+        auth_client_id,
+      </if>
+      <if test="userId != null">
+        user_id,
+      </if>
+      <if test="authScopeId != null">
+        auth_scope_id,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=INTEGER},
+      </if>
+      <if test="authClientId != null">
+        #{authClientId,jdbcType=INTEGER},
+      </if>
+      <if test="userId != null">
+        #{userId,jdbcType=INTEGER},
+      </if>
+      <if test="authScopeId != null">
+        #{authScopeId,jdbcType=INTEGER},
+      </if>
+    </trim>
+  </insert>
+  <update id="updateByPrimaryKeySelective" parameterType="com.zkqy.system.domain.sso.AuthClientUser">
+    update auth_client_user
+    <set>
+      <if test="authClientId != null">
+        auth_client_id = #{authClientId,jdbcType=INTEGER},
+      </if>
+      <if test="userId != null">
+        user_id = #{userId,jdbcType=INTEGER},
+      </if>
+      <if test="authScopeId != null">
+        auth_scope_id = #{authScopeId,jdbcType=INTEGER},
+      </if>
+    </set>
+    where id = #{id,jdbcType=INTEGER}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.zkqy.system.domain.sso.AuthClientUser">
+    update auth_client_user
+    set auth_client_id = #{authClientId,jdbcType=INTEGER},
+      user_id = #{userId,jdbcType=INTEGER},
+      auth_scope_id = #{authScopeId,jdbcType=INTEGER}
+    where id = #{id,jdbcType=INTEGER}
+  </update>
+  <select id="selectByClientId" parameterType="java.util.Map" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from auth_client_user
+    where auth_client_id = #{clientId,jdbcType=INTEGER}
+      AND auth_scope_id = #{scopeId,jdbcType=INTEGER}
+    AND user_id = #{userId,jdbcType=INTEGER}
+  </select>
+</mapper>

+ 147 - 0
zkqy-system/src/main/resources/mapper/system/sso/AuthRefreshTokenMapper.xml

@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zkqy.system.mapper.sso.AuthRefreshTokenMapper">
+    <resultMap id="BaseResultMap" type="com.zkqy.system.domain.sso.AuthRefreshToken">
+        <id column="id" jdbcType="INTEGER" property="id"/>
+        <result column="token_id" jdbcType="INTEGER" property="tokenId"/>
+        <result column="refresh_token" jdbcType="VARCHAR" property="refreshToken"/>
+        <result column="expires_in" jdbcType="BIGINT" property="expiresIn"/>
+        <result column="create_user" jdbcType="INTEGER" property="createUser"/>
+        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
+        <result column="update_user" jdbcType="INTEGER" property="updateUser"/>
+        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
+    </resultMap>
+    <sql id="Base_Column_List">
+        id
+        , token_id, refresh_token, expires_in, create_user, create_time, update_user, update_time
+    </sql>
+    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from auth_refresh_token
+        where id = #{id,jdbcType=INTEGER}
+    </select>
+    <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
+        delete
+        from auth_refresh_token
+        where id = #{id,jdbcType=INTEGER}
+    </delete>
+    <insert id="insert" parameterType="com.zkqy.system.domain.sso.AuthRefreshToken">
+        insert into auth_refresh_token (id, token_id, refresh_token,
+                                        expires_in, create_user, create_time,
+                                        update_user, update_time)
+        values (#{id,jdbcType=INTEGER}, #{tokenId,jdbcType=INTEGER}, #{refreshToken,jdbcType=VARCHAR},
+                #{expiresIn,jdbcType=BIGINT}, #{createUser,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP},
+                #{updateUser,jdbcType=INTEGER}, #{updateTime,jdbcType=TIMESTAMP})
+    </insert>
+    <insert id="insertSelective" parameterType="com.zkqy.system.domain.sso.AuthRefreshToken">
+        insert into auth_refresh_token
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">
+                id,
+            </if>
+            <if test="tokenId != null">
+                token_id,
+            </if>
+            <if test="refreshToken != null">
+                refresh_token,
+            </if>
+            <if test="expiresIn != null">
+                expires_in,
+            </if>
+            <if test="createUser != null">
+                create_user,
+            </if>
+            <if test="createTime != null">
+                create_time,
+            </if>
+            <if test="updateUser != null">
+                update_user,
+            </if>
+            <if test="updateTime != null">
+                update_time,
+            </if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">
+                #{id,jdbcType=INTEGER},
+            </if>
+            <if test="tokenId != null">
+                #{tokenId,jdbcType=INTEGER},
+            </if>
+            <if test="refreshToken != null">
+                #{refreshToken,jdbcType=VARCHAR},
+            </if>
+            <if test="expiresIn != null">
+                #{expiresIn,jdbcType=BIGINT},
+            </if>
+            <if test="createUser != null">
+                #{createUser,jdbcType=INTEGER},
+            </if>
+            <if test="createTime != null">
+                #{createTime,jdbcType=TIMESTAMP},
+            </if>
+            <if test="updateUser != null">
+                #{updateUser,jdbcType=INTEGER},
+            </if>
+            <if test="updateTime != null">
+                #{updateTime,jdbcType=TIMESTAMP},
+            </if>
+        </trim>
+    </insert>
+    <update id="updateByPrimaryKeySelective" parameterType="com.zkqy.system.domain.sso.AuthRefreshToken">
+        update auth_refresh_token
+        <set>
+            <if test="tokenId != null">
+                token_id = #{tokenId,jdbcType=INTEGER},
+            </if>
+            <if test="refreshToken != null">
+                refresh_token = #{refreshToken,jdbcType=VARCHAR},
+            </if>
+            <if test="expiresIn != null">
+                expires_in = #{expiresIn,jdbcType=BIGINT},
+            </if>
+            <if test="createUser != null">
+                create_user = #{createUser,jdbcType=INTEGER},
+            </if>
+            <if test="createTime != null">
+                create_time = #{createTime,jdbcType=TIMESTAMP},
+            </if>
+            <if test="updateUser != null">
+                update_user = #{updateUser,jdbcType=INTEGER},
+            </if>
+            <if test="updateTime != null">
+                update_time = #{updateTime,jdbcType=TIMESTAMP},
+            </if>
+        </set>
+        where id = #{id,jdbcType=INTEGER}
+    </update>
+    <update id="updateByPrimaryKey" parameterType="com.zkqy.system.domain.sso.AuthRefreshToken">
+        update auth_refresh_token
+        set token_id      = #{tokenId,jdbcType=INTEGER},
+            refresh_token = #{refreshToken,jdbcType=VARCHAR},
+            expires_in    = #{expiresIn,jdbcType=BIGINT},
+            create_user   = #{createUser,jdbcType=INTEGER},
+            create_time   = #{createTime,jdbcType=TIMESTAMP},
+            update_user   = #{updateUser,jdbcType=INTEGER},
+            update_time   = #{updateTime,jdbcType=TIMESTAMP}
+        where id = #{id,jdbcType=INTEGER}
+    </update>
+    <select id="selectByTokenId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from auth_refresh_token
+        where token_id = #{tokenId,jdbcType=INTEGER}
+    </select>
+    <select id="selectByRefreshToken" parameterType="java.lang.String" resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from auth_refresh_token
+        where refresh_token = #{refreshToken,jdbcType=VARCHAR}
+    </select>
+    <delete id="delTokenByUserId" parameterType="java.lang.Long">
+        delete
+        from auth_refresh_token
+        where create_user = #{userId,jdbcType=INTEGER}
+    </delete>
+</mapper>

+ 77 - 0
zkqy-system/src/main/resources/mapper/system/sso/AuthScopeMapper.xml

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zkqy.system.mapper.sso.AuthScopeMapper">
+  <resultMap id="BaseResultMap" type="com.zkqy.system.domain.sso.AuthScope">
+    <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="scope_name" jdbcType="VARCHAR" property="scopeName" />
+    <result column="description" jdbcType="VARCHAR" property="description" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    id, scope_name, description
+  </sql>
+  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from auth_scope
+    where id = #{id,jdbcType=INTEGER}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
+    delete from auth_scope
+    where id = #{id,jdbcType=INTEGER}
+  </delete>
+  <insert id="insert" parameterType="com.zkqy.system.domain.sso.AuthScope">
+    insert into auth_scope (id, scope_name, description
+      )
+    values (#{id,jdbcType=INTEGER}, #{scopeName,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}
+      )
+  </insert>
+  <insert id="insertSelective" parameterType="com.zkqy.system.domain.sso.AuthScope">
+    insert into auth_scope
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id,
+      </if>
+      <if test="scopeName != null">
+        scope_name,
+      </if>
+      <if test="description != null">
+        description,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=INTEGER},
+      </if>
+      <if test="scopeName != null">
+        #{scopeName,jdbcType=VARCHAR},
+      </if>
+      <if test="description != null">
+        #{description,jdbcType=VARCHAR},
+      </if>
+    </trim>
+  </insert>
+  <update id="updateByPrimaryKeySelective" parameterType="com.zkqy.system.domain.sso.AuthScope">
+    update auth_scope
+    <set>
+      <if test="scopeName != null">
+        scope_name = #{scopeName,jdbcType=VARCHAR},
+      </if>
+      <if test="description != null">
+        description = #{description,jdbcType=VARCHAR},
+      </if>
+    </set>
+    where id = #{id,jdbcType=INTEGER}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.zkqy.system.domain.sso.AuthScope">
+    update auth_scope
+    set scope_name = #{scopeName,jdbcType=VARCHAR},
+      description = #{description,jdbcType=VARCHAR}
+    where id = #{id,jdbcType=INTEGER}
+  </update>
+  <select id="selectByScopeName" parameterType="java.lang.String" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from auth_scope
+    where scope_name = #{scopeName,jdbcType=VARCHAR}
+  </select>
+</mapper>

+ 189 - 0
zkqy-system/src/main/resources/mapper/system/sso/SsoAccessTokenMapper.xml

@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zkqy.system.mapper.sso.SsoAccessTokenMapper">
+  <resultMap id="BaseResultMap" type="com.zkqy.system.domain.sso.SsoAccessToken">
+    <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="access_token" jdbcType="VARCHAR" property="accessToken" />
+    <result column="user_id" jdbcType="INTEGER" property="userId" />
+    <result column="user_name" jdbcType="VARCHAR" property="userName" />
+    <result column="ip" jdbcType="VARCHAR" property="ip" />
+    <result column="client_id" jdbcType="INTEGER" property="clientId" />
+    <result column="channel" jdbcType="VARCHAR" property="channel" />
+    <result column="expires_in" jdbcType="BIGINT" property="expiresIn" />
+    <result column="create_user" jdbcType="INTEGER" property="createUser" />
+    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
+    <result column="update_user" jdbcType="INTEGER" property="updateUser" />
+    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    id, access_token, user_id, user_name, ip, client_id, channel, expires_in, create_user,
+    create_time, update_user, update_time
+  </sql>
+  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from sso_access_token
+    where id = #{id,jdbcType=INTEGER}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
+    delete from sso_access_token
+    where id = #{id,jdbcType=INTEGER}
+  </delete>
+  <insert id="insert" parameterType="com.zkqy.system.domain.sso.SsoAccessToken">
+    insert into sso_access_token (id, access_token, user_id,
+      user_name, ip, client_id,
+      channel, expires_in, create_user,
+      create_time, update_user, update_time
+      )
+    values (#{id,jdbcType=INTEGER}, #{accessToken,jdbcType=VARCHAR}, #{userId,jdbcType=INTEGER},
+      #{userName,jdbcType=VARCHAR}, #{ip,jdbcType=VARCHAR}, #{clientId,jdbcType=INTEGER},
+      #{channel,jdbcType=VARCHAR}, #{expiresIn,jdbcType=BIGINT}, #{createUser,jdbcType=INTEGER},
+      #{createTime,jdbcType=TIMESTAMP}, #{updateUser,jdbcType=INTEGER}, #{updateTime,jdbcType=TIMESTAMP}
+      )
+  </insert>
+  <insert id="insertSelective" parameterType="com.zkqy.system.domain.sso.SsoAccessToken">
+    insert into sso_access_token
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id,
+      </if>
+      <if test="accessToken != null">
+        access_token,
+      </if>
+      <if test="userId != null">
+        user_id,
+      </if>
+      <if test="userName != null">
+        user_name,
+      </if>
+      <if test="ip != null">
+        ip,
+      </if>
+      <if test="clientId != null">
+        client_id,
+      </if>
+      <if test="channel != null">
+        channel,
+      </if>
+      <if test="expiresIn != null">
+        expires_in,
+      </if>
+      <if test="createUser != null">
+        create_user,
+      </if>
+      <if test="createTime != null">
+        create_time,
+      </if>
+      <if test="updateUser != null">
+        update_user,
+      </if>
+      <if test="updateTime != null">
+        update_time,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=INTEGER},
+      </if>
+      <if test="accessToken != null">
+        #{accessToken,jdbcType=VARCHAR},
+      </if>
+      <if test="userId != null">
+        #{userId,jdbcType=INTEGER},
+      </if>
+      <if test="userName != null">
+        #{userName,jdbcType=VARCHAR},
+      </if>
+      <if test="ip != null">
+        #{ip,jdbcType=VARCHAR},
+      </if>
+      <if test="clientId != null">
+        #{clientId,jdbcType=INTEGER},
+      </if>
+      <if test="channel != null">
+        #{channel,jdbcType=VARCHAR},
+      </if>
+      <if test="expiresIn != null">
+        #{expiresIn,jdbcType=BIGINT},
+      </if>
+      <if test="createUser != null">
+        #{createUser,jdbcType=INTEGER},
+      </if>
+      <if test="createTime != null">
+        #{createTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="updateUser != null">
+        #{updateUser,jdbcType=INTEGER},
+      </if>
+      <if test="updateTime != null">
+        #{updateTime,jdbcType=TIMESTAMP},
+      </if>
+    </trim>
+  </insert>
+  <update id="updateByPrimaryKeySelective" parameterType="com.zkqy.system.domain.sso.SsoAccessToken">
+    update sso_access_token
+    <set>
+      <if test="accessToken != null">
+        access_token = #{accessToken,jdbcType=VARCHAR},
+      </if>
+      <if test="userId != null">
+        user_id = #{userId,jdbcType=INTEGER},
+      </if>
+      <if test="userName != null">
+        user_name = #{userName,jdbcType=VARCHAR},
+      </if>
+      <if test="ip != null">
+        ip = #{ip,jdbcType=VARCHAR},
+      </if>
+      <if test="clientId != null">
+        client_id = #{clientId,jdbcType=INTEGER},
+      </if>
+      <if test="channel != null">
+        channel = #{channel,jdbcType=VARCHAR},
+      </if>
+      <if test="expiresIn != null">
+        expires_in = #{expiresIn,jdbcType=BIGINT},
+      </if>
+      <if test="createUser != null">
+        create_user = #{createUser,jdbcType=INTEGER},
+      </if>
+      <if test="createTime != null">
+        create_time = #{createTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="updateUser != null">
+        update_user = #{updateUser,jdbcType=INTEGER},
+      </if>
+      <if test="updateTime != null">
+        update_time = #{updateTime,jdbcType=TIMESTAMP},
+      </if>
+    </set>
+    where id = #{id,jdbcType=INTEGER}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.zkqy.system.domain.sso.SsoAccessToken">
+    update sso_access_token
+    set access_token = #{accessToken,jdbcType=VARCHAR},
+      user_id = #{userId,jdbcType=INTEGER},
+      user_name = #{userName,jdbcType=VARCHAR},
+      ip = #{ip,jdbcType=VARCHAR},
+      client_id = #{clientId,jdbcType=INTEGER},
+      channel = #{channel,jdbcType=VARCHAR},
+      expires_in = #{expiresIn,jdbcType=BIGINT},
+      create_user = #{createUser,jdbcType=INTEGER},
+      create_time = #{createTime,jdbcType=TIMESTAMP},
+      update_user = #{updateUser,jdbcType=INTEGER},
+      update_time = #{updateTime,jdbcType=TIMESTAMP}
+    where id = #{id,jdbcType=INTEGER}
+  </update>
+  <select id="selectByUserIdAndClientId" parameterType="java.util.Map" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from sso_access_token
+    where user_id = #{userId,jdbcType=INTEGER} AND client_id = #{clientId,jdbcType=INTEGER}
+  </select>
+  <select id="selectByAccessToken" parameterType="java.lang.String" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from sso_access_token
+    where access_token = #{accessToken,jdbcType=VARCHAR}
+  </select>
+</mapper>

+ 112 - 0
zkqy-system/src/main/resources/mapper/system/sso/SsoClientDetailsMapper.xml

@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zkqy.system.mapper.sso.SsoClientDetailsMapper">
+  <resultMap id="BaseResultMap" type="com.zkqy.system.domain.sso.SsoClientDetails">
+    <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="client_name" jdbcType="VARCHAR" property="clientName" />
+    <result column="description" jdbcType="VARCHAR" property="description" />
+    <result column="redirect_url" jdbcType="VARCHAR" property="redirectUrl" />
+    <result column="logout_url" jdbcType="VARCHAR" property="logoutUrl" />
+    <result column="status" jdbcType="INTEGER" property="status" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    id, client_name, description, redirect_url, logout_url, status
+  </sql>
+  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from sso_client_details
+    where id = #{id,jdbcType=INTEGER}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
+    delete from sso_client_details
+    where id = #{id,jdbcType=INTEGER}
+  </delete>
+  <insert id="insert" parameterType="com.zkqy.system.domain.sso.SsoClientDetails">
+    insert into sso_client_details (id, client_name, description,
+      redirect_url, logout_url, status
+      )
+    values (#{id,jdbcType=INTEGER}, #{clientName,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR},
+      #{redirectUrl,jdbcType=VARCHAR}, #{logoutUrl,jdbcType=VARCHAR}, #{status,jdbcType=INTEGER}
+      )
+  </insert>
+  <insert id="insertSelective" parameterType="com.zkqy.system.domain.sso.SsoClientDetails">
+    insert into sso_client_details
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id,
+      </if>
+      <if test="clientName != null">
+        client_name,
+      </if>
+      <if test="description != null">
+        description,
+      </if>
+      <if test="redirectUrl != null">
+        redirect_url,
+      </if>
+      <if test="logoutUrl != null">
+        logout_url,
+      </if>
+      <if test="status != null">
+        status,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=INTEGER},
+      </if>
+      <if test="clientName != null">
+        #{clientName,jdbcType=VARCHAR},
+      </if>
+      <if test="description != null">
+        #{description,jdbcType=VARCHAR},
+      </if>
+      <if test="redirectUrl != null">
+        #{redirectUrl,jdbcType=VARCHAR},
+      </if>
+      <if test="logoutUrl != null">
+        #{logoutUrl,jdbcType=VARCHAR},
+      </if>
+      <if test="status != null">
+        #{status,jdbcType=INTEGER},
+      </if>
+    </trim>
+  </insert>
+  <update id="updateByPrimaryKeySelective" parameterType="com.zkqy.system.domain.sso.SsoClientDetails">
+    update sso_client_details
+    <set>
+      <if test="clientName != null">
+        client_name = #{clientName,jdbcType=VARCHAR},
+      </if>
+      <if test="description != null">
+        description = #{description,jdbcType=VARCHAR},
+      </if>
+      <if test="redirectUrl != null">
+        redirect_url = #{redirectUrl,jdbcType=VARCHAR},
+      </if>
+      <if test="logoutUrl != null">
+        logout_url = #{logoutUrl,jdbcType=VARCHAR},
+      </if>
+      <if test="status != null">
+        status = #{status,jdbcType=INTEGER},
+      </if>
+    </set>
+    where id = #{id,jdbcType=INTEGER}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.zkqy.system.domain.sso.SsoClientDetails">
+    update sso_client_details
+    set client_name = #{clientName,jdbcType=VARCHAR},
+      description = #{description,jdbcType=VARCHAR},
+      redirect_url = #{redirectUrl,jdbcType=VARCHAR},
+      logout_url = #{logoutUrl,jdbcType=VARCHAR},
+      status = #{status,jdbcType=INTEGER}
+    where id = #{id,jdbcType=INTEGER}
+  </update>
+  <select id="selectByRedirectUrl" parameterType="java.lang.String" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from sso_client_details
+    where redirect_url = #{redirectUrl,jdbcType=VARCHAR}
+  </select>
+</mapper>

+ 140 - 0
zkqy-system/src/main/resources/mapper/system/sso/SsoRefreshTokenMapper.xml

@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zkqy.system.mapper.sso.SsoRefreshTokenMapper">
+  <resultMap id="BaseResultMap" type="com.zkqy.system.domain.sso.SsoRefreshToken">
+    <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="token_id" jdbcType="INTEGER" property="tokenId" />
+    <result column="refresh_token" jdbcType="VARCHAR" property="refreshToken" />
+    <result column="expires_in" jdbcType="BIGINT" property="expiresIn" />
+    <result column="create_user" jdbcType="INTEGER" property="createUser" />
+    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
+    <result column="update_user" jdbcType="INTEGER" property="updateUser" />
+    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    id, token_id, refresh_token, expires_in, create_user, create_time, update_user, update_time
+  </sql>
+  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from sso_refresh_token
+    where id = #{id,jdbcType=INTEGER}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
+    delete from sso_refresh_token
+    where id = #{id,jdbcType=INTEGER}
+  </delete>
+  <insert id="insert" parameterType="com.zkqy.system.domain.sso.SsoRefreshToken">
+    insert into sso_refresh_token (id, token_id, refresh_token,
+      expires_in, create_user, create_time,
+      update_user, update_time)
+    values (#{id,jdbcType=INTEGER}, #{tokenId,jdbcType=INTEGER}, #{refreshToken,jdbcType=VARCHAR},
+      #{expiresIn,jdbcType=BIGINT}, #{createUser,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP},
+      #{updateUser,jdbcType=INTEGER}, #{updateTime,jdbcType=TIMESTAMP})
+  </insert>
+  <insert id="insertSelective" parameterType="com.zkqy.system.domain.sso.SsoRefreshToken">
+    insert into sso_refresh_token
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id,
+      </if>
+      <if test="tokenId != null">
+        token_id,
+      </if>
+      <if test="refreshToken != null">
+        refresh_token,
+      </if>
+      <if test="expiresIn != null">
+        expires_in,
+      </if>
+      <if test="createUser != null">
+        create_user,
+      </if>
+      <if test="createTime != null">
+        create_time,
+      </if>
+      <if test="updateUser != null">
+        update_user,
+      </if>
+      <if test="updateTime != null">
+        update_time,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=INTEGER},
+      </if>
+      <if test="tokenId != null">
+        #{tokenId,jdbcType=INTEGER},
+      </if>
+      <if test="refreshToken != null">
+        #{refreshToken,jdbcType=VARCHAR},
+      </if>
+      <if test="expiresIn != null">
+        #{expiresIn,jdbcType=BIGINT},
+      </if>
+      <if test="createUser != null">
+        #{createUser,jdbcType=INTEGER},
+      </if>
+      <if test="createTime != null">
+        #{createTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="updateUser != null">
+        #{updateUser,jdbcType=INTEGER},
+      </if>
+      <if test="updateTime != null">
+        #{updateTime,jdbcType=TIMESTAMP},
+      </if>
+    </trim>
+  </insert>
+  <update id="updateByPrimaryKeySelective" parameterType="com.zkqy.system.domain.sso.SsoRefreshToken">
+    update sso_refresh_token
+    <set>
+      <if test="tokenId != null">
+        token_id = #{tokenId,jdbcType=INTEGER},
+      </if>
+      <if test="refreshToken != null">
+        refresh_token = #{refreshToken,jdbcType=VARCHAR},
+      </if>
+      <if test="expiresIn != null">
+        expires_in = #{expiresIn,jdbcType=BIGINT},
+      </if>
+      <if test="createUser != null">
+        create_user = #{createUser,jdbcType=INTEGER},
+      </if>
+      <if test="createTime != null">
+        create_time = #{createTime,jdbcType=TIMESTAMP},
+      </if>
+      <if test="updateUser != null">
+        update_user = #{updateUser,jdbcType=INTEGER},
+      </if>
+      <if test="updateTime != null">
+        update_time = #{updateTime,jdbcType=TIMESTAMP},
+      </if>
+    </set>
+    where id = #{id,jdbcType=INTEGER}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.zkqy.system.domain.sso.SsoRefreshToken">
+    update sso_refresh_token
+    set token_id = #{tokenId,jdbcType=INTEGER},
+      refresh_token = #{refreshToken,jdbcType=VARCHAR},
+      expires_in = #{expiresIn,jdbcType=BIGINT},
+      create_user = #{createUser,jdbcType=INTEGER},
+      create_time = #{createTime,jdbcType=TIMESTAMP},
+      update_user = #{updateUser,jdbcType=INTEGER},
+      update_time = #{updateTime,jdbcType=TIMESTAMP}
+    where id = #{id,jdbcType=INTEGER}
+  </update>
+  <select id="selectByTokenId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
+    select
+    <include refid="Base_Column_List" />
+    from sso_refresh_token
+    where token_id = #{tokenId,jdbcType=INTEGER}
+  </select>
+    <select id="selectByRefreshToken" parameterType="java.lang.String" resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List" />
+        from sso_refresh_token
+        where refresh_token = #{refreshToken,jdbcType=VARCHAR}
+    </select>
+</mapper>

+ 36 - 0
zkqy-ui/src/api/login.js

@@ -81,3 +81,39 @@ export function refreshToken() {
     method: 'get'
   })
 }
+
+// 单点登录方法
+export function ssoLogin(data) {
+  return request({
+    url: `/oauth2/authorize` + data.uri,
+    headers: {
+      isToken: false
+    },
+    method: 'post',
+    params: data
+  })
+}
+
+// 单点登录
+export function loginBySso(data) {
+  return request({
+    url: `/Oauth2Login/${data}`,
+    headers: {
+      isToken: false
+    },
+    method: 'get',
+    // params: data
+  })
+}
+
+// 调用后端权限校验接口
+export function authorize(data) {
+  return request({
+    url: `/authorize`,
+    headers: {
+      isToken: false
+    },
+    method: 'get',
+  })
+}
+

+ 33 - 7
zkqy-ui/src/layout/components/Sidebar/SidebarItem.vue

@@ -1,16 +1,17 @@
 <template>
   <div v-if="!item.hidden">
-    <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
+    <template
+      v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
       <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
         <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
-          <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
+          <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title"/>
         </el-menu-item>
       </app-link>
     </template>
 
     <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
       <template slot="title">
-        <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
+        <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title"/>
       </template>
       <sidebar-item
         v-for="child in item.children"
@@ -26,14 +27,15 @@
 
 <script>
 import path from 'path'
-import { isExternal } from '@/utils/validate'
+import {isExternal} from '@/utils/validate'
 import Item from './Item'
 import AppLink from './Link'
 import FixiOSBug from './FixiOSBug'
+import {mapState} from "vuex";
 
 export default {
   name: 'SidebarItem',
-  components: { Item, AppLink },
+  components: {Item, AppLink},
   mixins: [FixiOSBug],
   props: {
     // route object
@@ -54,6 +56,11 @@ export default {
     this.onlyOneChild = null
     return {}
   },
+  computed: {
+    ...mapState({
+      userInfo: (state) => state.user,
+    }),
+  },
   methods: {
     hasOneShowingChild(children = [], parent) {
       if (!children) {
@@ -76,22 +83,41 @@ export default {
 
       // Show parent if there are no child router to display
       if (showingChildren.length === 0) {
-        this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }
+        this.onlyOneChild = {...parent, path: '', noShowingChildren: true}
         return true
       }
 
       return false
     },
     resolvePath(routePath, routeQuery) {
+
       if (isExternal(routePath)) {
         return routePath
       }
       if (isExternal(this.basePath)) {
+        if (routeQuery) {
+          let query = JSON.parse(routeQuery);
+          if (query.key) {
+            // 设置code
+            query.key = window.localStorage.getItem("setoauthUUID" + this.userInfo.name);
+            query.tenantCode = this.userInfo.tenant.tenantCode
+            let baseURL = this.basePath
+            Object.keys(query).forEach((key, index) => {
+              if (index == 0) {
+                baseURL += '?' + key + '=' + query[key];
+              } else {
+                baseURL += '&' + key + '=' + query[key];
+              }
+            })
+            return baseURL;
+          }
+        }
         return this.basePath
       }
+
       if (routeQuery) {
         let query = JSON.parse(routeQuery);
-        return { path: path.resolve(this.basePath, routePath), query: query }
+        return {path: path.resolve(this.basePath, routePath), query: query}
       }
       return path.resolve(this.basePath, routePath)
     }

+ 1 - 1
zkqy-ui/src/permission.js

@@ -8,7 +8,7 @@ import {isRelogin} from '@/utils/request'
 
 NProgress.configure({showSpinner: false})
 
-const whiteList = ['/login', '/register', '/404', '/401']
+const whiteList = ['/login', '/register', '/404', '/401','/redirectAuth']
 
 router.beforeEach((to, from, next) => {
   NProgress.start()

+ 5 - 0
zkqy-ui/src/router/index.js

@@ -224,6 +224,11 @@ export const constantRoutes = [
     component: () => import('@/views/error/401'),
     hidden: true
   },
+  {
+    path: '/redirectAuth',
+    component: () => import('@/views/loading'),
+    hidden: true
+  },
   {
     path: '',
     component: Layout,

+ 19 - 1
zkqy-ui/src/store/modules/user.js

@@ -13,6 +13,7 @@ const user = {
     tenant: {},
     dataSource: {},
     nickName: "",
+    oauthUUID: ''
   },
 
   mutations: {
@@ -21,6 +22,8 @@ const user = {
     },
     SET_NAME: (state, name) => {
       state.name = name
+    }, SET_OAUTHUUID: (state, oauthUUID) => {
+      state.oauthUUID = oauthUUID
     },
     SET_NICKNAME: (state, nickName) => {
       state.nickName = nickName
@@ -46,6 +49,22 @@ const user = {
   },
 
   actions: {
+
+    setoauthUUID({commit}, data) {
+      console.log("复制oauthUUID", data)
+      commit('SET_OAUTHUUID', data)
+    },
+
+    // 单点登录
+    LoginBySso({commit}, data) {
+      let {username, token} = data;
+      return new Promise((resolve, reject) => {
+        commit('SET_NAME', username)
+        setToken(token)
+        commit('SET_TOKEN', token)
+        resolve();
+      })
+    },
     // 登录
     Login({commit}, userInfo) {
       const username = userInfo.username.trim()
@@ -102,7 +121,6 @@ const user = {
           if (res.tenant != null) {
             commit('SET_TENANT', res.tenant)
             commit('SET_DATASOURCE', res.dataSource)
-
           }
           commit('SET_NAME', user.userName)
           commit('SET_AVATAR', avatar)

+ 348 - 0
zkqy-ui/src/views/loading.vue

@@ -0,0 +1,348 @@
+<template>
+  <div id="app">
+    <div id="loader-wrapper">
+      <div id="loader"></div>
+      <div class="loader-section section-left"></div>
+      <div class="loader-section section-right"></div>
+      <div class="load_title">正在加载系统资源,请耐心等待</div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {loginBySso, authorize, isTenantExist} from "@/api/login";
+import {Base64} from "js-base64";
+
+export default {
+  name: "loading",
+  props: [],
+  components: {},
+  data() {
+    return {
+      token: "",
+      tenantId: null,
+    };
+  },
+  computed: {},
+  methods: {
+    // 存储配置信息
+    setConfig(config) {
+      let {
+        loginPageTitle,
+        loginPageDescription,
+        loginPageLogo,
+        loginPageBackgroundImage,
+        windowTitle,
+        windowLogo,
+      } = config;
+      if (loginPageTitle) {
+        window.sessionStorage.setItem("title", loginPageTitle);
+      } else {
+        window.sessionStorage.removeItem("title");
+      }
+      if (loginPageLogo) {
+        window.sessionStorage.setItem("logo", loginPageLogo);
+      } else {
+        window.sessionStorage.removeItem("logo");
+      }
+    },
+
+    // 获取配置信息
+    async getConfig(tenantCode) {
+      if (tenantCode != null) {
+        // 得到tenantId 查询裤中是否存在该租户
+        let res = await isTenantExist({tenantCode: tenantCode});
+        console.log("isTenantExist", res.data.tenantId);
+        if (res == undefined) {
+
+          this.$router.push({path: "/401"});
+        } else if (res?.data?.tenantId) {
+          this.tenantId = res.data.tenantId
+
+          // 判断当前编号是否存在库中
+          // this.tenantId = res.data.tenantId;
+          // this.loginForm.tenantID = this.tenantId;
+          if (res?.data?.loginPageConfiguration) {
+            this.setConfig(res.data.loginPageConfiguration || {});
+          } else {
+            window.sessionStorage.removeItem("title");
+            window.sessionStorage.removeItem("logo");
+          }
+        } else {
+          console.log("租户有问题!");
+          // 当前访问链接中的租户编号不存在
+          this.$router.push({path: "/401"});
+        }
+      } else {
+        this.$router.push({path: "/401"});
+      }
+    },
+
+    // 校验时间是否过时
+    validateTime(timeStamp) {
+      if (!timeStamp) return false;
+      let nowTime = parseInt(new Date().getTime() / 1000);
+      let limit = 120;
+      timeStamp = parseInt(timeStamp);
+      return nowTime - timeStamp < limit;
+    },
+    async initUserInfo() {
+      let {bWVz} = this.$route.query;
+      if (bWVz) {
+        let val = Base64.decode(bWVz);
+        let loginData = Base64.decode(bWVz).split("^_^");
+        let tenantCode = loginData[0];
+        let username = loginData[1];
+        let timeStamp = loginData[2];
+        let isOutTime = this.validateTime(timeStamp);
+        if (isOutTime) {
+          try {
+            await this.getConfig(tenantCode);
+            let parts = val.split("^_^");
+            parts[parts.length - 1] = this.tenantId;
+            let newStr = parts.join("^_^");
+            // 登录
+            let res = await loginBySso(Base64.encode(newStr));
+            if (res.code == 200) {
+              // 存储token 用户信息
+              this.$store
+                .dispatch("LoginBySso", {
+                  username: username,
+                  token: res.token,
+                })
+                .then(() => {
+                  this.$router
+                    .push({path: this.redirect || "/"})
+                    .catch(() => {
+                    });
+                });
+            } else {
+              this.$message.error("网络异常,请重新跳转");
+              this.$router.push("/401");
+            }
+          } catch (error) {
+            this.$message.error("网络异常,请重新跳转");
+            this.$router.push("/401");
+          }
+        } else {
+          this.$message.error("链接已过期,请重新跳转");
+          this.$router.push("/401");
+        }
+      } else {
+        this.$message.error("参数异常,请重新跳转");
+        this.$router.push("/401");
+      }
+    },
+    async authorizeHandler() {
+      try {
+        let res = await authorize();
+        console.log(res);
+        if (res.code == 200) {
+          window.location.href = res.data;
+        } else {
+          // this.$router.push("/401");
+        }
+      } catch (error) {
+      }
+    },
+  },
+
+  mounted() {
+    // this.getConfig("kjjt01");
+    // return;
+    if (this.$route.query.bWVz) {
+      this.initUserInfo();
+    }
+    if (this.$route.query.type == "oauth") {
+      this.authorizeHandler();
+    }
+  },
+};
+</script>
+
+<style scoped lang="scss">
+html,
+body,
+#app {
+  height: 100%;
+  margin: 0px;
+  padding: 0px;
+}
+
+.chromeframe {
+  margin: 0.2em 0;
+  background: #ccc;
+  color: #000;
+  padding: 0.2em 0;
+}
+
+#loader-wrapper {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 999999;
+}
+
+#loader {
+  display: block;
+  position: relative;
+  left: 50%;
+  top: 50%;
+  width: 150px;
+  height: 150px;
+  margin: -75px 0 0 -75px;
+  border-radius: 50%;
+  border: 3px solid transparent;
+  border-top-color: #fff;
+  -webkit-animation: spin 2s linear infinite;
+  -ms-animation: spin 2s linear infinite;
+  -moz-animation: spin 2s linear infinite;
+  -o-animation: spin 2s linear infinite;
+  animation: spin 2s linear infinite;
+  z-index: 1001;
+}
+
+#loader:before {
+  content: "";
+  position: absolute;
+  top: 5px;
+  left: 5px;
+  right: 5px;
+  bottom: 5px;
+  border-radius: 50%;
+  border: 3px solid transparent;
+  border-top-color: #fff;
+  -webkit-animation: spin 3s linear infinite;
+  -moz-animation: spin 3s linear infinite;
+  -o-animation: spin 3s linear infinite;
+  -ms-animation: spin 3s linear infinite;
+  animation: spin 3s linear infinite;
+}
+
+#loader:after {
+  content: "";
+  position: absolute;
+  top: 15px;
+  left: 15px;
+  right: 15px;
+  bottom: 15px;
+  border-radius: 50%;
+  border: 3px solid transparent;
+  border-top-color: #fff;
+  -moz-animation: spin 1.5s linear infinite;
+  -o-animation: spin 1.5s linear infinite;
+  -ms-animation: spin 1.5s linear infinite;
+  -webkit-animation: spin 1.5s linear infinite;
+  animation: spin 1.5s linear infinite;
+}
+
+@-webkit-keyframes spin {
+  0% {
+    -webkit-transform: rotate(0deg);
+    -ms-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  100% {
+    -webkit-transform: rotate(360deg);
+    -ms-transform: rotate(360deg);
+    transform: rotate(360deg);
+  }
+}
+
+@keyframes spin {
+  0% {
+    -webkit-transform: rotate(0deg);
+    -ms-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  100% {
+    -webkit-transform: rotate(360deg);
+    -ms-transform: rotate(360deg);
+    transform: rotate(360deg);
+  }
+}
+
+#loader-wrapper .loader-section {
+  position: fixed;
+  top: 0;
+  width: 51%;
+  height: 100%;
+  background: #7171c6;
+  z-index: 1000;
+  -webkit-transform: translateX(0);
+  -ms-transform: translateX(0);
+  transform: translateX(0);
+}
+
+#loader-wrapper .loader-section.section-left {
+  left: 0;
+}
+
+#loader-wrapper .loader-section.section-right {
+  right: 0;
+}
+
+.loaded #loader-wrapper .loader-section.section-left {
+  -webkit-transform: translateX(-100%);
+  -ms-transform: translateX(-100%);
+  transform: translateX(-100%);
+  -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+  transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+}
+
+.loaded #loader-wrapper .loader-section.section-right {
+  -webkit-transform: translateX(100%);
+  -ms-transform: translateX(100%);
+  transform: translateX(100%);
+  -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+  transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+}
+
+.loaded #loader {
+  opacity: 0;
+  -webkit-transition: all 0.3s ease-out;
+  transition: all 0.3s ease-out;
+}
+
+.loaded #loader-wrapper {
+  visibility: hidden;
+  -webkit-transform: translateY(-100%);
+  -ms-transform: translateY(-100%);
+  transform: translateY(-100%);
+  -webkit-transition: all 0.3s 1s ease-out;
+  transition: all 0.3s 1s ease-out;
+}
+
+.no-js #loader-wrapper {
+  display: none;
+}
+
+.no-js h1 {
+  color: #222222;
+}
+
+#loader-wrapper .load_title {
+  font-family: "Open Sans";
+  color: #fff;
+  font-size: 19px;
+  width: 100%;
+  text-align: center;
+  z-index: 9999999999999;
+  position: absolute;
+  top: 60%;
+  opacity: 1;
+  line-height: 30px;
+}
+
+#loader-wrapper .load_title span {
+  font-weight: normal;
+  font-style: italic;
+  font-size: 13px;
+  color: #fff;
+  opacity: 0.5;
+}
+</style>

+ 56 - 32
zkqy-ui/src/views/login.vue

@@ -59,7 +59,7 @@
           </el-col>
           <el-col :span="4" :offset="1">
             <div class="login-code">
-              <img :src="codeUrl" @click="getCode" class="login-code-img" />
+              <img :src="codeUrl" @click="getCode" class="login-code-img"/>
             </div>
           </el-col>
         </el-row>
@@ -70,7 +70,7 @@
           <img :src="codeUrl" @click="getCode" class="login-code-img" />
         </div> -->
         <el-checkbox v-model="loginForm.rememberMe" class="aaa"
-          >在这个设备上记住我
+        >在这个设备上记住我
         </el-checkbox>
       </el-row>
 
@@ -79,7 +79,7 @@
           class="logg"
           :loading="loading"
           @click.native.prevent="handleLogin"
-          >登录
+        >登录
         </el-col>
       </el-row>
     </el-form>
@@ -87,10 +87,10 @@
 </template>
 
 <script>
-import { getCodeImg, isTenantExist } from "@/api/login";
-import { changeDatasource } from "@/api/dataEngine";
+import {getCodeImg, isTenantExist, ssoLogin} from "@/api/login";
+import {changeDatasource} from "@/api/dataEngine";
 import Cookies from "js-cookie";
-import { encrypt, decrypt } from "@/utils/jsencrypt";
+import {encrypt, decrypt} from "@/utils/jsencrypt";
 
 export default {
   name: "Login",
@@ -111,12 +111,12 @@ export default {
       },
       loginRules: {
         username: [
-          { required: true, trigger: "blur", message: "请输入您的账号" },
+          {required: true, trigger: "blur", message: "请输入您的账号"},
         ],
         password: [
-          { required: true, trigger: "blur", message: "请输入您的密码" },
+          {required: true, trigger: "blur", message: "请输入您的密码"},
         ],
-        code: [{ required: true, trigger: "blur", message: "请输入验证码" }],
+        code: [{required: true, trigger: "blur", message: "请输入验证码"}],
       },
       loading: true,
       // 验证码开关
@@ -140,15 +140,18 @@ export default {
     this.validateTenantId();
   },
   methods: {
+    // client_id=x3qwrgrO1wYdz72joZ8YyIuD&scope=basic&response_type=code&state=AB1357&redirect_uri=http://127.0.0.1:8001/login
+
+
     // 校验url
     validateTenantId() {
       let tenantCode = this.$route.query["tenantCode"];
       if (tenantCode != null) {
         // 得到tenantId 查询裤中是否存在该租户
-        isTenantExist({ tenantCode: tenantCode }).then((res) => {
+        isTenantExist({tenantCode: tenantCode}).then((res) => {
           if (res == undefined) {
             this.$message.warning("请访问正确地址!");
-            this.$router.push({ path: "/401" });
+            this.$router.push({path: "/401"});
           } else if (res.data?.tenantId) {
             // 判断当前编号是否存在库中
             this.tenantId = res.data.tenantId;
@@ -162,11 +165,11 @@ export default {
             this.getCookie();
           } else {
             // 当前访问链接中的租户编号不存在
-            this.$router.push({ path: "/401" });
+            this.$router.push({path: "/401"});
           }
         });
       } else {
-        this.$router.push({ path: "/401" });
+        this.$router.push({path: "/401"});
       }
     },
     setConfig() {
@@ -188,9 +191,9 @@ export default {
 
           console.log(
             "" +
-              `url(${
-                process.env.VUE_APP_BASE_IMG_API + loginPageBackgroundImage
-              })`
+            `url(${
+              process.env.VUE_APP_BASE_IMG_API + loginPageBackgroundImage
+            })`
           );
         }
       }
@@ -266,7 +269,7 @@ export default {
         if (valid) {
           this.loading = true;
           if (this.loginForm.rememberMe) {
-            Cookies.set("username", this.loginForm.username, { expires: 30 });
+            Cookies.set("username", this.loginForm.username, {expires: 30});
             Cookies.set("password", encrypt(this.loginForm.password), {
               expires: 30,
             });
@@ -279,21 +282,42 @@ export default {
             Cookies.remove("rememberMe");
           }
           let form = {
+            uri: "?client_id=mestool&scope=basic&response_type=code&state=AB1357&redirect_uri=http://127.0.0.1:8066/oauth/callback",
             ...this.loginForm,
             tenantID: this.tenantId,
           };
-          this.$store
-            .dispatch("Login", form)
-            .then(() => {
-              changeDatasource(); //切换数据源
-              this.$router.push({ path: this.redirect || "/" }).catch(() => {});
-            })
-            .catch(() => {
-              this.loading = false;
-              if (this.captchaEnabled) {
-                this.getCode();
-              }
-            });
+          // 调用单点登录
+          ssoLogin(form).then((response) => {
+            this.loading = false;
+            if (response.code === 200) {
+              // Message({message: response.msg, type: "success"});
+              console.log("认证成功!", response)
+              // response.msg 当前用户唯一标识 跳转其他系统使用
+              // this.$store.dispatch("setoauthUUID", response.msg);
+              window.localStorage.setItem("setoauthUUID" + this.loginForm.username, response.msg);
+              setTimeout(() => {
+                global.window.location.href = response.data;
+              }, 1000);
+            }
+          }).catch(() => {
+            this.loading = false;
+            if (this.captchaEnabled) {
+              this.getCode();
+            }
+          });
+
+          // this.$store
+          //   .dispatch("Login", form)
+          //   .then(() => {
+          //     changeDatasource(); //切换数据源
+          //     this.$router.push({ path: this.redirect || "/" }).catch(() => {});
+          //   })
+          //   .catch(() => {
+          //     this.loading = false;
+          //     if (this.captchaEnabled) {
+          //       this.getCode();
+          //     }
+          //   });
         }
       });
     },
@@ -351,9 +375,9 @@ export default {
   opacity: 1;
   border-radius: 27px;
   background: linear-gradient(
-    135deg,
-    rgba(79, 138, 255, 1) 0%,
-    rgba(75, 94, 255, 1) 100%
+      135deg,
+      rgba(79, 138, 255, 1) 0%,
+      rgba(75, 94, 255, 1) 100%
   );
   box-shadow: 0px 4px 16px rgba(179, 192, 231, 1);
   color: #fff;