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

社交登录修改,自动绑定钉钉账号,通过手机号自动绑定

chenjun 1 éve
szülő
commit
ffe57007ee

+ 1 - 1
client/src/views/Login/SocialLogin.vue

@@ -201,7 +201,7 @@ const socialLogin = async () => {
   const route = useRoute()
   // 社交登录相关
   type.value = route?.query?.type as number
-  code.value = route?.query?.code as string
+  code.value = route?.query?.authCode as string
   state.value = route?.query?.state as string
 
   if (!type.value) {

+ 3 - 0
zjugis-framework/zjugis-spring-boot-starter-biz-social/src/main/java/com/zjugis/framework/social/core/ZjugisAuthRequestFactory.java

@@ -3,6 +3,7 @@ package com.zjugis.framework.social.core;
 import cn.hutool.core.util.EnumUtil;
 import cn.hutool.core.util.ReflectUtil;
 import com.zjugis.framework.social.core.enums.AuthExtendSource;
+import com.zjugis.framework.social.core.request.AuthDingtalkRequest;
 import com.zjugis.framework.social.core.request.AuthWeChatMiniAppRequest;
 import com.xingyuv.jushauth.cache.AuthStateCache;
 import com.xingyuv.jushauth.config.AuthConfig;
@@ -77,6 +78,8 @@ public class ZjugisAuthRequestFactory extends AuthRequestFactory {
         switch (authExtendSource) {
             case WECHAT_MINI_APP:
                 return new AuthWeChatMiniAppRequest(config, authStateCache);
+            case DINGTALK:
+                return new AuthDingtalkRequest(config, authStateCache);
             default:
                 return null;
         }

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

@@ -1,11 +1,14 @@
 package com.zjugis.framework.social.core.enums;
 
 import com.xingyuv.jushauth.config.AuthSource;
+import com.xingyuv.jushauth.enums.AuthResponseStatus;
+import com.xingyuv.jushauth.exception.AuthException;
 import com.xingyuv.jushauth.request.AuthDefaultRequest;
+import com.xingyuv.jushauth.request.AuthDingTalkRequest;
 
 /**
  * 拓展 JustAuth 各 api 需要的 url, 用枚举类分平台类型管理
- *
+ * <p>
  * 默认配置 {@link com.xingyuv.jushauth.config.AuthDefaultSource}
  *
  * @author timfruit
@@ -16,7 +19,6 @@ public enum AuthExtendSource implements AuthSource {
      * 微信小程序授权登录
      */
     WECHAT_MINI_APP {
-
         @Override
         public String authorize() {
             // 参见 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 文档
@@ -36,6 +38,27 @@ public enum AuthExtendSource implements AuthSource {
             throw new UnsupportedOperationException("不支持获取用户信息 url,请使用小程序内置函数 wx.getUserProfile() 获取用户信息");
         }
 
+        @Override
+        public Class<? extends AuthDefaultRequest> getTargetClass() {
+            return null;
+        }
+    },
+    DINGTALK {
+        @Override
+        public String authorize() {
+            return "https://login.dingtalk.com/oauth2/auth";
+        }
+
+        @Override
+        public String accessToken() {
+            return "https://api.dingtalk.com/v1.0/oauth2/userAccessToken";
+        }
+
+        @Override
+        public String userInfo() {
+            return "https://api.dingtalk.com//v1.0/contact/users/{unionId}";
+        }
+
         @Override
         public Class<? extends AuthDefaultRequest> getTargetClass() {
             return null;

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

@@ -0,0 +1,110 @@
+package com.zjugis.framework.social.core.request;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+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.AuthUserGender;
+import com.xingyuv.jushauth.exception.AuthException;
+import com.xingyuv.jushauth.model.AuthCallback;
+import com.xingyuv.jushauth.model.AuthToken;
+import com.xingyuv.jushauth.model.AuthUser;
+import com.xingyuv.jushauth.request.AuthDefaultRequest;
+import com.xingyuv.jushauth.utils.HttpUtils;
+import com.xingyuv.jushauth.utils.UrlBuilder;
+import com.zjugis.framework.social.core.enums.AuthExtendSource;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 扩展钉钉登录获取手机号
+ * @Author 陈俊
+ * @Date 2023/11/1 10:39
+ * @Version 1.0
+ */
+public class AuthDingtalkRequest extends AuthDefaultRequest {
+
+
+    public AuthDingtalkRequest(AuthConfig config, AuthStateCache authStateCache) {
+        super(config, AuthExtendSource.DINGTALK, authStateCache);
+    }
+
+    @Override
+    protected AuthToken getAccessToken(AuthCallback authCallback) {
+        String accessTokenUrl = accessTokenUrl(authCallback.getCode());
+        HttpHeader httpHeader = new HttpHeader();
+        Map<String, Object> paramsMap = new HashMap<>();
+        paramsMap.put("clientSecret", config.getClientSecret());
+        paramsMap.put("clientId", config.getClientId());
+        paramsMap.put("code", authCallback.getCode());
+        paramsMap.put("grantType", "authorization_code");
+
+        String response = new HttpUtils(config.getHttpConfig()).post(accessTokenUrl, JSONObject.toJSONString(paramsMap), httpHeader).getBody();
+        JSONObject object = JSON.parseObject(response);
+        return AuthToken.builder()
+                .accessToken(object.getString("accessToken"))
+                .refreshToken(object.getString("refreshToken"))
+                .expireIn(object.getIntValue("expireIn"))
+                .build();
+    }
+
+    @Override
+    protected AuthUser getUserInfo(AuthToken authToken) {
+        String accessToken = authToken.getAccessToken();
+        HttpHeader httpHeader = new HttpHeader();
+        httpHeader.add("x-acs-dingtalk-access-token", accessToken);
+        String response = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false).getBody();
+        JSONObject object = JSON.parseObject(response);
+        return AuthUser.builder()
+                .rawUserInfo(object)
+                .uuid(object.getString("unionId"))
+                .nickname(object.getString("nick"))
+                .username(object.getString("nick"))
+                .avatar(object.getString("avatarUrl"))
+                .email(object.getString("email"))
+                .gender(AuthUserGender.UNKNOWN)
+                .source(source.toString())
+                .token(authToken)
+                .build();
+    }
+
+    /**
+     * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
+     *
+     * @param state state 验证授权流程的参数,可以防止csrf
+     * @return 返回授权地址
+     * @since 1.9.3
+     */
+    @Override
+    public String authorize(String state) {
+        return UrlBuilder.fromBaseUrl(source.authorize())
+                .queryParam("response_type", "code")
+                .queryParam("client_id", config.getClientId())
+                .queryParam("scope", "openid")
+                .queryParam("redirect_uri", config.getRedirectUri())
+                .queryParam("state", getRealState(state))
+                .queryParam("prompt", "consent")
+                .build();
+    }
+
+    /**
+     * 返回获取userInfo的url
+     *
+     * @param authToken 用户授权后的token
+     * @return 返回获取userInfo的url
+     */
+    @Override
+    protected String userInfoUrl(AuthToken authToken) {
+        return UrlBuilder.fromBaseUrl(source.userInfo())
+                .build().replace("{unionId}","me");
+    }
+
+    @Override
+    protected String accessTokenUrl(String code) {
+        return UrlBuilder.fromBaseUrl(source.accessToken())
+                .build();
+    }
+}

+ 39 - 0
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/dal/dataobject/dept/UserDeptDO.java

@@ -0,0 +1,39 @@
+package com.zjugis.module.system.dal.dataobject.dept;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import com.zjugis.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 用户部门 DO
+ *
+ * @author 管理员
+ */
+@TableName("system_user_dept")
+@KeySequence("system_user_dept_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class UserDeptDO extends BaseDO {
+
+    /**
+     * id
+     */
+    @TableId
+    private Long id;
+    /**
+     * 用户ID
+     */
+    private Long userId;
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+}

+ 5 - 0
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/dal/dataobject/user/AdminUserDO.java

@@ -55,6 +55,11 @@ public class AdminUserDO extends TenantBaseDO {
      * 部门 ID
      */
     private Long deptId;
+    /**
+     * 部门 ID 数组
+     */
+    @TableField(typeHandler = JsonLongSetTypeHandler.class)
+    private Set<Long> deptIds;
     /**
      * 岗位编号数组
      */

+ 38 - 0
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/dal/mysql/dept/UserDeptMapper.java

@@ -0,0 +1,38 @@
+package com.zjugis.module.system.dal.mysql.dept;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.zjugis.framework.mybatis.core.mapper.BaseMapperX;
+import com.zjugis.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.zjugis.module.system.dal.dataobject.dept.UserDeptDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 用户部门 Mapper
+ *
+ * @author 管理员
+ */
+@Mapper
+public interface UserDeptMapper extends BaseMapperX<UserDeptDO> {
+
+    default List<UserDeptDO> selectListByUserId(Long userId) {
+        return selectList(UserDeptDO::getUserId, userId);
+    }
+
+    default void deleteByUserIdAndDeptId(Long userId, Collection<Long> deptIds) {
+        delete(new LambdaQueryWrapperX<UserDeptDO>()
+                .eq(UserDeptDO::getUserId, userId)
+                .in(UserDeptDO::getDeptId, deptIds));
+    }
+
+    default List<UserDeptDO> selectListByPostIds(Collection<Long> deptIds) {
+        return selectList(UserDeptDO::getDeptId, deptIds);
+    }
+
+    default void deleteByUserId(Long userId) {
+        delete(Wrappers.lambdaUpdate(UserDeptDO.class).eq(UserDeptDO::getDeptId, userId));
+    }
+
+}

+ 2 - 6
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/dal/mysql/user/AdminUserMapper.java

@@ -32,7 +32,7 @@ public interface AdminUserMapper extends BaseMapperX<AdminUserDO> {
                 .likeIfPresent(AdminUserDO::getMobile, reqVO.getMobile())
                 .eqIfPresent(AdminUserDO::getStatus, reqVO.getStatus())
                 .betweenIfPresent(AdminUserDO::getCreateTime, reqVO.getCreateTime())
-                .inIfPresent(AdminUserDO::getDeptId, deptIds)
+                .likeIfPresent(AdminUserDO::getDeptIds, reqVO.getDeptId()+"")
                 .orderByDesc(AdminUserDO::getId));
     }
 
@@ -42,7 +42,7 @@ public interface AdminUserMapper extends BaseMapperX<AdminUserDO> {
                 .likeIfPresent(AdminUserDO::getMobile, reqVO.getMobile())
                 .eqIfPresent(AdminUserDO::getStatus, reqVO.getStatus())
                 .betweenIfPresent(AdminUserDO::getCreateTime, reqVO.getCreateTime())
-                .inIfPresent(AdminUserDO::getDeptId, deptIds));
+                .likeIfPresent(AdminUserDO::getDeptIds, reqVO.getDeptId()+""));
     }
 
     default List<AdminUserDO> selectListByNickname(String nickname) {
@@ -53,8 +53,4 @@ public interface AdminUserMapper extends BaseMapperX<AdminUserDO> {
         return selectList(AdminUserDO::getStatus, status);
     }
 
-    default List<AdminUserDO> selectListByDeptIds(Collection<Long> deptIds) {
-        return selectList(AdminUserDO::getDeptId, deptIds);
-    }
-
 }

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

@@ -158,7 +158,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
         Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
                 reqVO.getCode(), reqVO.getState());
         if (userId == null) {
-            throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
+            throw exception(AUTH_MOBILE_NOT_EXISTS);
         }
 
         // 获得用户

+ 21 - 3
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/service/social/SocialUserServiceImpl.java

@@ -2,13 +2,16 @@ package com.zjugis.module.system.service.social;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Assert;
+import com.alibaba.fastjson.JSONObject;
 import com.zjugis.framework.common.util.http.HttpUtils;
 import com.zjugis.framework.social.core.ZjugisAuthRequestFactory;
 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;
+import com.zjugis.module.system.dal.dataobject.user.AdminUserDO;
 import com.zjugis.module.system.dal.mysql.social.SocialUserBindMapper;
 import com.zjugis.module.system.dal.mysql.social.SocialUserMapper;
+import com.zjugis.module.system.dal.mysql.user.AdminUserMapper;
 import com.zjugis.module.system.enums.social.SocialTypeEnum;
 import com.xingyuv.jushauth.model.AuthCallback;
 import com.xingyuv.jushauth.model.AuthResponse;
@@ -46,6 +49,9 @@ public class SocialUserServiceImpl implements SocialUserService {
     private SocialUserBindMapper socialUserBindMapper;
     @Resource
     private SocialUserMapper socialUserMapper;
+    @Resource
+    private AdminUserMapper adminUserMapper;
+
 
     @Override
     public String getAuthorizeUrl(Integer type, String redirectUri) {
@@ -130,18 +136,30 @@ public class SocialUserServiceImpl implements SocialUserService {
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public Long getBindUserId(Integer userType, Integer type, String code, String state) {
         // 获得社交用户
         SocialUserDO socialUser = authSocialUser(type, code, state);
         Assert.notNull(socialUser, "社交用户不能为空");
 
-        // 如果未绑定的社交用户,则无法自动登录,进行报错
+        Long userId;
+
+        // 如果未绑定的社交用户,进行自动绑定
         SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType,
                 socialUser.getId());
         if (socialUserBind == null) {
-            throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
+            String rawUserInfo = socialUser.getRawUserInfo();
+            String mobile = JSONObject.parseObject(rawUserInfo).get("mobile").toString();
+            String avatarUrl = JSONObject.parseObject(rawUserInfo).get("avatarUrl").toString();
+            AdminUserDO user = adminUserMapper.selectByMobile(mobile);
+            bindSocialUser(new SocialUserBindReqDTO(user.getId(), userType,
+                    type, code, state));
+            userId = user.getId();
+            adminUserMapper.updateById(new AdminUserDO().setId(userId).setAvatar(avatarUrl));
+        }else {
+            userId = socialUserBind.getUserId();
         }
-        return socialUserBind.getUserId();
+        return userId;
     }
 
     /**

+ 28 - 1
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/service/user/AdminUserServiceImpl.java

@@ -15,8 +15,10 @@ import com.zjugis.module.system.controller.admin.user.vo.profile.UserProfileUpda
 import com.zjugis.module.system.controller.admin.user.vo.user.*;
 import com.zjugis.module.system.convert.user.UserConvert;
 import com.zjugis.module.system.dal.dataobject.dept.DeptDO;
+import com.zjugis.module.system.dal.dataobject.dept.UserDeptDO;
 import com.zjugis.module.system.dal.dataobject.dept.UserPostDO;
 import com.zjugis.module.system.dal.dataobject.user.AdminUserDO;
+import com.zjugis.module.system.dal.mysql.dept.UserDeptMapper;
 import com.zjugis.module.system.dal.mysql.dept.UserPostMapper;
 import com.zjugis.module.system.dal.mysql.user.AdminUserMapper;
 import com.zjugis.module.system.service.dept.DeptService;
@@ -70,6 +72,8 @@ public class AdminUserServiceImpl implements AdminUserService {
 
     @Resource
     private UserPostMapper userPostMapper;
+    @Resource
+    private UserDeptMapper userDeptMapper;
 
     @Resource
     private FileApi fileApi;
@@ -109,10 +113,29 @@ public class AdminUserServiceImpl implements AdminUserService {
         // 更新用户
         AdminUserDO updateObj = UserConvert.INSTANCE.convert(reqVO);
         userMapper.updateById(updateObj);
+        // 更新部门
+        updateUserDept(reqVO, updateObj);
         // 更新岗位
         updateUserPost(reqVO, updateObj);
     }
 
+    private void updateUserDept(UserUpdateReqVO reqVO, AdminUserDO updateObj) {
+        Long userId = reqVO.getId();
+        Set<Long> dbDeptIds = convertSet(userDeptMapper.selectListByUserId(userId), UserDeptDO::getDeptId);
+        // 计算新增和删除的部门编号
+        Set<Long> deptIds = updateObj.getDeptIds();
+        Collection<Long> createDeptIds = CollUtil.subtract(deptIds, dbDeptIds);
+        Collection<Long> deleteDeptIds = CollUtil.subtract(dbDeptIds, deptIds);
+        // 执行新增和删除。对于已经授权的菜单,不用做任何处理
+        if (!CollectionUtil.isEmpty(createDeptIds)) {
+            userDeptMapper.insertBatch(convertList(createDeptIds,
+                    deptId -> new UserDeptDO().setUserId(userId).setDeptId(deptId)));
+        }
+        if (!CollectionUtil.isEmpty(deleteDeptIds)) {
+            userDeptMapper.deleteByUserIdAndDeptId(userId, deleteDeptIds);
+        }
+    }
+
     private void updateUserPost(UserUpdateReqVO reqVO, AdminUserDO updateObj) {
         Long userId = reqVO.getId();
         Set<Long> dbPostIds = convertSet(userPostMapper.selectListByUserId(userId), UserPostDO::getPostId);
@@ -228,7 +251,11 @@ public class AdminUserServiceImpl implements AdminUserService {
         if (CollUtil.isEmpty(deptIds)) {
             return Collections.emptyList();
         }
-        return userMapper.selectListByDeptIds(deptIds);
+        Set<Long> userIds = convertSet(userDeptMapper.selectListByPostIds(deptIds), UserDeptDO::getUserId);
+        if (CollUtil.isEmpty(userIds)) {
+            return Collections.emptyList();
+        }
+        return userMapper.selectBatchIds(userIds);
     }
 
     @Override