初步完成微信公众号登录

This commit is contained in:
timfruit 2021-10-26 21:35:06 +08:00
parent d194e14780
commit fcf1c228c5
28 changed files with 275 additions and 144 deletions

View File

@ -122,12 +122,6 @@
<artifactId>screw-core</artifactId> <!-- 实现数据库文档 -->
</dependency>
<!-- TODO 后续看情况,进行调整 -->
<dependency>
<groupId>com.xkcoding.justauth</groupId>
<artifactId>justauth-spring-boot-starter</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
<build>

View File

@ -8,10 +8,11 @@ import cn.iocoder.yudao.adminserver.modules.system.enums.permission.MenuTypeEnum
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.social.SysSocialService;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService;
import cn.iocoder.yudao.coreservice.modules.system.service.user.SysUserCoreService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
@ -132,7 +133,7 @@ public class SysAuthController {
@DeleteMapping("/social-unbind")
@ApiOperation("取消社交绑定")
public CommonResult<Boolean> socialUnbind(@RequestBody SysAuthSocialUnbindReqVO reqVO) {
socialService.unbindSocialUser(getLoginUserId(), reqVO.getType(), reqVO.getUnionId());
socialService.unbindSocialUser(getLoginUserId(), reqVO.getType(), reqVO.getUnionId(), UserTypeEnum.ADMIN);
return CommonResult.success(true);
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth;
import cn.iocoder.yudao.adminserver.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth;
import cn.iocoder.yudao.adminserver.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth;
import cn.iocoder.yudao.adminserver.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

View File

@ -19,12 +19,5 @@ public interface SysRedisKeyConstants {
"captcha_code:%s", // 参数为 uuid
STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
RedisKeyDefine SOCIAL_AUTH_USER = new RedisKeyDefine("社交的授权用户",
"social_auth_user:%d:%s", // 参数为 typecode
STRING, AuthUser.class, Duration.ofDays(1));
RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交的 state",
"social_auth_state:%s", // 参数为 state
STRING, String.class, Duration.ofHours(24)); // 值为 state
}

View File

@ -90,8 +90,6 @@ public interface SysErrorCodeConstants {
ErrorCode ERROR_CODE_NOT_EXISTS = new ErrorCode(1002013000, "错误码不存在");
ErrorCode ERROR_CODE_DUPLICATE = new ErrorCode(1002013001, "已经存在编码为【{}】的错误码");
// ========== 社交模块 1002014000 ==========
ErrorCode SOCIAL_AUTH_FAILURE = new ErrorCode(1002014000, "社交授权失败,原因是:{}");
ErrorCode SOCIAL_UNBIND_NOT_SELF = new ErrorCode(1002014001, "社交解绑失败,非当前用户绑定");
}

View File

@ -6,18 +6,18 @@ import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAu
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthSocialLogin2ReqVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthSocialLoginReqVO;
import cn.iocoder.yudao.adminserver.modules.system.convert.auth.SysAuthConvert;
import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.adminserver.modules.system.enums.logger.SysLoginLogTypeEnum;
import cn.iocoder.yudao.adminserver.modules.system.enums.logger.SysLoginResultEnum;
import cn.iocoder.yudao.adminserver.modules.system.service.auth.SysAuthService;
import cn.iocoder.yudao.adminserver.modules.system.service.common.SysCaptchaService;
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService;
import cn.iocoder.yudao.adminserver.modules.system.service.social.SysSocialService;
import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService;
import cn.iocoder.yudao.coreservice.modules.system.service.user.SysUserCoreService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
@ -75,6 +75,8 @@ public class SysAuthServiceImpl implements SysAuthService {
@Resource
private SysSocialService socialService;
private static final UserTypeEnum userTypeEnum = UserTypeEnum.ADMIN;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 获取 username 对应的 SysUserDO
@ -198,7 +200,7 @@ public class SysAuthServiceImpl implements SysAuthService {
// 如果未绑定 SysSocialUserDO 用户则无法自动登录进行报错
String unionId = socialService.getAuthUserUnionId(authUser);
List<SysSocialUserDO> socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId);
List<SysSocialUserDO> socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, userTypeEnum);
if (CollUtil.isEmpty(socialUsers)) {
throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
}
@ -216,7 +218,7 @@ public class SysAuthServiceImpl implements SysAuthService {
loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
// 绑定社交用户更新
socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser);
socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, userTypeEnum);
// 缓存登录用户到 Redis 返回 sessionId 编号
return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
@ -233,7 +235,7 @@ public class SysAuthServiceImpl implements SysAuthService {
loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
// 绑定社交用户新增
socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser);
socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, userTypeEnum);
// 缓存登录用户到 Redis 返回 sessionId 编号
return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
@ -246,7 +248,7 @@ public class SysAuthServiceImpl implements SysAuthService {
Assert.notNull(authUser, "授权用户不为空");
// 绑定社交用户新增
socialService.bindSocialUser(userId, reqVO.getType(), authUser);
socialService.bindSocialUser(userId, reqVO.getType(), authUser, userTypeEnum);
}
@Override
@ -267,7 +269,7 @@ public class SysAuthServiceImpl implements SysAuthService {
reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType());
reqDTO.setTraceId(TracerUtils.getTraceId());
reqDTO.setUserId(userId);
reqDTO.setUserType(UserTypeEnum.ADMIN.getValue());
reqDTO.setUserType(userTypeEnum.getValue());
reqDTO.setUsername(username);
reqDTO.setUserAgent(ServletUtils.getUserAgent());
reqDTO.setUserIp(ServletUtils.getClientIP());

