!382 统一登录,授权

feature/model
三个三 1 year ago committed by 疯狂的狮子Li
parent 6bbe11d494
commit 6b14778691

@ -6,7 +6,6 @@ import cn.hutool.core.util.ObjectUtil;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse; import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser; import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest; import me.zhyd.oauth.request.AuthRequest;
@ -87,7 +86,7 @@ public class AuthController {
} }
/** /**
* *
* *
* @param source * @param source
* @return * @return
@ -98,36 +97,31 @@ public class AuthController {
if (ObjectUtil.isNull(obj)) { if (ObjectUtil.isNull(obj)) {
return R.fail(source + "平台账号暂不支持"); return R.fail(source + "平台账号暂不支持");
} }
AuthRequest authRequest = SocialUtils.getAuthRequest(source, AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties);
obj.getClientId(),
obj.getClientSecret(),
obj.getRedirectUri());
String authorizeUrl = authRequest.authorize(AuthStateUtils.createState()); String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
return R.ok(authorizeUrl); return R.ok(authorizeUrl);
} }
/** /**
* *
* *
* @param source * @param loginBody
* @param callback
* @return * @return
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@GetMapping("/social-login") @PostMapping("/social/callback")
public R<String> socialLogin(String source, AuthCallback callback) { public R<LoginVo> socialLogin(@RequestBody LoginBody loginBody) {
SocialLoginConfigProperties obj = socialProperties.getType().get(source); // 获取第三方登录信息
if (ObjectUtil.isNull(obj)) { AuthResponse<AuthUser> response = SocialUtils.loginAuth(loginBody, socialProperties);
return R.fail(source + "平台账号暂不支持"); AuthUser authUserData = response.getData();
} // 判断授权响应是否成功
AuthRequest authRequest = SocialUtils.getAuthRequest(source, if (!response.ok()) {
obj.getClientId(), return R.fail(response.getMsg());
obj.getClientSecret(), }
obj.getRedirectUri()); return loginService.sociaRegister(authUserData);
AuthResponse<AuthUser> response = authRequest.login(callback);
return loginService.socialLogin(source, response);
} }
/** /**
* *
* *

@ -1,14 +1,12 @@
package org.dromara.web.service; package org.dromara.web.service;
import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser; import me.zhyd.oauth.model.AuthUser;
import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.constant.GlobalConstants;
@ -16,12 +14,14 @@ import org.dromara.common.core.constant.TenantConstants;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.dto.RoleDTO; import org.dromara.common.core.domain.dto.RoleDTO;
import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.enums.DeviceType;
import org.dromara.common.core.enums.LoginType; import org.dromara.common.core.enums.LoginType;
import org.dromara.common.core.enums.TenantStatus; import org.dromara.common.core.enums.TenantStatus;
import org.dromara.common.core.enums.UserStatus; import org.dromara.common.core.enums.UserStatus;
import org.dromara.common.core.exception.user.UserException; import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.*; import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.log.event.LogininforEvent; import org.dromara.common.log.event.LogininforEvent;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
@ -29,13 +29,13 @@ import org.dromara.common.tenant.exception.TenantException;
import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.SysUser; import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.bo.SysSocialBo; import org.dromara.system.domain.bo.SysSocialBo;
import org.dromara.system.domain.vo.SysSocialVo;
import org.dromara.system.domain.vo.SysTenantVo; import org.dromara.system.domain.vo.SysTenantVo;
import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ISysPermissionService; import org.dromara.system.service.ISysPermissionService;
import org.dromara.system.service.ISysSocialService; import org.dromara.system.service.ISysSocialService;
import org.dromara.system.service.ISysTenantService; import org.dromara.system.service.ISysTenantService;
import org.dromara.web.domain.vo.LoginVo;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -66,67 +66,26 @@ public class SysLoginService {
private final ISysSocialService sysSocialService; private final ISysSocialService sysSocialService;
private final SysUserMapper userMapper; private final SysUserMapper userMapper;
/**
*
*
* @param source
* @param authUser
* @return
*/
public R<String> socialLogin(String source, AuthResponse<AuthUser> authUser) {
// 判断授权响应是否成功
if (!authUser.ok()) {
return R.fail("对不起,授权信息验证不通过,请退出重试!");
}
AuthUser authUserData = authUser.getData();
SysSocialVo social = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
if (ObjectUtil.isNotNull(social)) {
SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getUserId, social.getUserId()));
// 执行登录和记录登录信息操作
return loginAndRecord(user.getTenantId(), user.getUserName(), authUserData);
} else {
// 判断是否已登录
if (!StpUtil.isLogin()) {
return R.fail("授权失败,请先登录才能绑定");
}
SysSocialBo bo = new SysSocialBo();
bo.setUserId(LoginHelper.getUserId());
bo.setAuthId(authUserData.getSource() + authUserData.getUuid());
bo.setSource(authUserData.getSource());
bo.setUserName(authUserData.getUsername());
bo.setNickName(authUserData.getNickname());
bo.setAvatar(authUserData.getAvatar());
bo.setOpenId(authUserData.getUuid());
BeanUtils.copyProperties(authUserData.getToken(), bo);
sysSocialService.insertByBo(bo);
SysUserVo sysUser = loadUserByUsername(LoginHelper.getTenantId(), LoginHelper.getUsername());
// 执行登录和记录登录信息操作
return loginAndRecord(sysUser.getTenantId(), sysUser.getUserName(), authUserData);
}
}
/** /**
* *
* * @param authUserData
* @param tenantId ID
* @param userName
* @param authUser
* @return * @return
*/ */
private R<String> loginAndRecord(String tenantId, String userName, AuthUser authUser) { public R<LoginVo> sociaRegister(AuthUser authUserData ){
checkTenant(tenantId); SysSocialBo bo = new SysSocialBo();
SysUserVo user = loadUserByUsername(tenantId, userName); bo.setUserId(LoginHelper.getUserId());
SaLoginModel model = new SaLoginModel(); bo.setAuthId(authUserData.getSource() + authUserData.getUuid());
model.setDevice(DeviceType.PC.getDevice()); bo.setOpenId(authUserData.getUuid());
// 生成token bo.setUserName(authUserData.getUsername());
LoginHelper.login(buildLoginUser(user), model); BeanUtils.copyProperties(authUserData, bo);
recordLogininfor(user.getTenantId(), userName, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); BeanUtils.copyProperties(authUserData.getToken(), bo);
recordLoginInfo(user.getUserId()); sysSocialService.insertByBo(bo);
return R.ok(StpUtil.getTokenValue()); return R.ok();
} }
/** /**
* 退 * 退
*/ */

