浏览代码

pms APP端钉钉登录接口

zhangcl 1 月之前
父节点
当前提交
3f159fcf21

+ 1 - 1
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/IotDeviceController.java

@@ -95,7 +95,7 @@ public class IotDeviceController {
 
     @PostMapping("/saveDeviceStatuses")
     @Operation(summary = "批量调整设备状态")
-    @PreAuthorize("@ss.hasPermission('rq:iot-device:create')")
+    @PreAuthorize("@ss.hasPermission('pms:iot-device-status-log:create')")
     public CommonResult<Long> saveDeviceStatuses(@Valid @RequestBody List<IotDevicePersonRelationSaveReqVO> reqVOS) {
         return success(iotDevicePersonService.saveDeviceStatuses(reqVOS));
     }

+ 8 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java

@@ -9,9 +9,7 @@ import cn.iocoder.yudao.framework.security.config.SecurityProperties;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
-import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserBindReqVO;
 import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
-import cn.iocoder.yudao.module.system.convert.social.SocialUserConvert;
 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
@@ -186,4 +184,12 @@ public class AuthController {
         return success(authLoginRespVO);
     }
 
+    @PostMapping("/appSocialLogin")
+    @PermitAll
+    @Operation(summary = "app 钉钉快捷登录,使用 code 授权码", description = "适合未登录的用户,但是社交账号已绑定用户")
+    public CommonResult<AuthLoginRespVO> appSocialLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) {
+        AuthLoginRespVO authLoginRespVO = authService.appSocialLogin(reqVO);
+        return success(authLoginRespVO);
+    }
+
 }

+ 8 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java

