diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java index bba6113d..f46cbdc3 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java @@ -2,9 +2,18 @@ package org.dromara.web.controller; import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSON; +import cn.hutool.json.JSONUtil; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; +import me.zhyd.oauth.cache.AuthStateCache; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthRequest; +import me.zhyd.oauth.utils.AuthStateUtils; import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.model.EmailLoginBody; import org.dromara.common.core.domain.model.LoginBody; @@ -13,9 +22,16 @@ import org.dromara.common.core.domain.model.SmsLoginBody; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.social.config.SocialConfig; +import org.dromara.common.social.config.properties.ConfigProperties; +import org.dromara.common.social.config.properties.SocialProperties; +import org.dromara.common.social.utils.AuthRedisStateCache; +import org.dromara.common.social.utils.SocialUtils; import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.system.domain.bo.SysTenantBo; import org.dromara.system.domain.vo.SysTenantVo; +import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.service.ISocialUserService; import org.dromara.system.service.ISysConfigService; import org.dromara.system.service.ISysTenantService; import org.dromara.web.domain.vo.LoginTenantVo; @@ -26,8 +42,10 @@ import org.dromara.web.service.SysRegisterService; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.io.IOException; import java.net.URL; import java.util.List; +import java.util.Map; /** * 认证 @@ -41,10 +59,14 @@ import java.util.List; @RequestMapping("/auth") public class AuthController { + private final SocialProperties socialProperties; private final SysLoginService loginService; private final SysRegisterService registerService; private final ISysConfigService configService; private final ISysTenantService tenantService; + private final ISocialUserService socialUserService; + + /** * 登录方法 @@ -115,6 +137,71 @@ public class AuthController { return R.ok(loginVo); } + + /** + * 认证授权 + * @param source + */ + @GetMapping("/binding/{source}") + @ResponseBody + public R authBinding(@PathVariable("source") String source, HttpServletRequest request){ + SysUserVo userLoding = new SysUserVo(); + if (ObjectUtil.isNull(userLoding)) { + return R.fail("授权失败,请先登录再绑定"); + } + if (socialUserService.isExistByUserIdAndSource(userLoding.getUserId(),source)) + { + return R.fail(source + "平台账号已经被账号绑定"); + } + ConfigProperties obj = socialProperties.getType().get(source); + if (ObjectUtil.isNull(obj)){ + return R.fail(source + "平台账号暂不支持"); + } + AuthRequest authRequest = SocialUtils.getAuthRequest(source, + obj.getClientId(), + obj.getClientSecret(), + obj.getRedirectUri()); + String authorizeUrl = authRequest.authorize(AuthStateUtils.createState()); + return R.ok(authorizeUrl); + } + + /** + * 第三方登录回调业务处理 + * @param source + * @param callback + * @param request + * @return + */ + @SuppressWarnings("unchecked") + @GetMapping("/social-login/{source}") + public R socialLogin(@PathVariable("source") String source, AuthCallback callback, HttpServletRequest request) throws IOException { + ConfigProperties obj = socialProperties.getType().get(source); + if (ObjectUtil.isNull(obj)){ + return R.fail(source + "平台账号暂不支持"); + } + AuthRequest authRequest = SocialUtils.getAuthRequest(source, + obj.getClientId(), + obj.getClientSecret(), + obj.getRedirectUri()); + AuthResponse response = authRequest.login(callback); + return loginService.socialLogin(source, response, request); + } + + /** + * 取消授权 + * @param socialId + */ + @DeleteMapping(value = "/unlock/{socialId}") + public R unlockSocial(@PathVariable Long socialId) + { + Boolean rows = socialUserService.deleteWithValidById(socialId); + return rows ? R.ok() : R.fail("取消授权失败"); + } + + + + + /** * 退出登录 */ diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 92a3640e..78cad6ed 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -169,3 +169,151 @@ sms: signName: 测试 # 腾讯专用 sdkAppId: + +justauth: + enabled: true + type: + QQ: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/qq/callback + union-id: false + WEIBO: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/weibo/callback + gitee: + client-id: 38eaaa1b77b5e064313057a2f5745ce3a9f3e7686d9bd302c7df2f308ef6db81 + client-secret: 2e633af8780cb9fe002c4c7291b722db944402e271efb99b062811f52d7da1ff + redirect-uri: http://localhost:8888/social-login?source=gitee + DINGTALK: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/dingtalk/callback + BAIDU: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/baidu/callback + CSDN: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/csdn/callback + CODING: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/coding/callback + coding-group-name: xx + OSCHINA: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/oschina/callback + ALIPAY: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/alipay/callback + alipay-public-key: MIIB**************DAQAB + WECHAT_OPEN: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/wechat_open/callback + WECHAT_MP: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/wechat_mp/callback + WECHAT_ENTERPRISE: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/wechat_enterprise/callback + agent-id: 1000002 + TAOBAO: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/taobao/callback + GOOGLE: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/google/callback + FACEBOOK: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/facebook/callback + DOUYIN: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/douyin/callback + LINKEDIN: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/linkedin/callback + MICROSOFT: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/microsoft/callback + MI: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/mi/callback + TOUTIAO: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/toutiao/callback + TEAMBITION: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/teambition/callback + RENREN: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/renren/callback + PINTEREST: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/pinterest/callback + STACK_OVERFLOW: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/stack_overflow/callback + stack-overflow-key: asd*********asd + HUAWEI: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/huawei/callback + KUJIALE: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/kujiale/callback + GITLAB: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/gitlab/callback + MEITUAN: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/meituan/callback + ELEME: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/eleme/callback + TWITTER: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/twitter/callback + XMLY: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/xmly/callback + # 设备唯一标识ID + device-id: xxxxxxxxxxxxxx + # 客户端操作系统类型,1-iOS系统,2-Android系统,3-Web + client-os-type: 3 + # 客户端包名,如果 clientOsType 为1或2时必填。对Android客户端是包名,对IOS客户端是Bundle ID + #pack-id: xxxx + FEISHU: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/feishu/callback + JD: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: http://oauth.xkcoding.com/demo/oauth/jd/callback + diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java index 09bf44b6..dbadfc2d 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java @@ -26,7 +26,12 @@ public enum DeviceType { /** * 小程序端 */ - XCX("xcx"); + XCX("xcx"), + + /** + * social第三方端 + */ + SOCIAL("social"); private final String device; } diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/ConfigProperties.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/ConfigProperties.java new file mode 100644 index 00000000..813b03a6 --- /dev/null +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/ConfigProperties.java @@ -0,0 +1,58 @@ +package org.dromara.common.social.config.properties; + +import lombok.Data; + +@Data +public class ConfigProperties { + + /** + * 应用 ID + */ + private String clientId; + + /** + * 应用密钥 + */ + private String clientSecret; + + /** + * 回调地址 + */ + private String redirectUri; + + /** + * 是否获取unionId + */ + private boolean unionId; + + /** + * Coding 企业名称 + */ + private String codingGroupName; + + /** + * 支付宝公钥 + */ + private String alipayPublicKey; + + /** + * 企业微信应用ID + */ + private String agentId; + + /** + * stackoverflow api key + */ + private String stackOverflowKey; + + /** + * 设备ID + */ + private String deviceId; + + /** + * 客户端系统类型 + */ + private String clientOsType; + +} diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/AuthRedisStateCache.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/AuthRedisStateCache.java new file mode 100644 index 00000000..f438a1c8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/AuthRedisStateCache.java @@ -0,0 +1,79 @@ +package org.dromara.common.social.utils; + +import jakarta.annotation.PostConstruct; +import me.zhyd.oauth.cache.AuthStateCache; +import org.dromara.common.redis.utils.RedisUtils; +import org.dromara.common.social.config.properties.SocialProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; + +import java.time.Duration; + +public class AuthRedisStateCache implements AuthStateCache { + + private final SocialProperties socialProperties; + private final RedisTemplate redisTemplate; + + private ValueOperations valueOperations; + + @PostConstruct + public void init() { + valueOperations = redisTemplate.opsForValue(); + } + + + public AuthRedisStateCache() { + this.socialProperties = new SocialProperties(); + redisTemplate = new RedisTemplate<>(); + } + + /** + * 存入缓存 + * + * @param key 缓存key + * @param value 缓存内容 + */ + @Override + public void cache(String key, String value) { + // TODO: 自定义存入缓存 + RedisUtils.setCacheObject(key, value, Duration.ofMillis(socialProperties.getTimeout())); + } + + /** + * 存入缓存 + * + * @param key 缓存key + * @param value 缓存内容 + * @param timeout 指定缓存过期时间(毫秒) + */ + @Override + public void cache(String key, String value, long timeout) { + // TODO: 自定义存入缓存 + RedisUtils.setCacheObject(key, value, Duration.ofMillis(timeout)); + } + + /** + * 获取缓存内容 + * + * @param key 缓存key + * @return 缓存内容 + */ + @Override + public String get(String key) { + // TODO: 自定义获取缓存内容 + return RedisUtils.getCacheObject(key); + } + + /** + * 是否存在key,如果对应key的value值已过期,也返回false + * + * @param key 缓存key + * @return true:存在key,并且value没过期;false:key不存在或者已过期 + */ + @Override + public boolean containsKey(String key) { + // TODO: 自定义判断key是否存在 + return RedisUtils.hasKey(key); + } +}