package org.dromara.auth.controller; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; 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.apache.dubbo.config.annotation.DubboReference; import org.dromara.auth.domain.vo.LoginTenantVo; import org.dromara.auth.domain.vo.LoginVo; import org.dromara.auth.domain.vo.TenantListVo; import org.dromara.auth.form.RegisterBody; import org.dromara.auth.form.SocialLoginBody; import org.dromara.auth.service.IAuthStrategy; import org.dromara.auth.service.SysLoginService; import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.model.LoginBody; import org.dromara.common.core.utils.*; import org.dromara.common.encrypt.annotation.ApiEncrypt; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.social.config.properties.SocialLoginConfigProperties; import org.dromara.common.social.config.properties.SocialProperties; import org.dromara.common.social.utils.SocialUtils; import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.resource.api.RemoteMessageService; import org.dromara.system.api.RemoteClientService; import org.dromara.system.api.RemoteConfigService; import org.dromara.system.api.RemoteSocialService; import org.dromara.system.api.RemoteTenantService; import org.dromara.system.api.domain.vo.RemoteClientVo; import org.dromara.system.api.domain.vo.RemoteTenantVo; import org.springframework.web.bind.annotation.*; import java.net.URL; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * token 控制 * * @author Lion Li */ @Slf4j @RequiredArgsConstructor @RestController public class TokenController { private final SocialProperties socialProperties; private final SysLoginService sysLoginService; private final ScheduledExecutorService scheduledExecutorService; @DubboReference private final RemoteConfigService remoteConfigService; @DubboReference private final RemoteTenantService remoteTenantService; @DubboReference private final RemoteClientService remoteClientService; @DubboReference private final RemoteSocialService remoteSocialService; @DubboReference(stub = "true") private final RemoteMessageService remoteMessageService; /** * 登录方法 * * @param body 登录信息 * @return 结果 */ @ApiEncrypt @PostMapping("/login") public R login(@RequestBody String body) { LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class); ValidatorUtils.validate(loginBody); // 授权类型和客户端id String clientId = loginBody.getClientId(); String grantType = loginBody.getGrantType(); RemoteClientVo clientVo = remoteClientService.queryByClientId(clientId); // 查询不到 client 或 client 内不包含 grantType if (ObjectUtil.isNull(clientVo) || !StringUtils.contains(clientVo.getGrantType(), grantType)) { log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType); return R.fail(MessageUtils.message("auth.grant.type.error")); } else if (!UserConstants.NORMAL.equals(clientVo.getStatus())) { return R.fail(MessageUtils.message("auth.grant.type.blocked")); } // 校验租户 sysLoginService.checkTenant(loginBody.getTenantId()); // 登录 LoginVo loginVo = IAuthStrategy.login(body, clientVo, grantType); Long userId = LoginHelper.getUserId(); scheduledExecutorService.schedule(() -> { try { remoteMessageService.sendMessage(userId, "欢迎登录RuoYi-Cloud-Plus微服务管理系统"); } catch (Exception ignored) { } }, 3, TimeUnit.SECONDS); return R.ok(loginVo); } /** * 第三方登录请求 * * @param source 登录来源 * @return 结果 */ @GetMapping("/binding/{source}") public R authBinding(@PathVariable("source") String source) { SocialLoginConfigProperties obj = socialProperties.getType().get(source); if (ObjectUtil.isNull(obj)) { return R.fail(source + "平台账号暂不支持"); } AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties); String authorizeUrl = authRequest.authorize(AuthStateUtils.createState()); return R.ok("操作成功", authorizeUrl); } /** * 第三方登录回调业务处理 绑定授权 * * @param loginBody 请求体 * @return 结果 */ @PostMapping("/social/callback") public R socialCallback(@RequestBody SocialLoginBody loginBody) { // 获取第三方登录信息 AuthResponse response = SocialUtils.loginAuth( loginBody.getSource(), loginBody.getSocialCode(), loginBody.getSocialState(), socialProperties); AuthUser authUserData = response.getData(); // 判断授权响应是否成功 if (!response.ok()) { return R.fail(response.getMsg()); } sysLoginService.socialRegister(authUserData); return R.ok(); } /** * 取消授权 * * @param socialId socialId */ @DeleteMapping(value = "/unlock/{socialId}") public R unlockSocial(@PathVariable Long socialId) { Boolean rows = remoteSocialService.deleteWithValidById(socialId); return rows ? R.ok() : R.fail("取消授权失败"); } /** * 登出方法 */ @PostMapping("logout") public R logout() { sysLoginService.logout(); return R.ok(); } /** * 用户注册 */ @ApiEncrypt @PostMapping("register") public R register(@RequestBody RegisterBody registerBody) { if (!remoteConfigService.selectRegisterEnabled(registerBody.getTenantId())) { return R.fail("当前系统没有开启注册功能!"); } // 用户注册 sysLoginService.register(registerBody); return R.ok(); } /** * 登录页面租户下拉框 * * @return 租户列表 */ @GetMapping("/tenant/list") public R tenantList(HttpServletRequest request) throws Exception { List tenantList = remoteTenantService.queryList(); List voList = MapstructUtils.convert(tenantList, TenantListVo.class); // 获取域名 String host; String referer = request.getHeader("referer"); if (StringUtils.isNotBlank(referer)) { // 这里从referer中取值是为了本地使用hosts添加虚拟域名,方便本地环境调试 host = referer.split("//")[1].split("/")[0]; } else { host = new URL(request.getRequestURL().toString()).getHost(); } // 根据域名进行筛选 List list = StreamUtils.filter(voList, vo -> StringUtils.equals(vo.getDomain(), host)); // 返回对象 LoginTenantVo vo = new LoginTenantVo(); vo.setVoList(CollUtil.isNotEmpty(list) ? list : voList); vo.setTenantEnabled(TenantHelper.isEnable()); return R.ok(vo); } }