Forráskód Böngészése

钉钉内部应用免登API

chenjun 1 éve
szülő
commit
e97e476064

+ 1 - 1
zjugis-framework/zjugis-spring-boot-starter-biz-social/src/main/java/com/zjugis/framework/social/core/enums/AuthExtendSource.java

@@ -56,7 +56,7 @@ public enum AuthExtendSource implements AuthSource {
 
         @Override
         public String userInfo() {
-            return "https://api.dingtalk.com//v1.0/contact/users/{unionId}";
+            return "https://api.dingtalk.com/v1.0/contact/users/{unionId}";
         }
 
         @Override

+ 59 - 0
zjugis-framework/zjugis-spring-boot-starter-biz-social/src/main/java/com/zjugis/framework/social/core/request/AuthDingtalkRequest.java

@@ -6,13 +6,18 @@ import com.xingyuv.http.support.HttpHeader;
 import com.xingyuv.http.util.MapUtil;
 import com.xingyuv.jushauth.cache.AuthStateCache;
 import com.xingyuv.jushauth.config.AuthConfig;
+import com.xingyuv.jushauth.enums.AuthResponseStatus;
 import com.xingyuv.jushauth.enums.AuthUserGender;
 import com.xingyuv.jushauth.exception.AuthException;
+import com.xingyuv.jushauth.log.Log;
 import com.xingyuv.jushauth.model.AuthCallback;
+import com.xingyuv.jushauth.model.AuthResponse;
 import com.xingyuv.jushauth.model.AuthToken;
 import com.xingyuv.jushauth.model.AuthUser;
 import com.xingyuv.jushauth.request.AuthDefaultRequest;
+import com.xingyuv.jushauth.utils.AuthChecker;
 import com.xingyuv.jushauth.utils.HttpUtils;
+import com.xingyuv.jushauth.utils.StringUtils;
 import com.xingyuv.jushauth.utils.UrlBuilder;
 import com.zjugis.framework.social.core.enums.AuthExtendSource;
 
@@ -71,6 +76,60 @@ public class AuthDingtalkRequest extends AuthDefaultRequest {
                 .build();
     }
 
+    protected AuthToken getAccessTokenApp() {
+        HttpHeader httpHeader = new HttpHeader();
+        Map<String, Object> paramsMap = new HashMap<>();
+        paramsMap.put("appSecret", config.getClientSecret());
+        paramsMap.put("appKey", config.getClientId());
+        String response = new HttpUtils(config.getHttpConfig()).post("https://api.dingtalk.com/v1.0/oauth2/accessToken", JSONObject.toJSONString(paramsMap), httpHeader).getBody();
+        JSONObject object = JSON.parseObject(response);
+        return AuthToken.builder()
+                .accessToken(object.getString("accessToken"))
+                .expireIn(object.getIntValue("expireIn"))
+                .build();
+    }
+
+
+    public AuthUser getUserInfoApp(String authCode) {
+        AuthToken authToken = this.getAccessTokenApp();
+        String accessToken = authToken.getAccessToken();
+        HttpHeader httpHeader = new HttpHeader();
+        httpHeader.add("x-acs-dingtalk-access-token", accessToken);
+        Map<String, Object> paramsMap = new HashMap<>();
+        paramsMap.put("code", authCode);
+        String response = new HttpUtils(config.getHttpConfig()).post("https://oapi.dingtalk.com/topapi/v2/user/getuserinfo?access_token="+accessToken, JSONObject.toJSONString(paramsMap), httpHeader).getBody();
+        JSONObject object = JSON.parseObject(response).getJSONObject("result");
+        return AuthUser.builder()
+                .rawUserInfo(object)
+                .uuid(object.getString("unionid"))
+                .nickname(object.getString("name"))
+                .username(object.getString("name"))
+                .gender(AuthUserGender.UNKNOWN)
+                .source(source.toString())
+                .token(authToken)
+                .build();
+    }
+
+
+    /**
+     * 处理{@link AuthDefaultRequest#login(AuthCallback)} 发生异常的情况,统一响应参数
+     *
+     * @param e 具体的异常
+     * @return AuthResponse
+     */
+    AuthResponse responseError(Exception e) {
+        int errorCode = AuthResponseStatus.FAILURE.getCode();
+        String errorMsg = e.getMessage();
+        if (e instanceof AuthException) {
+            AuthException authException = ((AuthException) e);
+            errorCode = authException.getErrorCode();
+            if (StringUtils.isNotEmpty(authException.getErrorMsg())) {
+                errorMsg = authException.getErrorMsg();
+            }
+        }
+        return AuthResponse.builder().code(errorCode).msg(errorMsg).build();
+    }
+
     /**
      * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
      *

+ 41 - 0
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/controller/app/auth/AppAuthController.java

@@ -0,0 +1,41 @@
+package com.zjugis.module.system.controller.app.auth;
+
+import com.zjugis.framework.common.pojo.CommonResult;
+import com.zjugis.framework.operatelog.core.annotations.OperateLog;
+import com.zjugis.module.system.controller.admin.auth.vo.AuthLoginReqVO;
+import com.zjugis.module.system.controller.admin.auth.vo.AuthLoginRespVO;
+import com.zjugis.module.system.service.auth.AdminAuthService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.annotation.security.PermitAll;
+import javax.validation.Valid;
+
+import static com.zjugis.framework.common.pojo.CommonResult.success;
+
+/**
+ * @Author 陈俊
+ * @Date 2024/4/22 11:33
+ * @Version 1.0
+ */
+@Tag(name = "用户 App - 认证")
+@RestController
+@RequestMapping("/system/auth")
+@Validated
+public class AppAuthController {
+
+    @Resource
+    private AdminAuthService authService;
+
+    @PostMapping("/login-ding")
+    @PermitAll
+    @Operation(summary = "钉钉内部应用免登")
+    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
+    public CommonResult<AuthLoginRespVO> loginDing(@RequestParam("code") String requestAuthCode) {
+        return success(authService.loginDing(requestAuthCode));
+    }
+}

+ 6 - 0
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/service/auth/AdminAuthService.java

@@ -70,4 +70,10 @@ public interface AdminAuthService {
      */
     AuthLoginRespVO refreshToken(String refreshToken);
 
+    /**
+     * 钉钉内部应用免登
+     * @param requestAuthCode
+     * @return
+     */
+    AuthLoginRespVO loginDing(String requestAuthCode);
 }

+ 19 - 0
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/service/auth/AdminAuthServiceImpl.java

@@ -17,6 +17,7 @@ import com.zjugis.module.system.enums.logger.LoginLogTypeEnum;
 import com.zjugis.module.system.enums.logger.LoginResultEnum;
 import com.zjugis.module.system.enums.oauth2.OAuth2ClientConstants;
 import com.zjugis.module.system.enums.sms.SmsSceneEnum;
+import com.zjugis.module.system.enums.social.SocialTypeEnum;
 import com.zjugis.module.system.service.logger.LoginLogService;
 import com.zjugis.module.system.service.member.MemberService;
 import com.zjugis.module.system.service.oauth2.OAuth2TokenService;
@@ -206,6 +207,24 @@ public class AdminAuthServiceImpl implements AdminAuthService {
         return AuthConvert.INSTANCE.convert(accessTokenDO);
     }
 
+    @Override
+    public AuthLoginRespVO loginDing(String requestAuthCode) {
+        // 使用免登授权码获取用户ID
+        String userId = socialUserService.getBindUserIdByAuthCode(SocialTypeEnum.DINGTALK.getType(), requestAuthCode);
+        if (userId == null) {
+            throw exception(AUTH_MOBILE_NOT_EXISTS);
+        }
+
+        // 获得用户
+        AdminUserDO user = userService.getUser(userId);
+        if (user == null) {
+            throw exception(USER_NOT_EXISTS);
+        }
+
+        // 创建 Token 令牌,记录登录日志
+        return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
+    }
+
     @Override
     public void logout(String token, Integer logType) {
         // 删除访问令牌

+ 8 - 0
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/service/social/SocialUserService.java

@@ -78,4 +78,12 @@ public interface SocialUserService {
     String getBindUserId(Integer userType, Integer type, String code, String state);
 
     List<String> getBindUserIds(Collection<String> socialIds, Integer socialType);
+
+    /**
+     * 企业内部应用免登
+     * @param type 社交平台的类型 {@link SocialTypeEnum}
+     * @param requestAuthCode
+     * @return
+     */
+    String getBindUserIdByAuthCode(Integer type, String requestAuthCode);
 }

+ 16 - 1
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/service/social/SocialUserServiceImpl.java

@@ -8,8 +8,10 @@ import com.xingyuv.jushauth.model.AuthResponse;
 import com.xingyuv.jushauth.model.AuthUser;
 import com.xingyuv.jushauth.request.AuthRequest;
 import com.xingyuv.jushauth.utils.AuthStateUtils;
+import com.zjugis.framework.common.enums.UserTypeEnum;
 import com.zjugis.framework.common.util.http.HttpUtils;
 import com.zjugis.framework.social.core.ZjugisAuthRequestFactory;
+import com.zjugis.framework.social.core.request.AuthDingtalkRequest;
 import com.zjugis.module.system.api.social.dto.SocialUserBindReqDTO;
 import com.zjugis.module.system.dal.dataobject.social.SocialUserBindDO;
 import com.zjugis.module.system.dal.dataobject.social.SocialUserDO;
@@ -159,7 +161,7 @@ public class SocialUserServiceImpl implements SocialUserService {
             bindSocialUser(new SocialUserBindReqDTO(user.getId(), userType,
                     type, code, state));
             userId = user.getId();
-            if(avatarUrl != null){
+            if (avatarUrl != null) {
                 adminUserMapper.updateById(new AdminUserDO().setId(userId).setAvatar(avatarUrl.toString()));
             }
         } else {
@@ -179,6 +181,19 @@ public class SocialUserServiceImpl implements SocialUserService {
         }
     }
 
+    @Override
+    public String getBindUserIdByAuthCode(Integer type, String requestAuthCode) {
+        // 获得对应的 AuthRequest 实现
+        AuthDingtalkRequest authRequest = (AuthDingtalkRequest) zjugisAuthRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource());
+        AuthUser userInfoApp = authRequest.getUserInfoApp(requestAuthCode);
+        SocialUserDO socialUser = socialUserMapper.selectByTypeAndOpenid(type, userInfoApp.getUuid());
+        Assert.notNull(socialUser, "社交用户不能为空");
+        Integer userType = UserTypeEnum.ADMIN.getValue();
+        SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType, socialUser.getId());
+        Assert.notNull(socialUserBind, "社交用户未绑定");
+        return socialUserBind.getUserId();
+    }
+
     /**
      * 请求社交平台,获得授权的用户
      *