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<LoginVo> 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<String> 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<Void> socialCallback(@RequestBody LoginBody loginBody) {
|
// 获取第三方登录信息
|
AuthResponse<AuthUser> 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<Void> unlockSocial(@PathVariable Long socialId) {
|
Boolean rows = socialUserService.deleteWithValidById(socialId);
|
return rows ? R.ok() : R.fail("取消授权失败");
|
}
|
|
|
/**
|
* 退出登录
|
*/
|
@PostMapping("/logout")
|
public R<Void> logout() {
|
loginService.logout();
|
return R.ok("退出成功");
|
}
|
|
/**
|
* 用户注册
|
*/
|
@PostMapping("/register")
|
public R<Void> 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<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
|
List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo());
|
List<TenantListVo> 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<TenantListVo> 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<LoginVo> auto(RegisterBody user) {
|
AutoLoginUtil.padding(user);
|
String clientId = "e5cd7e4891bf95d1d19206ce24a7b32e";
|
//pc
|
SysClient client = clientService.queryByClientId(clientId);
|
//检验是否存在账号
|
boolean exists = userMapper.exists(new LambdaQueryWrapper<SysUser>()
|
.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<String, String> 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;
|
}
|
}
|
}
|