View File

@ -85,6 +85,12 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.xkcoding.justauth</groupId>
<artifactId>justauth-spring-boot-starter</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
</project>

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social;
package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.adminserver.modules.system.dal.mysql.social;
package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.social;
import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;

View File

@ -2,6 +2,9 @@ package cn.iocoder.yudao.coreservice.modules.system.dal.redis;
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import me.zhyd.oauth.model.AuthUser;
import java.time.Duration;
import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;
@ -16,4 +19,11 @@ public interface SysRedisKeyCoreConstants {
"login_user:%s", // 参数为 sessionId
STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
RedisKeyDefine SOCIAL_AUTH_USER = new RedisKeyDefine("社交的授权用户",
"social_auth_user:%d:%s", // 参数为 typecode
STRING, AuthUser.class, Duration.ofDays(1));
RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交的 state",
"social_auth_state:%s", // 参数为 state
STRING, String.class, Duration.ofHours(24)); // 值为 state
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.adminserver.modules.system.dal.redis.social;
package cn.iocoder.yudao.coreservice.modules.system.dal.redis.social;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import me.zhyd.oauth.model.AuthCallback;
@ -8,10 +8,11 @@ import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import static cn.iocoder.yudao.adminserver.modules.system.dal.redis.SysRedisKeyConstants.SOCIAL_AUTH_USER;
import static cn.iocoder.yudao.coreservice.modules.system.dal.redis.SysRedisKeyCoreConstants.SOCIAL_AUTH_USER;
/**
* 社交 {@link me.zhyd.oauth.model.AuthUser} RedisDAO
* 社交 {@link AuthUser} RedisDAO
*
* @author 芋道源码
*/

View File