@ -0,0 +1,109 @@
package org.dromara.web.service.impl;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.domain.model.LoginBody;
import org.dromara.common.core.domain.model.SocialLogin;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.core.validate.auth.SocialGroup;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.social.config.properties.SocialProperties;
import org.dromara.common.social.utils.SocialUtils;
import org.dromara.system.domain.SysClient;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysSocialVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ISysSocialService;
import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service;
/**
*
*
* @author thiszhc is
*/
@Slf4j
@Service("social" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor
public class socialAuthStrategy implements IAuthStrategy {
private final SocialProperties socialProperties;
private final ISysSocialService sysSocialService;
private final SysUserMapper userMapper;
private final SysLoginService loginService;
@Override
public void validate(LoginBody loginBody) {
ValidatorUtils.validate(loginBody, SocialGroup.class);
}
/**
* -
* @param clientId id
* @param loginBody
* @param client
*/
@Override
public LoginVo login(String clientId, LoginBody loginBody, SysClient client) {
AuthResponse<AuthUser> response = SocialUtils.loginAuth(loginBody,socialProperties);
if (!response.ok()) {
throw new ServiceException(response.getMsg());
}
AuthUser authUserData = response.getData();
SysSocialVo social = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
if (!ObjectUtil.isNotNull(social)) {
throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
}//验证授权表里面的租户id是否包含当前租户id
if (ObjectUtil.isNotNull(social) && StrUtil.isNotBlank(social.getTenantId())
&& !social.getTenantId().contains(loginBody.getTenantId())) {
throw new ServiceException("对不起,你没有权限登录当前租户!");
}
return loadinUser(social, client);
}
/**
*
*
* @param social
* @param client
* @return
*/
private LoginVo loadinUser(SysSocialVo social, SysClient client) {
SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getUserId, social.getUserId()));
SocialLogin loginUser = new SocialLogin();
loginUser.setUserId(user.getUserId());
loginUser.setTenantId(user.getTenantId());
loginUser.setUsername(user.getUserName());
loginUser.setUserType(user.getUserType());
// 执行登录
SaLoginModel model = new SaLoginModel();
model.setDevice(client.getDeviceType());
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
// 例如: 后台用户30分钟过期 app用户1天过期
model.setTimeout(client.getTimeout());
model.setActiveTimeout(client.getActiveTimeout());
// 生成token
LoginHelper.login(loginUser, model);
loginService.recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
loginService.recordLoginInfo(user.getUserId());
LoginVo loginVo = new LoginVo();
loginVo.setAccessToken(StpUtil.getTokenValue());
return loginVo;
}
}

