接入 dingtalk 钉钉的三方登陆,流程未接入

This commit is contained in:
YunaiV
2021-10-02 18:31:05 +08:00
parent a56b4a7c9c
commit e05d90590e
15 changed files with 342 additions and 102 deletions

View File

@ -1,29 +1,22 @@
package cn.iocoder.yudao.adminserver.modules.system.controller.auth;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.adminserver.modules.system.enums.user.SysUserSocialTypeEnum;
import cn.iocoder.yudao.adminserver.modules.system.service.auth.SysUserSessionService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthLoginReqVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthLoginRespVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthMenuRespVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthPermissionInfoRespVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.*;
import cn.iocoder.yudao.adminserver.modules.system.convert.auth.SysAuthConvert;
import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.permission.SysMenuDO;
import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.permission.SysRoleDO;
import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.adminserver.modules.system.enums.permission.MenuTypeEnum;
import cn.iocoder.yudao.adminserver.modules.system.enums.user.SysUserSocialTypeEnum;
import cn.iocoder.yudao.adminserver.modules.system.service.auth.SysAuthService;
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService;
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysRoleService;
import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import com.aliyuncs.CommonResponse;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import com.xkcoding.justauth.AuthRequestFactory;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
@ -35,26 +28,18 @@ 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.commons.lang.StringUtils;
import org.quartz.SimpleTrigger;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriBuilder;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserRoleIds;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserRoleIds;
@Api(tags = "认证")
@RestController
@ -71,8 +56,6 @@ public class SysAuthController {
private SysRoleService roleService;
@Resource
private SysPermissionService permissionService;
@Resource
private SysUserSessionService sysUserSessionService;
@Resource
private AuthRequestFactory authRequestFactory;
@ -86,42 +69,6 @@ public class SysAuthController {
return success(SysAuthLoginRespVO.builder().token(token).build());
}
@GetMapping("/third-login-redirect")
@ApiOperation("三方登陆的跳转")
@ApiImplicitParams({
@ApiImplicitParam(name = "type", value = "三方类型", required = true, dataTypeClass = Integer.class),
@ApiImplicitParam(name = "redirectUri", value = "回调路径", dataTypeClass = String.class)
})
public CommonResult<String> login(@RequestParam("type") Integer type,
@RequestParam("redirectUri") String redirectUri) throws IOException {
// 获得对应的 AuthRequest 实现
AuthRequest authRequest = authRequestFactory.get(SysUserSocialTypeEnum.valueOfType(type).getSource());
// 生成跳转地址
String authorizeUri = authRequest.authorize(AuthStateUtils.createState());
authorizeUri = HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri);
// authorizeUri = UrlBuilder.fromBaseUrl(authorizeUri).queryParam("redirect_uri", redirectUri).build();
return CommonResult.success(authorizeUri);
}
@RequestMapping("/{type}/callback")
public AuthResponse login(@PathVariable String type, AuthCallback callback) {
AuthRequest authRequest = authRequestFactory.get(type);
AuthResponse<AuthUser> response = authRequest.login(callback);
log.info("【response】= {}", JSONUtil.toJsonStr(response));
return response;
}
// @RequestMapping("/auth2/login/{oauthType}")
// @ApiOperation("第三方登录")
// @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
// public CommonResult<SysAuthLoginRespVO> login(@PathVariable String oauthType) {
// Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// //TODO NPE
// String token = sysUserSessionService.getSessionId(authentication.getName());
// // 返回结果
// return success(SysAuthLoginRespVO.builder().token(token).build());
// }
@GetMapping("/get-permission-info")
@ApiOperation("获取登陆用户的权限信息")
public CommonResult<SysAuthPermissionInfoRespVO> getPermissionInfo() {
@ -153,4 +100,38 @@ public class SysAuthController {
return success(SysAuthConvert.INSTANCE.buildMenuTree(menuList));
}
// ========== 三方登陆相关 ==========
@GetMapping("/third-login-redirect")
@ApiOperation("三方登陆的跳转")
@ApiImplicitParams({
@ApiImplicitParam(name = "type", value = "三方类型", required = true, dataTypeClass = Integer.class),
@ApiImplicitParam(name = "redirectUri", value = "回调路径", dataTypeClass = String.class)
})
public CommonResult<String> thirdLoginRedirect(@RequestParam("type") Integer type,
@RequestParam("redirectUri") String redirectUri) {
// 获得对应的 AuthRequest 实现
AuthRequest authRequest = authRequestFactory.get(SysUserSocialTypeEnum.valueOfType(type).getSource());
// 生成跳转地址
String authorizeUri = authRequest.authorize(AuthStateUtils.createState());
authorizeUri = HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri);
return CommonResult.success(authorizeUri);
}
@PostMapping("/third-login")
@ApiOperation("三方登陆,使用 code 授权码")
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
public CommonResult<SysAuthLoginRespVO> thirdLogin(@RequestBody @Valid SysAuthThirdLoginReqVO reqVO) {
String token = authService.thirdLogin(reqVO, getClientIP(), getUserAgent());
return null;
}
@RequestMapping("/{type}/callback")
public AuthResponse login(@PathVariable String type, AuthCallback callback) {
AuthRequest authRequest = authRequestFactory.get(type);
AuthResponse<AuthUser> response = authRequest.login(callback);
log.info("【response】= {}", JSONUtil.toJsonStr(response));
return response;
}
}

View File