@ -14,4 +14,9 @@ public interface SysErrorCodeConstants {
ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1006000001, "模板参数({})缺失");
ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1006000000, "短信模板不存在");
// ========== 社交模块 1006001000 ==========
ErrorCode SOCIAL_AUTH_FAILURE = new ErrorCode(1006001000, "社交授权失败,原因是:{}");
ErrorCode SOCIAL_UNBIND_NOT_SELF = new ErrorCode(1006001001, "社交解绑失败,非当前用户绑定");
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.adminserver.modules.system.enums.social;
package cn.iocoder.yudao.coreservice.modules.system.enums.social;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
@ -21,11 +21,19 @@ public enum SysSocialTypeEnum implements IntArrayValuable {
GITEE(10, "GITEE"), // https://gitee.com/api/v5/oauth_doc#/
DINGTALK(20, "DINGTALK"), // https://developers.dingtalk.com/document/app/obtain-identity-credentials
WECHAT_ENTERPRISE(30, "WECHAT_ENTERPRISE"), // https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html
/**
* 微信公众平台 - H5
*/
WECHAT_MP(12, "WECHAT_MP"), // https://www.cnblogs.com/juewuzhe/p/11905461.html
/**
* 微信开放平台 - 小程序
*/
WECHAT_OPEN(11, "WECHAT_OPEN"), // https://justauth.wiki/guide/oauth/wechat_open/#_2-%E7%94%B3%E8%AF%B7%E5%BC%80%E5%8F%91%E8%80%85%E8%B5%84%E8%B4%A8%E8%AE%A4%E8%AF%81
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSocialTypeEnum::getType).toArray();
public static final List<Integer> WECHAT_ALL = ListUtil.toList(WECHAT_ENTERPRISE.type);
public static final List<Integer> WECHAT_ALL = ListUtil.toList(WECHAT_ENTERPRISE.type, WECHAT_MP.type, WECHAT_OPEN.type);
/**
* 类型

View File

@ -1,8 +1,9 @@
package cn.iocoder.yudao.adminserver.modules.system.service.social;
package cn.iocoder.yudao.coreservice.modules.system.service.social;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.adminserver.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import me.zhyd.oauth.model.AuthUser;
@ -49,7 +50,7 @@ public interface SysSocialService {
* @param unionId 社交平台的 unionId
* @return 社交用户列表
*/
List<SysSocialUserDO> getAllSocialUserList(Integer type, String unionId);
List<SysSocialUserDO> getAllSocialUserList(Integer type, String unionId, UserTypeEnum userTypeEnum);
/**
* 获得指定用户的社交用户列表
@ -57,7 +58,7 @@ public interface SysSocialService {
* @param userId 用户编号
* @return 社交用户列表
*/
List<SysSocialUserDO> getSocialUserList(Long userId);
List<SysSocialUserDO> getSocialUserList(Long userId,UserTypeEnum userTypeEnum);
/**
* 绑定社交用户
@ -66,7 +67,7 @@ public interface SysSocialService {
* @param type 社交平台的类型 {@link SysSocialTypeEnum}
* @param authUser 授权用户
*/
void bindSocialUser(Long userId, Integer type, AuthUser authUser);
void bindSocialUser(Long userId, Integer type, AuthUser authUser, UserTypeEnum userTypeEnum);
/**
* 取消绑定社交用户
@ -75,6 +76,6 @@ public interface SysSocialService {
* @param type 社交平台的类型 {@link SysSocialTypeEnum}
* @param unionId 社交平台的 unionId
*/
void unbindSocialUser(Long userId, Integer type, String unionId);
void unbindSocialUser(Long userId, Integer type, String unionId,UserTypeEnum userTypeEnum);
}

View File

@ -1,11 +1,11 @@
package cn.iocoder.yudao.adminserver.modules.system.service.social.impl;
package cn.iocoder.yudao.coreservice.modules.system.service.social.impl;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.adminserver.modules.system.dal.mysql.social.SysSocialUserMapper;
import cn.iocoder.yudao.adminserver.modules.system.dal.redis.social.SysSocialAuthUserRedisDAO;
import cn.iocoder.yudao.adminserver.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.adminserver.modules.system.service.social.SysSocialService;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.social.SysSocialUserMapper;
import cn.iocoder.yudao.coreservice.modules.system.dal.redis.social.SysSocialAuthUserRedisDAO;
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
@ -25,8 +25,8 @@ import javax.validation.Valid;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.SOCIAL_AUTH_FAILURE;
import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.SOCIAL_UNBIND_NOT_SELF;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.SOCIAL_AUTH_FAILURE;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.SOCIAL_UNBIND_NOT_SELF;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
@ -75,25 +75,25 @@ public class SysSocialServiceImpl implements SysSocialService {
}
@Override
public List<SysSocialUserDO> getAllSocialUserList(Integer type, String unionId) {
public List<SysSocialUserDO> getAllSocialUserList(Integer type, String unionId,UserTypeEnum userTypeEnum) {
List<Integer> types = SysSocialTypeEnum.getRelationTypes(type);
return socialUserMapper.selectListByTypeAndUnionId(UserTypeEnum.ADMIN.getValue(), types, unionId);
return socialUserMapper.selectListByTypeAndUnionId(userTypeEnum.getValue(), types, unionId);
}
@Override
public List<SysSocialUserDO> getSocialUserList(Long userId) {
return socialUserMapper.selectListByUserId(UserTypeEnum.ADMIN.getValue(), userId);
public List<SysSocialUserDO> getSocialUserList(Long userId,UserTypeEnum userTypeEnum) {
return socialUserMapper.selectListByUserId(userTypeEnum.getValue(), userId);
}
@Override
@Transactional
public void bindSocialUser(Long userId, Integer type, AuthUser authUser) {
public void bindSocialUser(Long userId, Integer type, AuthUser authUser,UserTypeEnum userTypeEnum) {
// 获得 unionId 对应的 SysSocialUserDO 列表
String unionId = getAuthUserUnionId(authUser);
List<SysSocialUserDO> socialUsers = this.getAllSocialUserList(type, unionId);
List<SysSocialUserDO> socialUsers = this.getAllSocialUserList(type, unionId, userTypeEnum);
// 逻辑一如果 userId 之前绑定过该 type 的其它账号需要进行解绑
this.unbindOldSocialUser(userId, type, unionId);
this.unbindOldSocialUser(userId, type, unionId, userTypeEnum);
// 逻辑二如果 socialUsers 指定的 userId 改变需要进行更新
// 例如说一个微信 unionId 对应了多个社交账号结果其中有个关联了新的 userId则其它也要跟着修改
@ -112,7 +112,7 @@ public class SysSocialServiceImpl implements SysSocialService {
.nickname(authUser.getNickname()).avatar(authUser.getAvatar()).rawUserInfo(toJsonString(authUser.getRawUserInfo()))
.build();
if (socialUser == null) {
saveSocialUser.setUserId(userId).setUserType(UserTypeEnum.ADMIN.getValue())
saveSocialUser.setUserId(userId).setUserType(userTypeEnum.getValue())
.setType(type).setOpenid(authUser.getUuid()).setUnionId(unionId);
socialUserMapper.insert(saveSocialUser);
} else {
@ -122,9 +122,9 @@ public class SysSocialServiceImpl implements SysSocialService {
}
@Override
public void unbindSocialUser(Long userId, Integer type, String unionId) {
public void unbindSocialUser(Long userId, Integer type, String unionId, UserTypeEnum userTypeEnum) {
// 获得 unionId 对应的所有 SysSocialUserDO 社交用户
List<SysSocialUserDO> socialUsers = this.getAllSocialUserList(type, unionId);
List<SysSocialUserDO> socialUsers = this.getAllSocialUserList(type, unionId, userTypeEnum);
if (CollUtil.isEmpty(socialUsers)) {
return;
}
@ -140,10 +140,10 @@ public class SysSocialServiceImpl implements SysSocialService {
}
@VisibleForTesting
public void unbindOldSocialUser(Long userId, Integer type, String newUnionId) {
public void unbindOldSocialUser(Long userId, Integer type, String newUnionId, UserTypeEnum userTypeEnum) {
List<Integer> types = SysSocialTypeEnum.getRelationTypes(type);
List<SysSocialUserDO> oldSocialUsers = socialUserMapper.selectListByTypeAndUserId(
UserTypeEnum.ADMIN.getValue(), types, userId);
userTypeEnum.getValue(), types, userId);
// 如果新老的 unionId 是一致的说明无需解绑
if (CollUtil.isEmpty(oldSocialUsers) || Objects.equals(newUnionId, oldSocialUsers.get(0).getUnionId())) {
return;

View File

@ -1,55 +0,0 @@
package cn.iocoder.yudao.userserver.modules.member.enums.social;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
/**
* 社交平台的类型枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum SysSocialTypeEnum implements IntArrayValuable {
GITEE(10, "GITEE"), // https://gitee.com/api/v5/oauth_doc#/
DINGTALK(20, "DINGTALK"), // https://developers.dingtalk.com/document/app/obtain-identity-credentials
WECHAT_ENTERPRISE(30, "WECHAT_ENTERPRISE"), // https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSocialTypeEnum::getType).toArray();
public static final List<Integer> WECHAT_ALL = ListUtil.toList(WECHAT_ENTERPRISE.type);
/**
* 类型
*/
private final Integer type;
/**
* 类型的标识
*/
private final String source;
@Override
public int[] array() {
return ARRAYS;
}
public static SysSocialTypeEnum valueOfType(Integer type) {
return ArrayUtil.firstMatch(o -> o.getType().equals(type), values());
}
public static List<Integer> getRelationTypes(Integer type) {
if (WECHAT_ALL.contains(type)) {
return WECHAT_ALL;
}
return ListUtil.toList(type);
}
}

View File

@ -1,9 +1,13 @@
package cn.iocoder.yudao.userserver.modules.system.controller.auth;
import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*;
import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
@ -13,11 +17,13 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
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;
@Api(tags = "认证")
@RestController
@ -30,6 +36,9 @@ public class SysAuthController {
private SysAuthService authService;
@Resource
private SysSmsCodeService smsCodeService;
@Resource
private SysSocialService socialService;
@PostMapping("/login")
@ApiOperation("使用手机 + 密码登录")
@ -70,42 +79,47 @@ public class SysAuthController {
})
public CommonResult<String> socialAuthRedirect(@RequestParam("type") Integer type,
@RequestParam("redirectUri") String redirectUri) {
// return CommonResult.success(socialService.getAuthorizeUrl(type, redirectUri));
return null;
return CommonResult.success(socialService.getAuthorizeUrl(type, redirectUri));
}
@GetMapping("/social-login-get")
@ApiOperation("社交登录,使用 code 授权码")
@ResponseBody
@Deprecated
public CommonResult<MbrAuthSocialLoginReqVO> socialLoginGet(HttpServletRequest request,String code,String state) {
// 返回结果
MbrAuthSocialLoginReqVO reqVO = MbrAuthSocialLoginReqVO.builder().state(state).code(code).build();
reqVO.setType(12);
System.out.println(JSON.toJSON(reqVO));
return success(reqVO);
}
@PostMapping("/social-login")
@ApiOperation("社交登录,使用 code 授权码")
public CommonResult<SysAuthLoginRespVO> socialLogin(@RequestBody @Valid MbrAuthSocialLoginReqVO reqVO) {
// String token = authService.socialLogin(reqVO, getClientIP(), getUserAgent());
// // 返回结果
// return success(MbrAuthLoginRespVO.builder().token(token).build());
return null;
String token = authService.socialLogin(reqVO, getClientIP(), getUserAgent());
return success(SysAuthLoginRespVO.builder().token(token).build());
}
@PostMapping("/social-login2")
@ApiOperation("社交登录,使用 code 授权码 + 账号密码")
public CommonResult<SysAuthLoginRespVO> socialLogin2(@RequestBody @Valid MbrAuthSocialLogin2ReqVO reqVO) {
// String token = authService.socialLogin2(reqVO, getClientIP(), getUserAgent());
// // 返回结果
// return success(MbrAuthLoginRespVO.builder().token(token).build());
return null;
String token = authService.socialLogin2(reqVO, getClientIP(), getUserAgent());
return success(SysAuthLoginRespVO.builder().token(token).build());
}
@PostMapping("/social-bind")
@ApiOperation("社交绑定,使用 code 授权码")
public CommonResult<Boolean> socialBind(@RequestBody @Valid MbrAuthSocialBindReqVO reqVO) {
// authService.socialBind(getLoginUserId(), reqVO);
// return CommonResult.success(true);
return null;
authService.socialBind(getLoginUserId(), reqVO);
return CommonResult.success(true);
}
@DeleteMapping("/social-unbind")
@ApiOperation("取消社交绑定")
public CommonResult<Boolean> socialUnbind(@RequestBody MbrAuthSocialUnbindReqVO reqVO) {
// socialService.unbindSocialUser(getLoginUserId(), reqVO.getType(), reqVO.getUnionId());
// return CommonResult.success(true);
return null;
socialService.unbindSocialUser(getLoginUserId(), reqVO.getType(), reqVO.getUnionId(), UserTypeEnum.MEMBER);
return CommonResult.success(true);
}
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.userserver.modules.member.enums.social.SysSocialTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.userserver.modules.member.enums.social.SysSocialTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.userserver.modules.member.enums.social.SysSocialTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.userserver.modules.member.enums.social.SysSocialTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;

View File

@ -14,6 +14,7 @@ public interface SysErrorCodeConstants {
ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1005000001, "登录失败,账号被禁用");
ErrorCode AUTH_LOGIN_FAIL_UNKNOWN = new ErrorCode(1005000002, "登录失败"); // 登录失败的兜底未知原因
ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1005000003, "Token 已经过期");
ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1005000004, "未绑定账号,需要进行绑定");
// ========== SMS CODE 模块 1005001000 ==========
ErrorCode USER_SMS_CODE_NOT_FOUND = new ErrorCode(1005001000, "验证码不存在");
@ -22,4 +23,7 @@ public interface SysErrorCodeConstants {
ErrorCode USER_SMS_CODE_NOT_CORRECT = new ErrorCode(1005001003, "验证码不正确");
ErrorCode USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1005001004, "超过每日短信发送数量");
ErrorCode USER_SMS_CODE_SEND_TOO_FAST = new ErrorCode(1005001005, "短信发送过于频率");
// ========== 用户模块 1005002000 ==========
ErrorCode USER_NOT_EXISTS = new ErrorCode(1005002001, "用户不存在");
}

View File

@ -1,8 +1,7 @@
package cn.iocoder.yudao.userserver.modules.system.service.auth;
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthLoginReqVO;
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSmsLoginReqVO;
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*;
import javax.validation.Valid;
@ -35,4 +34,33 @@ public interface SysAuthService extends SecurityAuthFrameworkService {
*/
String smsLogin(@Valid SysAuthSmsLoginReqVO reqVO, String userIp, String userAgent);
/**
* 社交登录使用 code 授权码
*
* @param reqVO 登录信息
* @param userIp 用户 IP
* @param userAgent 用户 UA
* @return 身份令牌使用 JWT 方式
*/
String socialLogin(@Valid MbrAuthSocialLoginReqVO reqVO, String userIp, String userAgent);
/**
* 社交登录使用 code 授权码 + 账号密码
*
* @param reqVO 登录信息
* @param userIp 用户 IP
* @param userAgent 用户 UA
* @return 身份令牌使用 JWT 方式
*/
String socialLogin2(@Valid MbrAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent);
/**
* 社交绑定使用 code 授权码
*
* @param userId 用户编号
* @param reqVO 绑定信息
*/
void socialBind(Long userId, @Valid MbrAuthSocialBindReqVO reqVO);
}

View File

@ -1,25 +1,29 @@
package cn.iocoder.yudao.userserver.modules.system.service.auth.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginLogTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginResultEnum;
import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthLoginReqVO;
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSmsLoginReqVO;
import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*;
import cn.iocoder.yudao.userserver.modules.system.convert.auth.SysAuthConvert;
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthUser;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
@ -33,6 +37,8 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@ -59,6 +65,9 @@ public class SysAuthServiceImpl implements SysAuthService {
private SysLoginLogCoreService loginLogCoreService;
@Resource
private SysUserSessionCoreService userSessionCoreService;
@Resource
private SysSocialService socialService;
private static final UserTypeEnum userTypeEnum = UserTypeEnum.MEMBER;
@Override
public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
@ -99,6 +108,65 @@ public class SysAuthServiceImpl implements SysAuthService {
return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
}
@Override
public String socialLogin(MbrAuthSocialLoginReqVO reqVO, String userIp, String userAgent) {
// 使用 code 授权码进行登录
AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState());
org.springframework.util.Assert.notNull(authUser, "授权用户不为空");
// 如果未绑定 SysSocialUserDO 用户则无法自动登录进行报错
String unionId = socialService.getAuthUserUnionId(authUser);
List<SysSocialUserDO> socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, userTypeEnum);
if (CollUtil.isEmpty(socialUsers)) {
throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
}
// 自动登录
MbrUserDO user = userService.getUser(socialUsers.get(0).getUserId());
if (user == null) {
throw exception(USER_NOT_EXISTS);
}
this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_SOCIAL, SysLoginResultEnum.SUCCESS);
// 创建 LoginUser 对象
LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
// TODO 芋艿需要改造下增加各种登录方式
// loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
// 绑定社交用户更新
socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, userTypeEnum);
// 缓存登录用户到 Redis 返回 sessionId 编号
return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
}
@Override
public String socialLogin2(MbrAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent) {
// 使用 code 授权码进行登录
AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState());
org.springframework.util.Assert.notNull(authUser, "授权用户不为空");
// 使用账号密码进行登录
LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword());
// loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
// 绑定社交用户新增
socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, userTypeEnum);
// 缓存登录用户到 Redis 返回 sessionId 编号
return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
}
@Override
public void socialBind(Long userId, MbrAuthSocialBindReqVO reqVO) {
// 使用 code 授权码进行登录
AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState());
org.springframework.util.Assert.notNull(authUser, "授权用户不为空");
// 绑定社交用户新增
socialService.bindSocialUser(userId, reqVO.getType(), authUser, userTypeEnum);
}
private LoginUser login0(String username, String password) {
final SysLoginLogTypeEnum logTypeEnum = SysLoginLogTypeEnum.LOGIN_USERNAME;
// 用户验证
@ -207,7 +275,7 @@ public class SysAuthServiceImpl implements SysAuthService {
reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType());
reqDTO.setTraceId(TracerUtils.getTraceId());
reqDTO.setUserId(userId);
reqDTO.setUserType(UserTypeEnum.MEMBER.getValue());
reqDTO.setUserType(userTypeEnum.getValue());
reqDTO.setUsername(username);
reqDTO.setUserAgent(ServletUtils.getUserAgent());
reqDTO.setUserIp(ServletUtils.getClientIP());

View File

@ -138,3 +138,30 @@ yudao:
- ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
demo: true # 开启演示模式
justauth:
enabled: true
type:
GITEE: # Gitee
client-id: ee61f0374a4c6c404a8717094caa7a410d76950e45ff60348015830c519ba5c1
client-secret: 7c044a5671be3b051414db0cf2cec6ad702dd298d2416ba24ceaf608e6fa26f9
ignore-check-redirect-uri: true
DINGTALK: # 钉钉
client-id: dingvrnreaje3yqvzhxg
client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI
ignore-check-redirect-uri: true
WECHAT_ENTERPRISE: # 企业微信
client-id: wwd411c69a39ad2e54
client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw
agent-id: 1000004
ignore-check-redirect-uri: true
WECHAT_MP: # 微信公众平台 - H5 https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
client-id: wxa5a05b85ac361f96
client-secret: 247073c7cebb67f27f0e769195c2a57e
ignore-check-redirect-uri: true
cache:
type: REDIS
prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE::
timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟

View File

@ -140,3 +140,29 @@ yudao:
- ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
demo: false # 关闭演示模式
justauth:
enabled: true
type:
GITEE: # Gitee
client-id: ee61f0374a4c6c404a8717094caa7a410d76950e45ff60348015830c519ba5c1
client-secret: 7c044a5671be3b051414db0cf2cec6ad702dd298d2416ba24ceaf608e6fa26f9
ignore-check-redirect-uri: true
DINGTALK: # 钉钉
client-id: dingvrnreaje3yqvzhxg
client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI
ignore-check-redirect-uri: true
WECHAT_ENTERPRISE: # 企业微信
client-id: wwd411c69a39ad2e54
client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw
agent-id: 1000004
ignore-check-redirect-uri: true
WECHAT_MP: # 微信公众平台 - H5 https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
client-id: wxa5a05b85ac361f96
client-secret: 247073c7cebb67f27f0e769195c2a57e
ignore-check-redirect-uri: true
cache:
type: REDIS
prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE::
timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