@ -3,10 +3,7 @@ package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Email;
import org.dromara.common.core.constant.UserConstants; import org.dromara.common.core.constant.UserConstants;
import lombok.Data; import lombok.Data;
import org.dromara.common.core.validate.auth.EmailGroup; import org.dromara.common.core.validate.auth.*;
import org.dromara.common.core.validate.auth.PasswordGroup;
import org.dromara.common.core.validate.auth.SmsGroup;
import org.dromara.common.core.validate.auth.WechatGroup;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
@ -103,4 +100,21 @@ public class LoginBody {
@NotBlank(message = "{xcx.code.not.blank}", groups = {WechatGroup.class}) @NotBlank(message = "{xcx.code.not.blank}", groups = {WechatGroup.class})
private String xcxCode; private String xcxCode;
/**
*
*/
@NotBlank(message = "{social.source.not.blank}" , groups = {SocialGroup.class})
private String source;
/**
* code
*/
@NotBlank(message = "{social.code.not.blank}" , groups = {SocialGroup.class})
private String socialCode;
/**
* socialState
*/
@NotBlank(message = "{social.state.not.blank}" , groups = {SocialGroup.class})
private String socialState;
} }

@ -0,0 +1,21 @@
package org.dromara.common.core.domain.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
*
*
* @author thiszhc is
*/
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class SocialLogin extends LoginUser{
/**
* openid
*/
private String openid;
}

@ -0,0 +1,4 @@
package org.dromara.common.core.validate.auth;
public interface SocialGroup {
}