@@ -63,6 +63,14 @@ public interface AdminAuthService {
      */
     AuthLoginRespVO socialLogin(@Valid AuthSocialLoginReqVO reqVO);
 
+    /**
+     * app 钉钉快捷登录,使用 code 授权码
+     *
+     * @param reqVO 登录信息
+     * @return 登录结果
+     */
+    AuthLoginRespVO appSocialLogin(@Valid AuthSocialLoginReqVO reqVO);
+
     /**
      * 刷新访问令牌
      *

+ 27 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.system.service.auth;
 
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
@@ -214,6 +216,31 @@ public class AdminAuthServiceImpl implements AdminAuthService {
         }
     }
 
+    @Override
+    public AuthLoginRespVO appSocialLogin(AuthSocialLoginReqVO reqVO) {
+        try {
+            String accessToken = DingtalkUtil.getUserToken(reqVO.getCode());
+            System.out.println("通过授权码获取 accessToken:" + accessToken);
+            // 通过 accessToken 获取 用户信息 对比数据库中存储的用户信息 发放平台 token
+            if (StrUtil.isEmpty(accessToken)) {
+                throw exception(USER_NOT_EXISTS);
+            }
+            String mobile = DingtalkUtil.getUserInfo(accessToken);
+            if (StrUtil.isEmpty(mobile)) {
+                throw exception(USER_NOT_EXISTS);
+            }
+            // 查询当前用户表中 此手机号 是否存在
+            AdminUserDO user = userService.getUserByMobile(mobile);
+            if (ObjUtil.isEmpty(user)) {
+                throw exception(USER_NOT_EXISTS);
+            }
+            // 创建 Token 令牌,记录登录日志
+            return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     @VisibleForTesting
     void validateCaptcha(AuthLoginReqVO reqVO) {
         ResponseModel response = doValidateCaptcha(reqVO);

+ 1 - 4
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dingtalk/DingtalkSendServiceImpl.java

@@ -2,9 +2,8 @@ package cn.iocoder.yudao.module.system.service.dingtalk;
 
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.module.system.api.dingtalk.DingtalkSendApi;
-import cn.iocoder.yudao.module.system.api.dingtalk.dto.send.DingtalkSendSingleToUserReqDTO;
-import cn.iocoder.yudao.module.system.service.dingtalk.vo.DingTalkMsgParams;
 import cn.iocoder.yudao.module.system.api.dingtalk.dto.send.NotifySendResponse;
+import cn.iocoder.yudao.module.system.service.dingtalk.vo.DingTalkMsgParams;
 import com.alibaba.fastjson.JSON;
 import com.aliyun.dingtalkoauth2_1_0.Client;
 import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest;
@@ -20,8 +19,6 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
-import javax.annotation.Resource;
-
 /**
  * 钉钉消息 Service 发送的实现
  *

+ 118 - 11
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/DingtalkUtil.java

@@ -1,5 +1,14 @@
 package cn.iocoder.yudao.module.system.util;
 
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
+import com.aliyun.dingtalkcontact_1_0.models.GetUserHeaders;
+import com.aliyun.dingtalkcontact_1_0.models.GetUserResponse;
+import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenRequest;
+import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenResponse;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teaopenapi.models.Config;
+import com.aliyun.teautil.models.RuntimeOptions;
 import com.dingtalk.api.DefaultDingTalkClient;
 import com.dingtalk.api.DingTalkClient;
 import com.dingtalk.api.request.OapiGettokenRequest;
@@ -13,38 +22,52 @@ import com.dingtalk.api.response.OapiV2UserGetuserinfoResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
 
 /**
  * @author suiyy
  * @date 20230602
  * @desc 钉钉API接口工具类
  */
+@Service
 public class DingtalkUtil {
     private static final Logger logger = LoggerFactory.getLogger(DingtalkUtil.class);
 
     /**
      * 微应用appKey
      */
-    @Value("${justauth.type.DINGTALK.client-id}")
-    private String APP_KEY;
+    private static String APPKEY;
+    @Value("${dingtalk.APP_KEY}")
+    public void setAPPKEY(String APPKEY) {
+        DingtalkUtil.APPKEY = APPKEY;
+    }
 
     /**
      * 微应用appSecret
      */
-    @Value("${justauth.type.DINGTALK.client-secret}")
-    private String APP_SECRET;
+    private static String APPSECRET;
+    @Value("${dingtalk.APP_SECRET}")
+    public void setAPPSECRET(String APPSECRET) {
+        DingtalkUtil.APPSECRET = APPSECRET;
+    }
 
     /**
      * 获取access_token
      */
+    private static String GETACCESSTOKENURL;
     @Value("${dingtalk.GET_ACCESS_TOKEN_URL}")
-    private String GET_ACCESS_TOKEN_URL;
+    public void setGETACCESSTOKENURL(String GETACCESSTOKENURL) {
+        DingtalkUtil.GETACCESSTOKENURL = GETACCESSTOKENURL;
+    }
 
     /**
      * 根据钉钉用户id获取用户信息
      */
+    private static String URLGETUSERINFOBYUSERID;
     @Value("${dingtalk.URL_GET_USERINFO_BYUSERID}")
-    private String URL_GET_USERINFO_BYUSERID;
+    public void setURLGETUSERINFOBYUSERID(String URLGETUSERINFOBYUSERID) {
+        DingtalkUtil.URLGETUSERINFOBYUSERID = URLGETUSERINFOBYUSERID;
+    }
 
     /**
      * 通过免登授权码获取用户信息
@@ -52,16 +75,26 @@ public class DingtalkUtil {
     @Value("${dingtalk.URL_GET_USERINFO_BYAUTHCODE}")
     private String URL_GET_USERINFO_BYAUTHCODE;
 
+    /**
+     * 通过UNIONID获取用户信息
+     */
+
+    private static String URLGETUSERINFOBYUNIONID;
+    @Value("${dingtalk.URL_GET_USERINFO_BYUNIONID}")
+    public void setURLGETUSERINFOBYUNIONID(String URLGETUSERINFOBYUNIONID) {
+        DingtalkUtil.URLGETUSERINFOBYUNIONID = URLGETUSERINFOBYUNIONID;
+    }
+
     /**
      * 获取钉钉 accessToken
      * @return
      * @throws Exception
      */
     public static String getAccessToken() throws Exception {
-        DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
+        DefaultDingTalkClient client = new DefaultDingTalkClient(GETACCESSTOKENURL);
         OapiGettokenRequest request = new OapiGettokenRequest();
-        request.setAppkey("dingmr9ez0ecgbmscfeb");
-        request.setAppsecret("VhG_zMdTvIBwA_0Ef8FJ0foH3VYYo5T-kw0ukX_PBA8Ah1xl7AjDw5RVYCU0DTpe");
+        request.setAppkey(APPKEY);
+        request.setAppsecret(APPSECRET);
         request.setHttpMethod("GET");
         OapiGettokenResponse response = client.execute(request);
         return response.getAccessToken();
@@ -69,7 +102,7 @@ public class DingtalkUtil {
 
     public static String getUserIdByUnion(String union) throws Exception {
         String accessToken = getAccessToken();
-        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid");
+        DingTalkClient client = new DefaultDingTalkClient(URLGETUSERINFOBYUNIONID);
         OapiUserGetbyunionidRequest req = new OapiUserGetbyunionidRequest();
         req.setUnionid(union);
         OapiUserGetbyunionidResponse rsp = client.execute(req, accessToken);
@@ -107,7 +140,7 @@ public class DingtalkUtil {
         // 1. 获取access_token
         String accessToken = getAccessToken();
         // 2. 获取用户详情
-        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
+        DingTalkClient client = new DefaultDingTalkClient(URLGETUSERINFOBYUSERID);
         OapiV2UserGetRequest req = new OapiV2UserGetRequest();
         req.setUserid(userId);
         req.setLanguage("zh_CN");
@@ -119,4 +152,78 @@ public class DingtalkUtil {
         return rsp.getResult();
     }
 
+    /**
+     * 根据 临时授权码 获取用户 accessToken
+     * @param code
+     * @throws Exception
+     */
+    public static String getUserToken(String code) throws Exception {
+        com.aliyun.dingtalkoauth2_1_0.Client client = createClient();
+        GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest()
+                .setClientId(APPKEY)
+                .setClientSecret(APPSECRET)
+                .setCode(code)
+                .setGrantType("authorization_code");
+        System.out.println("配置文件中的微应用信息:" + APPKEY + " - " + APPSECRET);
+        try {
+            GetUserTokenResponse userToken = client.getUserToken(getUserTokenRequest);
+            System.out.println(JSON.toJSONString(userToken));
+            return userToken.getBody().getAccessToken();
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+                System.out.println(JSON.toJSONString(err));
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+                System.out.println(JSON.toJSONString(err));
+            }
+        }
+        return StrUtil.EMPTY;
+    }
+
+    /**
+     * 根据 accessToken 获取用户信息(特别是手机号)
+     * @param accessToken
+     * @throws Exception
+     */
+    public static String getUserInfo(String accessToken) throws Exception {
+        com.aliyun.dingtalkcontact_1_0.Client client = createContactClient();
+        GetUserHeaders getUserHeaders = new GetUserHeaders();
+        getUserHeaders.xAcsDingtalkAccessToken = accessToken;
+        try {
+            GetUserResponse response = client.getUserWithOptions("me", getUserHeaders, new RuntimeOptions());
+            System.out.println(JSON.toJSON(response));
+            String mobile = response.getBody().getMobile();
+            return mobile;
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+                System.out.println(JSON.toJSONString(err));
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+                System.out.println(JSON.toJSONString(err));
+            }
+        }
+        return StrUtil.EMPTY;
+    }
+
+    public static com.aliyun.dingtalkoauth2_1_0.Client createClient() throws Exception {
+        Config config = new Config();
+        config.protocol = "https";
+        config.regionId = "central";
+        return new com.aliyun.dingtalkoauth2_1_0.Client(config);
+    }
+
+    public static com.aliyun.dingtalkcontact_1_0.Client createContactClient() throws Exception {
+        Config config = new Config();
+        config.protocol = "https";
+        config.regionId = "central";
+        return new com.aliyun.dingtalkcontact_1_0.Client(config);
+    }
 }

+ 1 - 0
yudao-server/src/main/resources/application.yaml

@@ -290,6 +290,7 @@ yudao:
       - /admin-api/system/dict-data/page
       - /admin-api/rq/iot-tree/simple-list
       - /system/auth/social-login
+      - /system/auth/appSocialLogin
       - /admin-api/rq/deviceTD/runningTsData #查询时序数据库设备参数
       - /admin-api/rq/deviceTD/runningSpanData #查询时序数据库设备参数
     ignore-tables: