package cn.iocoder.yudao.module.system.service.social; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ReflectUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper; import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; import cn.iocoder.yudao.module.system.framework.justauth.core.AuthRequestFactory; import com.google.common.annotations.VisibleForTesting; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import me.zhyd.oauth.config.AuthConfig; import me.zhyd.oauth.model.AuthCallback; import me.zhyd.oauth.model.AuthResponse; import me.zhyd.oauth.model.AuthUser; import me.zhyd.oauth.request.AuthAlipayRequest; import me.zhyd.oauth.request.AuthRequest; import me.zhyd.oauth.utils.AuthStateUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; /** * 社交应用 Service 实现类 * * @author 超级管理员 */ @Service @Slf4j public class SocialClientServiceImpl implements SocialClientService { @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") @Autowired(required = false) // 由于 justauth.enable 配置项,可以关闭 AuthRequestFactory 的功能,所以这里只能不强制注入 private AuthRequestFactory authRequestFactory; @Resource private SocialClientMapper socialClientMapper; @Override public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) { // 获得对应的 AuthRequest 实现 AuthRequest authRequest = buildAuthRequest(socialType, userType); // 生成跳转地址 String authorizeUri = authRequest.authorize(AuthStateUtils.createState()); return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri); } @Override public AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state) { // 构建请求 AuthRequest authRequest = buildAuthRequest(socialType, userType); AuthCallback authCallback = AuthCallback.builder().code(code).auth_code(code).state(state).build(); // 执行请求 AuthResponse authResponse = authRequest.login(authCallback); log.info("[getAuthUser][请求社交平台 type({}) request({}) response({})]", socialType, toJsonString(authCallback), toJsonString(authResponse)); if (!authResponse.ok()) { throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg()); } return (AuthUser) authResponse.getData(); } /** * 构建 AuthRequest 对象,支持多租户配置 * * @param socialType 社交类型 * @param userType 用户类型 * @return AuthRequest 对象 */ @VisibleForTesting AuthRequest buildAuthRequest(Integer socialType, Integer userType) { // 1. 先查找默认的配置项,从 application-*.yaml 中读取 AuthRequest request = authRequestFactory.get(SocialTypeEnum.valueOfType(socialType).getSource()); Assert.notNull(request, String.format("社交平台(%d) 不存在", socialType)); // 2. 查询 DB 的配置项,如果存在则进行覆盖 SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(socialType, userType); if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { // 2.1 构造新的 AuthConfig 对象 AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(request, "config"); AuthConfig newAuthConfig = ReflectUtil.newInstance(authConfig.getClass()); BeanUtil.copyProperties(authConfig, newAuthConfig); // 2.2 修改对应的 clientId + clientSecret 密钥 newAuthConfig.setClientId(client.getClientId()); newAuthConfig.setClientSecret(client.getClientSecret()); if (client.getAgentId() != null) { // 如果有 agentId 则修改 agentId newAuthConfig.setAgentId(client.getAgentId()); } // 2.3 设置会 request 里,进行后续使用 if (SocialTypeEnum.ALIPAY_MINI_PROGRAM.getType().equals(socialType)) { // 特殊:如果是支付宝的小程序,多了 publicKey 属性,可见 AuthConfig 里的 alipayPublicKey 字段说明 return new AuthAlipayRequest(newAuthConfig, client.getPublicKey()); } ReflectUtil.setFieldValue(request, "config", newAuthConfig); } return request; } // =================== 客户端管理 =================== @Override public Long createSocialClient(SocialClientSaveReqVO createReqVO) { // 校验重复 validateSocialClientUnique(null, createReqVO.getUserType(), createReqVO.getSocialType()); // 插入 SocialClientDO client = BeanUtils.toBean(createReqVO, SocialClientDO.class); socialClientMapper.insert(client); return client.getId(); } @Override public void updateSocialClient(SocialClientSaveReqVO updateReqVO) { // 校验存在 validateSocialClientExists(updateReqVO.getId()); // 校验重复 validateSocialClientUnique(updateReqVO.getId(), updateReqVO.getUserType(), updateReqVO.getSocialType()); // 更新 SocialClientDO updateObj = BeanUtils.toBean(updateReqVO, SocialClientDO.class); socialClientMapper.updateById(updateObj); } @Override public void deleteSocialClient(Long id) { // 校验存在 validateSocialClientExists(id); // 删除 socialClientMapper.deleteById(id); } @Override public void deleteSocialClientList(List ids) { socialClientMapper.deleteByIds(ids); } private void validateSocialClientExists(Long id) { if (socialClientMapper.selectById(id) == null) { throw exception(SOCIAL_CLIENT_NOT_EXISTS); } } /** * 校验社交应用是否重复,需要保证 userType + socialType 唯一 * 原因是,不同端(userType)选择某个社交登录(socialType)时,需要通过 {@link #buildAuthRequest(Integer, Integer)} 构建对应的请求 * * @param id 编号 * @param userType 用户类型 * @param socialType 社交类型 */ private void validateSocialClientUnique(Long id, Integer userType, Integer socialType) { SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( socialType, userType); if (client == null) { return; } if (id == null // 新增时,说明重复 || ObjUtil.notEqual(id, client.getId())) { // 更新时,如果 id 不一致,说明重复 throw exception(SOCIAL_CLIENT_UNIQUE); } } @Override public SocialClientDO getSocialClient(Long id) { return socialClientMapper.selectById(id); } @Override public PageResult getSocialClientPage(SocialClientPageReqVO pageReqVO) { return socialClientMapper.selectPage(pageReqVO); } }