@ -1,18 +1,38 @@
package org.dromara.common.social.utils; package org.dromara.common.social.utils;
import cn.hutool.core.util.ObjectUtil;
import me.zhyd.oauth.config.AuthConfig; import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException; import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.*; import me.zhyd.oauth.request.*;
import org.dromara.common.core.domain.model.LoginBody;
import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
import org.dromara.common.social.config.properties.SocialProperties;
/** /**
* *
* *
* @author thiszhc * @author thiszhc
*/ */
public class SocialUtils { public class SocialUtils {
public static AuthResponse<AuthUser> loginAuth(LoginBody loginBody, SocialProperties socialProperties) throws AuthException {
AuthRequest authRequest = getAuthRequest(loginBody.getSource(), socialProperties);
AuthCallback callback = new AuthCallback();
callback.setCode(loginBody.getSocialCode());
callback.setState(loginBody.getSocialState());
return authRequest.login(callback);
}
public static AuthRequest getAuthRequest(String source, String clientId, public static AuthRequest getAuthRequest(String source,SocialProperties socialProperties) throws AuthException {
String clientSecret, String redirectUri) throws AuthException { SocialLoginConfigProperties obj = socialProperties.getType().get(source);
if (ObjectUtil.isNull(obj)) {
throw new AuthException("不支持的第三方登录类型");
}
String clientId = obj.getClientId();
String clientSecret = obj.getClientSecret();
String redirectUri = obj.getRedirectUri();
AuthRequest authRequest = null; AuthRequest authRequest = null;
switch (source.toLowerCase()) { switch (source.toLowerCase()) {
case "dingtalk" -> case "dingtalk" ->
@ -45,13 +65,15 @@ public class SocialUtils {
case "alipay" -> case "alipay" ->
// 支付宝在创建回调地址时不允许使用localhost或者127.0.0.1所以这儿的回调地址使用的局域网内的ip // 支付宝在创建回调地址时不允许使用localhost或者127.0.0.1所以这儿的回调地址使用的局域网内的ip
authRequest = new AuthAlipayRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret) authRequest = new AuthAlipayRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.alipayPublicKey("").redirectUri(redirectUri).build()); .redirectUri(redirectUri).build());
case "qq" -> case "qq" ->
authRequest = new AuthQqRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret) authRequest = new AuthQqRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build()); .redirectUri(redirectUri).build());
case "wechat_open" -> authRequest = new AuthWeChatOpenRequest(AuthConfig.builder().clientId(clientId) case "wechat_open" -> authRequest = new AuthWeChatOpenRequest(AuthConfig.builder().clientId(clientId)
.clientSecret(clientSecret).redirectUri(redirectUri).build()); .clientSecret(clientSecret).redirectUri(redirectUri).build());
case "csdn" -> case "csdn" ->
//注意,经咨询CSDN官方客服得知CSDN的授权开放平台已经下线。如果以前申请过的应用可以继续使用但是不再支持申请新的应用。
// so, 本项目中的CSDN登录只能针对少部分用户使用了
authRequest = new AuthCsdnRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret) authRequest = new AuthCsdnRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build()); .redirectUri(redirectUri).build());
case "taobao" -> case "taobao" ->

@ -57,9 +57,8 @@ public class SysSocialBo extends TenantEntity {
private String refreshToken; private String refreshToken;
/** /**
* open id * id
*/ */
@NotBlank(message = "用户的 open id不能为空", groups = { AddGroup.class, EditGroup.class })
private String openId; private String openId;
/** /**
@ -139,4 +138,5 @@ public class SysSocialBo extends TenantEntity {
private String oauthTokenSecret; private String oauthTokenSecret;
} }

@ -73,7 +73,7 @@ public class SysSocialVo implements Serializable {
/** /**
* open id * open id
*/ */
@ExcelProperty(value = "用户的 open id") @ExcelProperty(value = "平台的唯一id")
private String openId; private String openId;
/** /**

@ -7,9 +7,9 @@ create table sys_social
id bigint not null comment '主键', id bigint not null comment '主键',
user_id bigint not null comment '用户ID', user_id bigint not null comment '用户ID',
tenant_id varchar(20) default null comment '租户id', tenant_id varchar(20) default null comment '租户id',
auth_id varchar(255) not null comment '授权+授权openid', auth_id varchar(255) not null comment '平台+平台唯一id',
source varchar(255) not null comment '用户来源', source varchar(255) not null comment '用户来源',
open_id varchar(255) default null comment '原生open id', open_id varchar(255) default null comment '平台编号唯一id',
user_name varchar(30) not null comment '登录账号', user_name varchar(30) not null comment '登录账号',
nick_name varchar(30) default '' comment '用户昵称', nick_name varchar(30) default '' comment '用户昵称',
email varchar(255) default '' comment '用户邮箱', email varchar(255) default '' comment '用户邮箱',

Loading…
Cancel
Save