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.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.aliyun.dingtalkcontact_1_0.models.GetUserHeaders; import com.aliyun.dingtalkcontact_1_0.models.GetUserResponseBody; import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenRequest; import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenResponse; import com.aliyun.teautil.models.RuntimeOptions; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.dingtalk.api.DefaultDingTalkClient; import com.dingtalk.api.DingTalkClient; import com.dingtalk.api.request.*; import com.dingtalk.api.response.OapiUserGetbyunionidResponse; import com.dingtalk.api.response.OapiV2UserGetResponse; import com.taobao.api.ApiException; 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.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.domain.model.RegisterBody; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MessageUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.redis.utils.RedisUtils; 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.system.domain.SysClient; import org.dromara.system.domain.SysUser; import org.dromara.system.domain.bo.SysTenantBo; import org.dromara.system.domain.vo.SysTenantVo; import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.service.*; import org.dromara.system.uitil.AutoLoginUtil; import org.dromara.web.domain.vo.LoginTenantVo; import org.dromara.web.domain.vo.LoginVo; import org.dromara.web.domain.vo.TenantListVo; import org.dromara.web.service.IAuthStrategy; import org.dromara.web.service.SysLoginService; import org.dromara.web.service.SysRegisterService; import org.dromara.web.utils.RZTHttpUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import com.aliyun.teaopenapi.models.Config; import java.net.URL; import java.time.Duration; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 认证 * * @author Lion Li */ @Slf4j @SaIgnore @Validated @RequiredArgsConstructor @RestController @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 ISysSocialService socialUserService; private final ISysClientService clientService; private final ISysUserService userService; private final SysUserMapper userMapper; private final RZTHttpUtils rzthttpUtils; /** * 登录方法 * * @param loginBody 登录信息 * @return 结果 */ @PostMapping("/login") public R login(@Validated @RequestBody LoginBody loginBody) { // 授权类型和客户端id String clientId = loginBody.getClientId(); String grantType = loginBody.getGrantType(); SysClient client = clientService.queryByClientId(clientId); // 查询不到 client 或 client 内不包含 grantType if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) { log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType); return R.fail(MessageUtils.message("auth.grant.type.error")); } else if (!UserConstants.NORMAL.equals(client.getStatus())) { return R.fail(MessageUtils.message("auth.grant.type.blocked")); } // 校验租户 loginService.checkTenant(loginBody.getTenantId()); // 登录 return R.ok(IAuthStrategy.login(loginBody, client)); } /** * 第三方登录请求 * * @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 LoginBody loginBody) { // 获取第三方登录信息 AuthResponse response = SocialUtils.loginAuth(loginBody, socialProperties); AuthUser authUserData = response.getData(); // 判断授权响应是否成功 if (!response.ok()) { return R.fail(response.getMsg()); } loginService.socialRegister(authUserData); return R.ok(); } /** * 取消授权 * * @param socialId socialId */ @DeleteMapping(value = "/unlock/{socialId}") public R unlockSocial(@PathVariable Long socialId) { Boolean rows = socialUserService.deleteWithValidById(socialId); return rows ? R.ok() : R.fail("取消授权失败"); } /** * 退出登录 */ @PostMapping("/logout") public R logout() { loginService.logout(); return R.ok("退出成功"); } /** * 用户注册 */ @PostMapping("/register") public R register(@Validated @RequestBody RegisterBody user) { if (!configService.selectRegisterEnabled(user.getTenantId())) { return R.fail("当前系统没有开启注册功能!"); } registerService.register(user); return R.ok(); } /** * 登录页面租户下拉框 * * @return 租户列表 */ @GetMapping("/tenant/list") public R tenantList(HttpServletRequest request) throws Exception { List tenantList = tenantService.queryList(new SysTenantBo()); 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); } /** * 自动注册并登录 */ // @SaCheckPermission("system:user:auto") @GetMapping(value = "auto") public R auto(RegisterBody user) { AutoLoginUtil.padding(user); String clientId = "e5cd7e4891bf95d1d19206ce24a7b32e"; //pc SysClient client = clientService.queryByClientId(clientId); //检验是否存在账号 boolean exists = userMapper.exists(new LambdaQueryWrapper() .eq(SysUser::getUserName, user.getUsername()) .eq(SysUser::getStatus, "0") .eq(SysUser::getDelFlag, "0")); if (!exists) { registerService.register(user); } LoginBody loginBody = AutoLoginUtil.create(user, clientId); // 登录 return R.ok(IAuthStrategy.login(loginBody, client)); } public static com.aliyun.dingtalkoauth2_1_0.Client authClient() throws Exception { Config config = new Config(); config.protocol = "https"; config.regionId = "central"; return new com.aliyun.dingtalkoauth2_1_0.Client(config); } /** * 获取用户token * * @param authCode * @return * @throws Exception */ //接口地址:注意/auth与钉钉登录与分享的回调域名地址一致 @RequestMapping(value = "/dingdingLogin", method = RequestMethod.GET) public String getAccessToken(@RequestParam(value = "authCode") String authCode, @RequestParam(value = "code") String code) throws Exception { com.aliyun.dingtalkoauth2_1_0.Client client = authClient(); GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest() //应用基础信息-应用信息的AppKey,请务必替换为开发的应用AppKey .setClientId("dingl5dxahaj3uzfug66") //应用基础信息-应用信息的AppSecret,,请务必替换为开发的应用AppSecret .setClientSecret("AGDu7NfzkverlMXq8CUDiy6EXx5jSL4v2p-Odz1mpXV5e4_K3kB1Acat0RftzBXC") .setCode(authCode) .setGrantType("authorization_code"); GetUserTokenResponse getUserTokenResponse = client.getUserToken(getUserTokenRequest); System.out.println(getUserTokenResponse.getBody()); if ("dingf4816bf92d85d84435c2f4657eb6378f".equals(getUserTokenResponse.getBody().getCorpId())) { //获取用户个人token String accessToken = getUserTokenResponse.getBody().getAccessToken(); GetUserResponseBody userResponseBody = getUserinfoNoId(accessToken); return userResponseBody.mobile; } return ""; } public static com.aliyun.dingtalkcontact_1_0.Client contactClient() throws Exception { Config config = new Config(); config.protocol = "https"; config.regionId = "central"; return new com.aliyun.dingtalkcontact_1_0.Client(config); } /** * 获取用户个人信息 * * @param accessToken * @return * @throws Exception */ public GetUserResponseBody getUserinfoNoId(String accessToken) throws Exception { com.aliyun.dingtalkcontact_1_0.Client client = contactClient(); GetUserHeaders getUserHeaders = new GetUserHeaders(); getUserHeaders.xAcsDingtalkAccessToken = accessToken; //获取用户个人信息,如需获取当前授权人的信息,unionId参数必须传me GetUserResponseBody userResponse = client.getUserWithOptions("me", getUserHeaders, new RuntimeOptions()).getBody(); return userResponse; } public String getUserId(String accessToken, String unionId) throws ApiException { DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid"); OapiUserGetbyunionidRequest req = new OapiUserGetbyunionidRequest(); req.setUnionid(unionId); OapiUserGetbyunionidResponse rsp = client.execute(req, accessToken); if (rsp.isSuccess()) { return rsp.getResult().getUserid(); } return null; } public OapiV2UserGetResponse.UserGetResponse getUserinfo(String accessToken, String userId) throws Exception { DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get"); OapiV2UserGetRequest req = new OapiV2UserGetRequest(); req.setUserid(userId); req.setLanguage("en_US"); OapiV2UserGetResponse rsp = client.execute(req, accessToken); return rsp.getResult(); } final String redisKey = "RZT_TOKEN"; @RequestMapping(value = "/rztLogin", method = RequestMethod.GET) public String getRztAccessToken(@RequestParam(value = "code") String code) { try { log.info("code值:{}", code); String accessToken = getRztToken(); if (StrUtil.isNotBlank(accessToken)) { Map hashMap = new HashMap<>(); hashMap.put("code", code); hashMap.put("token", accessToken); String userJson = rzthttpUtils.sendGetRequest("/login/info", hashMap); log.info("用户信息:{}", userJson); JSONObject userObject = JSON.parseObject(userJson); if (userObject.getIntValue("errcode") == 0) { return userObject.getString("userid"); } else { log.error("获取用户信息失败:{}", userObject.getString("errmsg")); } } } catch (Exception e) { log.error("获取用户信息异常:{}", e.getMessage()); } return ""; } private String getRztToken() throws Exception { String token = RedisUtils.getCacheObject(redisKey); if (StrUtil.isEmpty(token)) { String corpId = "ww9904fd98c1b0df9e"; String corpSecret = "mZdTP-ULDWHEPgFCpl62OwudbP3bODgqN9lC-rUtNSA"; String accessJson = rzthttpUtils.sendGetRequest("/gettoken?corpId=" + corpId + "&corpsecret=" + corpSecret, null); JSONObject jsonObject = JSON.parseObject(accessJson); if (jsonObject.getIntValue("errcode") == 0) { String accessToken = jsonObject.getString("access_token"); Integer expiresIn = jsonObject.getIntValue("expires_in"); RedisUtils.setCacheObject(redisKey, accessToken, Duration.ofSeconds(expiresIn)); return accessToken; } else { log.error("获取token失败:{},{}", jsonObject.getIntValue("errcode"), jsonObject.getString("errmsg")); return null; } } else { return token; } } }