@ -0,0 +1,37 @@
package cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth;
import cn.iocoder.yudao.adminserver.modules.system.enums.user.SysUserSocialTypeEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
@ApiModel("三方登陆 Request VO使用 code 授权码")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SysAuthThirdLoginReqVO {
@ApiModelProperty(value = "三方平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
@InEnum(SysUserSocialTypeEnum.class)
@NotNull(message = "三方平台的类型不能为空")
private Integer type;
@ApiModelProperty(value = "授权码", required = true, example = "1024")
@NotEmpty(message = "授权码不能为空")
private String code;
@ApiModelProperty(value = "state", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
@NotEmpty(message = "state 不能为空")
private String state;
}

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.adminserver.modules.system.convert.auth;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthMenuRespVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthPermissionInfoRespVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthThirdLoginReqVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.profile.SysUserProfileUpdatePasswordReqVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.profile.SysUserProfileUpdateReqVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.user.SysUserCreateReqVO;
@ -11,6 +12,7 @@ import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysUserDO
import cn.iocoder.yudao.adminserver.modules.system.enums.permission.MenuIdEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthUser;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@ -43,6 +45,8 @@ public interface SysAuthConvert {
LoginUser convert(SysUserProfileUpdatePasswordReqVO reqVO);
AuthCallback convert(SysAuthThirdLoginReqVO bean);
/**
* 将菜单列表,构建成菜单树
*

View File

@ -41,6 +41,7 @@ public class SysUserSocialDO extends BaseDO {
* 三方平台的类型
*/
private SysUserSocialTypeEnum type;
/**
* 三方 openid
*/
@ -56,6 +57,10 @@ public class SysUserSocialDO extends BaseDO {
* 如果没有 unionId 的平台,直接使用 openid 作为该字段的值
*/
private String unionId;
/**
* 原始 Token 数据,一般是 JSON 格式
*/
private String rawTokenInfo;
/**
* 用户昵称
@ -66,9 +71,9 @@ public class SysUserSocialDO extends BaseDO {
*/
private String avatar;
/**
* 原始数据,一般是 JSON 格式
* 原始用户数据,一般是 JSON 格式
*/
private String info;
private String rawUserInfo;
}

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.adminserver.modules.system.enums.user;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.adminserver.modules.system.enums.errorcode.SysErrorCodeTypeEnum;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import com.fasterxml.jackson.annotation.JsonCreator;
import jodd.util.ArraysUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -19,6 +20,7 @@ import java.util.Arrays;
public enum SysUserSocialTypeEnum implements IntArrayValuable {
GITEE(10, "GITEE"), // https://gitee.com/api/v5/oauth_doc#/
DINGTALK(20, "DINGTALK"), // https://developers.dingtalk.com/document/app/obtain-identity-credentials
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysUserSocialTypeEnum::getType).toArray();

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.adminserver.modules.system.service.auth;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthThirdLoginReqVO;
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthLoginReqVO;
@ -22,4 +23,14 @@ public interface SysAuthService extends SecurityAuthFrameworkService {
*/
String login(SysAuthLoginReqVO reqVO, String userIp, String userAgent);
/**
* 三方登陆用户,使用 code 授权码
*
* @param reqVO 登陆信息
* @param userIp 用户 IP
* @param userAgent 用户 UA
* @return 身份令牌,使用 JWT 方式
*/
String thirdLogin(SysAuthThirdLoginReqVO reqVO, String userIp, String userAgent);
}

View File

@ -1,7 +1,11 @@
package cn.iocoder.yudao.adminserver.modules.system.service.auth.impl;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthThirdLoginReqVO;
import cn.iocoder.yudao.adminserver.modules.system.enums.user.SysUserSocialTypeEnum;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthLoginReqVO;
@ -17,7 +21,12 @@ import cn.iocoder.yudao.adminserver.modules.system.service.logger.SysLoginLogSer
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService;
import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import com.xkcoding.justauth.AuthRequestFactory;
import lombok.extern.slf4j.Slf4j;
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 org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
@ -60,6 +69,9 @@ public class SysAuthServiceImpl implements SysAuthService {
@Resource
private SysUserSessionService userSessionService;
@Resource
private AuthRequestFactory authRequestFactory;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 获取 username 对应的 SysUserDO
@ -97,6 +109,24 @@ public class SysAuthServiceImpl implements SysAuthService {
return userSessionService.createUserSession(loginUser, userIp, userAgent);
}
@Override
public String thirdLogin(SysAuthThirdLoginReqVO reqVO, String userIp, String userAgent) {
// 使用 code 授权码,进行登陆
AuthRequest authRequest = authRequestFactory.get(SysUserSocialTypeEnum.valueOfType(reqVO.getType()).getSource());
AuthCallback authCallback = SysAuthConvert.INSTANCE.convert(reqVO);
AuthResponse<?> authResponse = authRequest.login(authCallback);
log.info("[thirdLogin][请求三方平台 type({}) request({}) response({})]", reqVO.getType(), JsonUtils.toJsonString(authCallback),
JsonUtils.toJsonString(authResponse));
if (!authResponse.ok()) {
throw new RuntimeException(""); // TODO 芋艿:补全
}
AuthUser authUser = (AuthUser) authResponse.getData();
// 查找到对应的
return null;
}
private void verifyCaptcha(String username, String captchaUUID, String captchaCode) {
String code = captchaService.getCaptchaCode(captchaUUID);
// 验证码不存在

View File

@ -173,8 +173,12 @@ justauth:
type:
GITEE:
client-id: 6bb0b37a8a017e5e2dc4c34ca4756dcf80e8e392585e7035d3ede7a6db50426e
client-secret: f117b9de5e9267bcd48db83d4cb078ea8cf9a5d17cda83481e3d9090df3fa01d
client-secret: ba9f1f42e77be71f461b54da83b6f4b45a052dd7f93418f00f91f4e6934dfd1f
ignore-check-redirect-uri: true
# redirect-uri: http://127.0.0.1:48080/api/gitee/callback
DINGTALK:
client-id: dingvrnreaje3yqvzhxg
client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI
ignore-check-redirect-uri: true
cache:
type: default