初始代码

This commit is contained in:
wangmingwei
2026-04-21 16:49:46 +08:00
parent aae9dc4036
commit f0453ff3a3
2396 changed files with 256575 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>yunzhupaas-oauth</artifactId>
<groupId>com.yunzhupaas</groupId>
<version>5.2.0-RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yunzhupaas-oauth-biz</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.yunzhupaas</groupId>
<artifactId>yunzhupaas-oauth-entity</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.yunzhupaas</groupId>
<artifactId>yunzhupaas-system-biz</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.yunzhupaas</groupId>
<artifactId>yunzhupaas-common-all</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.yunzhupaas</groupId>
<artifactId>yunzhupaas-exception</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>com.yunzhupaas</groupId>
<artifactId>yunzhupaas-message-biz</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.yunzhupaas</groupId>
<artifactId>yunzhupaas-permission-controller</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.yunzhupaas</groupId>
<artifactId>yunzhupaas-visualdev-portal-biz</artifactId>
<version>${project.version}</version>
</dependency>
<!-- redis相关依赖包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.yunzhupaas</groupId>
<artifactId>yunzhupaas-common-security</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,213 @@
package com.yunzhupaas.granter;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yunzhupaas.base.ActionResult;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.config.YunzhupaasOauthConfig;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.consts.AuthConsts;
import com.yunzhupaas.consts.LoginTicketStatus;
import com.yunzhupaas.exception.LoginException;
import com.yunzhupaas.model.BaseSystemInfo;
import com.yunzhupaas.model.LoginTicketModel;
import com.yunzhupaas.model.SocialUnbindModel;
import com.yunzhupaas.permission.controller.SocialsUserController;
import com.yunzhupaas.permission.model.socails.SocialsUserInfo;
import com.yunzhupaas.permission.util.implicit.ImplicitLoginUtil;
import com.yunzhupaas.util.ServletUtil;
import com.yunzhupaas.util.StringUtil;
import com.yunzhupaas.util.TicketUtil;
import com.yunzhupaas.util.UserProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
import static com.yunzhupaas.granter.ImplicitTokenGranter.GRANT_TYPE;
@Slf4j
@Component(GRANT_TYPE)
public class ImplicitTokenGranter extends AbstractTokenGranter {
public static final String GRANT_TYPE = "implicit";
public static final Integer ORDER = 5;
private static final String URL_LOGIN = "/Login/implicit/**";
@Autowired
private ImplicitLoginUtil implicitLoginUtil;
@Autowired
private SocialsUserController socialsUserApi;
@Autowired
private YunzhupaasOauthConfig oauthConfig;
public ImplicitTokenGranter() {
super(URL_LOGIN);
}
protected String getGrantType() {
return GRANT_TYPE;
}
public ActionResult logout() {
return super.logout();
}
public int getOrder() {
return ORDER;
}
@Override
protected void loginSuccess(UserInfo userInfo, BaseSystemInfo baseSystemInfo) {
}
@Override
protected void loginFailure(UserInfo userInfo, BaseSystemInfo baseSystemInfo, Exception e) {
super.loginFailure(userInfo, baseSystemInfo, e);
}
@Override
protected String getUserDetailKey() {
return AuthConsts.USERDETAIL_USER_ID;
}
public ActionResult granter(Map<String, String> map) throws LoginException {
SaRequest req = SaHolder.getRequest();
String code = req.getParam("code");
String source = req.getParam("source");
String userAgent = ServletUtil.getUserAgent();
if (userAgent.contains("wxwork")) {
source = "wechat_enterprise";
}
if (userAgent.contains("DingTalk")) {
source = "dingtalk";
}
// 授权回调登录接口重定向携带token的首页
if (StringUtil.isEmpty(source)) {
return ActionResult.fail(MsgCode.OA028.get());
}
// 跳js页面直接调用授权链接
if (StringUtil.isEmpty(code)) {
String authLink = implicitLoginUtil.getAuthLink(source);
SaHolder.getResponse().redirect(authLink);
return null;
}
String uuid = implicitLoginUtil.loginByCode(source, code, null);
// uuid登录
return this.loginByUuid(source, uuid);
}
/**
* 通过第三方用户id登录
*
* @param source
* @param uuid
* @return
* @throws LoginException
*/
protected ActionResult loginByUuid(String source, String uuid) throws LoginException {
boolean isApp = "APP".equalsIgnoreCase(UserProvider.getDeviceForAgent().getDevice());
String url = isApp ? configValueUtil.getAppDomain() : configValueUtil.getFrontDomain();
SocialsUserInfo socialsUserInfo = socialsUserApi.getUserInfo(source, uuid, null);
if (configValueUtil.isMultiTenancy()) {
if (socialsUserInfo == null || CollectionUtil.isEmpty(socialsUserInfo.getTenantUserInfo())) {
// 未绑定写入缓存
LoginTicketModel ticketModel = (new LoginTicketModel())
.setStatus(LoginTicketStatus.UnBindMes.getStatus())
.setTicketTimeout(System.currentTimeMillis() + oauthConfig.getTicketTimeout() * 1000);
createdTicketState(ticketModel, url, isApp);
return ActionResult.success();
}
if (socialsUserInfo.getTenantUserInfo().size() == 1) {
UserInfo userInfo = socialsUserInfo.getUserInfo();
// 切换租户
switchTenant(userInfo);
// 获取系统配置
BaseSystemInfo baseSystemInfo = getSysconfig(userInfo);
// 登录账号
String token = super.loginAccount(userInfo, baseSystemInfo);
// 返回登录信息
String redirectUrl = url + "/sso" + "?token=" + token;
if (isApp) {
redirectUrl = url + "/pages/login/sso-redirect" + "?token=" + token;
}
SaHolder.getResponse().redirect(redirectUrl);
return ActionResult.success();
} else {
// 多租户信息写入ticket缓存
JSONArray tenantUserInfo = socialsUserInfo.getTenantUserInfo();
LoginTicketModel ticketModel = (new LoginTicketModel())
.setStatus(LoginTicketStatus.Multitenancy.getStatus()).setValue(tenantUserInfo.toJSONString())
.setTicketTimeout(System.currentTimeMillis() + oauthConfig.getTicketTimeout() * 1000);
createdTicketState(ticketModel, url, isApp);
return ActionResult.success();
}
} else {
if (socialsUserInfo == null || socialsUserInfo.getUserInfo() == null) {
// 未绑定写入缓存
LoginTicketModel ticketModel = (new LoginTicketModel())
.setStatus(LoginTicketStatus.UnBindMes.getStatus())
.setTicketTimeout(System.currentTimeMillis() + oauthConfig.getTicketTimeout() * 1000);
createdTicketState(ticketModel, url, isApp);
return ActionResult.success();
}
UserInfo userInfo = socialsUserInfo.getUserInfo();
// 切换租户
switchTenant(userInfo);
// 获取系统配置
BaseSystemInfo baseSystemInfo = getSysconfig(userInfo);
// 登录账号
String token = super.loginAccount(userInfo, baseSystemInfo);
String redirectUrl = url + "/sso" + "?token=" + token;
if (isApp) {
redirectUrl = url + "/pages/login/sso-redirect" + "?token=" + token;
}
SaHolder.getResponse().redirect(redirectUrl);
return ActionResult.success();
}
}
/**
* 创建票据
*
* @param loginTicketModel
* @param url
* @param isApp
* @return
*/
private String createdTicketState(LoginTicketModel loginTicketModel, String url, boolean isApp) {
String ticket = TicketUtil.createTicket(loginTicketModel, oauthConfig.getTicketTimeout());
String MultitenancyUrl = url + "/login?YUNZHUPAAS_TICKET=" + ticket;
if (isApp) {
MultitenancyUrl = url + "/pages/login/index?YUNZHUPAAS_TICKET=" + ticket;
}
SaHolder.getResponse().redirect(MultitenancyUrl);
return ticket;
}
/**
* 未绑定-更新票据缓存
*
* @param socialType
* @param socialUnionid
* @param socialName
* @return
*/
protected LoginTicketModel updateTicketUnbind(String socialType, String socialUnionid, String socialName) {
LoginTicketModel loginTicketModel = null;
SocialUnbindModel obj = new SocialUnbindModel(socialType, socialUnionid, socialName);
String ticket = this.getYunzhupaasTicket();
if (!ticket.isEmpty()) {
loginTicketModel = (new LoginTicketModel()).setStatus(LoginTicketStatus.UnBind.getStatus())
.setValue(JSONObject.toJSONString(obj));
TicketUtil.updateTicket(ticket, loginTicketModel, (Long) 300L);
}
return loginTicketModel;
}
}

View File

@@ -0,0 +1,283 @@
package com.yunzhupaas.granter;
import cn.dev33.satoken.context.SaHolder;
import com.yunzhupaas.base.ActionResult;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.consts.AuthConsts;
import com.yunzhupaas.consts.DeviceType;
import com.yunzhupaas.database.util.TenantDataSourceUtil;
import com.yunzhupaas.exception.LoginException;
import com.yunzhupaas.message.entity.UserDeviceEntity;
import com.yunzhupaas.message.service.UserDeviceService;
import com.yunzhupaas.model.*;
import com.yunzhupaas.model.tenant.TenantVO;
import com.yunzhupaas.permission.controller.SocialsUserController;
import com.yunzhupaas.permission.entity.UserEntity;
import com.yunzhupaas.permission.service.UserService;
import com.yunzhupaas.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import static com.yunzhupaas.granter.PasswordTokenGranter.GRANT_TYPE;
import static com.yunzhupaas.util.Constants.ADMIN_KEY;
/**
* 账号密码认证
*
* @author 云筑产品开发平台组
* @user N
* @copyright 深圳市乐程软件有限公司
* @date 2024/9/17 22:13
*/
@Slf4j
@Component(GRANT_TYPE)
public class PasswordTokenGranter extends AbstractTokenGranter {
public static final String GRANT_TYPE = "password";
public static final Integer ORDER = 1;
private static final String URL_LOGIN = "";
public PasswordTokenGranter() {
super(URL_LOGIN);
}
public PasswordTokenGranter(String authenticationUrl) {
super(authenticationUrl);
}
@Autowired
private UserService userApi;
@Autowired
private UserDetailsServiceBuilder userDetailsServiceBuilder;
@Autowired
private SocialsUserController socialsUserApi;
@Autowired
private UserDeviceService userDeviceService;
@Override
public ActionResult<LoginVO> granter(Map<String, String> loginParameters) throws LoginException {
LoginForm loginForm = JsonUtil.getJsonToBean(loginParameters, LoginForm.class);
UserInfo userInfo = UserProvider.getUser();
// 切换租户
switchTenant(userInfo);
// 获取系统配置
BaseSystemInfo baseSystemInfo = getSysconfig(userInfo);
// 预检信息
preAuthenticate(loginForm, userInfo, baseSystemInfo);
// 登录账号
super.loginAccount(userInfo, baseSystemInfo);
// 返回登录信息
LoginVO loginResult = getLoginVo(userInfo);
return ActionResult.success(loginResult);
}
@Override
public int getOrder() {
return ORDER;
}
/**
* 可重写实现邮箱、短信、TOTP验证
*
* @param loginForm
* @param sysConfigInfo
* @throws LoginException
*/
protected void preAuthenticate(LoginForm loginForm, UserInfo userInfo, BaseSystemInfo sysConfigInfo)
throws LoginException {
// 验证密码
UserEntity userEntity = userDetailsServiceBuilder.getUserDetailService(AuthConsts.USERDETAIL_ACCOUNT)
.loadUserEntity(userInfo);
userInfo.setUserId(userEntity.getId());
userInfo.setUserName(userEntity.getRealName());
UserProvider.setLocalLoginUser(userInfo);
// 判断是否开启验证码
if (Objects.nonNull(sysConfigInfo) && "1".equals(String.valueOf(sysConfigInfo.getEnableVerificationCode()))) {
// 验证验证码
String timestamp = String.valueOf(redisUtil.getString(loginForm.getTimestamp()));
if (StringUtil.isEmpty(timestamp)) {
throw new LoginException(MsgCode.LOG107.get());
}
if (!loginForm.getCode().equalsIgnoreCase(timestamp)) {
throw new LoginException(MsgCode.LOG104.get());
}
}
try {
authenticate(loginForm, userEntity, sysConfigInfo);
} catch (Exception e) {
authenticateFailure(userEntity, sysConfigInfo);
throw e;
}
LoginHolder.setUserEntity(userEntity);
}
protected void authenticate(LoginForm loginForm, UserEntity userEntity, BaseSystemInfo systemInfo)
throws LoginException {
authenticateLock(userEntity, systemInfo);
authenticatePassword(loginForm, userEntity, systemInfo);
}
protected void authenticateLock(UserEntity userEntity, BaseSystemInfo systemInfo) throws LoginException {
// 判断当前账号是否被锁定
Integer lockMark = userEntity.getEnabledMark();
if (Objects.nonNull(lockMark) && lockMark == 2) {
// 获取解锁时间
Date unlockTime = userEntity.getUnlockTime();
// 账号锁定
if (systemInfo.getLockType() == 1 || Objects.isNull(unlockTime)) {
throw new LoginException(MsgCode.LOG012.get());
}
// 延迟登陆锁定
long millis = System.currentTimeMillis();
if (unlockTime.getTime() > millis) {
// 转成分钟
int time = (int) ((unlockTime.getTime() - millis) / (1000 * 60));
throw new LoginException(MsgCode.LOG108.get(time + 1));
} else if (unlockTime.getTime() < millis
&& userEntity.getLogErrorCount() >= systemInfo.getPasswordErrorsNumber()) {
// 已经接触错误时间锁定的话就重置错误次数
userEntity.setLogErrorCount(0);
userEntity.setEnabledMark(1);
userApi.updateById(userEntity);
}
}
}
protected void authenticatePassword(LoginForm loginForm, UserEntity userEntity, BaseSystemInfo systemInfo)
throws LoginException {
String inputPwd = loginForm.getPassword();
try {
// 前端md5后进行aes加密
inputPwd = DesUtil.aesOrDecode(inputPwd, false, true);
} catch (Exception e) {
log.error(MsgCode.OA013.get() + ":{}", inputPwd, e);
inputPwd = "";
}
if (!userEntity.getPassword()
.equals(Md5Util.getStringMd5(inputPwd + userEntity.getSecretkey().toLowerCase()))) {
throw new LoginException(MsgCode.LOG101.get());
}
}
protected void authenticateFailure(UserEntity entity, BaseSystemInfo sysConfigInfo) {
if (entity != null) {
// 超级管理员特权,不会锁定
if (!ADMIN_KEY.equals(entity.getAccount())) {
// 判断是否需要锁定账号,哪种锁定方式
// 大于2则判断有效
Integer errorsNumber = sysConfigInfo.getPasswordErrorsNumber();
// 判断是否开启
if (errorsNumber != null && errorsNumber > 2) {
// 加入错误次数
Integer errorCount = entity.getLogErrorCount() != null ? entity.getLogErrorCount() + 1 : 1;
entity.setLogErrorCount(errorCount);
Integer lockType = sysConfigInfo.getLockType();
if (errorCount >= errorsNumber) {
entity.setEnabledMark(2);
// 如果是延时锁定
if (Objects.nonNull(lockType) && lockType == 2) {
Integer lockTime = sysConfigInfo.getLockTime();
Date date = new Date((System.currentTimeMillis() + (lockTime * 60 * 1000)));
entity.setUnlockTime(date);
}
}
if (lockType == 1) {
entity.setUnlockTime(null);
}
userApi.updateById(entity);
}
}
}
}
@Override
protected void preLogin(UserInfo userInfo, BaseSystemInfo baseSystemInfo) throws LoginException {
}
@Override
protected void loginSuccess(UserInfo userInfo, BaseSystemInfo baseSystemInfo) {
super.loginSuccess(userInfo, baseSystemInfo);
// 登录成功绑定第三方
if (SaHolder.getRequest().hasParam(AuthConsts.PARAMS_YUNZHUPAAS_TICKET)) {
String ticket = SaHolder.getRequest().getParam(AuthConsts.PARAMS_YUNZHUPAAS_TICKET);
LoginTicketModel ticketModel = TicketUtil.parseTicket(ticket);
if (ticketModel != null) {
SocialUnbindModel jsonToBean = JsonUtil.getJsonToBean(ticketModel.getValue(), SocialUnbindModel.class);
if (jsonToBean != null) {
socialsUserApi.loginAutoBinding(jsonToBean.getSocialType(), jsonToBean.getSocialUnionid(),
jsonToBean.getSocialName(),
userInfo.getUserId(), userInfo.getTenantId());
}
}
}
if (SaHolder.getRequest().hasParam(AuthConsts.Client_Id)) {
String Client_Id = SaHolder.getRequest().getParam(AuthConsts.Client_Id);
if (StringUtil.isNotBlank(Client_Id) && !"null".equals(Client_Id)) {
UserDeviceEntity userDeviceEntity = userDeviceService.getInfoByClientId(Client_Id);
if (userDeviceEntity != null) {
userDeviceEntity.setUserId(userInfo.getUserId());
userDeviceEntity.setLastModifyTime(DateUtil.getNowDate());
userDeviceEntity.setLastModifyUserId(userInfo.getUserId());
userDeviceService.update(userDeviceEntity.getId(), userDeviceEntity);
} else {
userDeviceEntity = new UserDeviceEntity();
userDeviceEntity.setId(RandomUtil.uuId());
userDeviceEntity.setUserId(userInfo.getUserId());
userDeviceEntity.setClientId(Client_Id);
userDeviceEntity.setCreatorTime(DateUtil.getNowDate());
userDeviceEntity.setCreatorUserId(userInfo.getUserId());
userDeviceService.create(userDeviceEntity);
}
}
}
}
protected LoginVO getLoginVo(UserInfo userInfo) {
LoginVO loginVO = new LoginVO();
loginVO.setTheme(userInfo.getTheme());
loginVO.setToken(userInfo.getToken());
if (configValueUtil.isMultiTenancy()) {
// 删除卫翎信息
TenantVO tenantVO = TenantDataSourceUtil.getCacheTenantInfo(userInfo.getTenantId());
if (tenantVO != null && tenantVO.getWl_qrcode() != null) {
loginVO.setWl_qrcode(tenantVO.getWl_qrcode());
tenantVO.setWl_qrcode(null);
TenantDataSourceUtil.setTenantInfo(tenantVO);
}
}
return loginVO;
}
@Override
public ActionResult logout() {
UserInfo userInfo = UserProvider.getUser();
if (userInfo.getUserId() != null) {
if ("1".equals(String.valueOf(loginService.getBaseSystemConfig(userInfo.getTenantId()).getSingleLogin()))) {
UserProvider.logoutByUserId(userInfo.getUserId(), DeviceType.valueOf(userInfo.getLoginDevice()));
} else {
UserProvider.logoutByToken(userInfo.getToken());
}
}
return ActionResult.success(MsgCode.OA014.get());
}
@Override
protected String getGrantType() {
return GRANT_TYPE;
}
@Override
protected String getUserDetailKey() {
return AuthConsts.USERDETAIL_ACCOUNT;
}
}

View File

@@ -0,0 +1,97 @@
package com.yunzhupaas.granter;
import com.yunzhupaas.base.ActionResult;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.config.ConfigValueUtil;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.consts.AuthConsts;
import com.yunzhupaas.consts.DeviceType;
import com.yunzhupaas.exception.LoginException;
import com.yunzhupaas.model.BaseSystemInfo;
import com.yunzhupaas.model.LoginVO;
import com.yunzhupaas.util.StringUtil;
import com.yunzhupaas.util.UserProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
import static com.yunzhupaas.granter.ScanCodeTokenGranter.GRANT_TYPE;
@Slf4j
@Component(GRANT_TYPE)
public class ScanCodeTokenGranter extends AbstractTokenGranter {
public static final String GRANT_TYPE = "scancode";
public static final Integer ORDER = 5;
private static final String URL_LOGIN = "";
@Autowired
protected ConfigValueUtil configValueUtil;
public ScanCodeTokenGranter() {
super(URL_LOGIN);
}
/**
* @param loginParameters {userId, tenantId}
* @return
* @throws LoginException
*/
@Override
public ActionResult granter(Map<String, String> loginParameters) throws LoginException {
String token = loginParameters.get("token");
// 验证token是否有效
UserInfo userInfo = UserProvider.getUser(token);
if (configValueUtil.isMultiTenancy()){
userInfo.setUserAccount(userInfo.getTenantId()+"@"+userInfo.getUserAccount());
}
//切换租户
switchTenant(userInfo);
//获取系统配置
BaseSystemInfo baseSystemInfo = getSysconfig(userInfo);
//登录账号
super.loginAccount(userInfo, baseSystemInfo);
//返回登录信息
LoginVO loginResult = getLoginVo(userInfo);
return ActionResult.success(loginResult);
}
@Override
public int getOrder() {
return ORDER;
}
protected LoginVO getLoginVo(UserInfo userInfo) {
LoginVO loginVO = new LoginVO();
loginVO.setTheme(userInfo.getTheme());
loginVO.setToken(userInfo.getToken());
return loginVO;
}
@Override
public ActionResult logout() {
UserInfo userInfo = UserProvider.getUser();
if (userInfo.getUserId() != null) {
if ("1".equals(String.valueOf(loginService.getBaseSystemConfig(userInfo.getTenantId()).getSingleLogin()))) {
UserProvider.logoutByUserId(userInfo.getUserId(), DeviceType.valueOf(userInfo.getLoginDevice()));
} else {
UserProvider.logoutByToken(userInfo.getToken());
}
}
return ActionResult.success(MsgCode.OA014.get());
}
@Override
protected String getGrantType() {
return GRANT_TYPE;
}
@Override
protected String getUserDetailKey() {
return AuthConsts.USERDETAIL_USER_ID;
}
}

View File

@@ -0,0 +1,289 @@
package com.yunzhupaas.granter;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yunzhupaas.base.ActionResult;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.consts.AuthConsts;
import com.yunzhupaas.consts.LoginTicketStatus;
import com.yunzhupaas.exception.LoginException;
import com.yunzhupaas.model.BaseSystemInfo;
import com.yunzhupaas.model.LoginTicketModel;
import com.yunzhupaas.model.SocialUnbindModel;
import com.yunzhupaas.model.LoginVO;
import com.yunzhupaas.permission.controller.SocialsUserController;
import com.yunzhupaas.permission.model.socails.SocialsUserInfo;
import com.yunzhupaas.service.impl.UserDetailsByUserIdServiceImpl;
import com.yunzhupaas.util.ServletUtil;
import com.yunzhupaas.util.StringUtil;
import com.yunzhupaas.util.TicketUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import jakarta.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Map;
import static com.yunzhupaas.granter.SocialsTokenGranter.GRANT_TYPE;
@Slf4j
@Component(GRANT_TYPE)
public class SocialsTokenGranter extends AbstractTokenGranter {
public static final String GRANT_TYPE = "socials";
public static final Integer ORDER = 5;
private static final String URL_LOGIN = "/Login/socials/**";
@Autowired
private SocialsUserController socialsUserApi;
public SocialsTokenGranter() {
super(URL_LOGIN);
}
protected String getGrantType() {
return GRANT_TYPE;
}
public ActionResult granter(Map<String, String> map) throws LoginException {
SaRequest req = SaHolder.getRequest();
String code = req.getParam("code");
String state = req.getParam("state");
String source = req.getParam("source");
String uuid = req.getParam("uuid");
if (StringUtil.isEmpty(code)) {
code = req.getParam("authCode") != null ? req.getParam("authCode") : req.getParam("auth_code");
}
// 是否是微信qq唤醒或者小程序登录
if (StringUtil.isNotEmpty(uuid)) {
try {
return loginByCode(source, code, null, uuid, null);
} catch (Exception e) {
// 更新登录结果
outError(e.getMessage());
}
}
if (StringUtil.isEmpty(req.getParam(AuthConsts.PARAMS_YUNZHUPAAS_TICKET))) {
// 租户列表登陆标识
boolean tenantLogin = StringUtil.isEmpty(req.getParam("tenantLogin")) ? false
: req.getParam("tenantLogin").equals("true") ? true : false;
// 租户列表点击登录调用
if (!tenantLogin) {
// 绑定
socialsBinding(req, code, state, source);
return null;
} else {// 租户列表点击登录
LoginVO loginVO = tenantLogin(req);
return ActionResult.success(MsgCode.OA015.get(), loginVO);
}
} else {
// 票据登陆
if (!isValidYunzhupaasTicket()) {
outError(MsgCode.OA016.get());
return null;
}
// 接受CODE 进行登录
if (SaFoxUtil.isNotEmpty(code)) {
try {
String socialName = req.getParam("socialName");
ActionResult actionResult = loginByCode(source, code, state, null, socialName);
if (400 == actionResult.getCode() || "wechat_applets".equals(req.getParam("source"))) {
return actionResult;
}
return null;
} catch (Exception e) {
// 更新登录结果
outError(e.getMessage());
}
return null;
}
return null;
}
}
/**
* 租户列表登录
*
* @param
* @return
* @copyright 深圳市乐程软件有限公司
* @date 2024/9/21
*/
private LoginVO tenantLogin(SaRequest req) throws LoginException {
String userId = req.getParam("userId");
String account = req.getParam("account");
String tenantId = req.getParam("tenantId");
UserInfo userInfo = new UserInfo();
userInfo.setUserId(userId);
userInfo.setTenantId(tenantId);
userInfo.setUserAccount(tenantId + "@" + account);
// 切换租户
switchTenant(userInfo);
// 获取系统配置
BaseSystemInfo baseSystemInfo = getSysconfig(userInfo);
// 登录账号
super.loginAccount(userInfo, baseSystemInfo);
// 返回登录信息
LoginVO loginVo = getLoginVo(userInfo);
return loginVo;
}
/**
* 第三方绑定
*/
private void socialsBinding(SaRequest req, String code, String state, String source) {
String userId = req.getParam("userId");
String tenantId = req.getParam("tenantId");
PrintWriter out = null;
try {
HttpServletResponse response = ServletUtil.getResponse();
response.setCharacterEncoding("utf-8");
response.setHeader("content-type", "text/html;charset=utf-8");
out = response.getWriter();
JSONObject binding = socialsUserApi.binding(source, userId, tenantId, code, state);
out.print(
"<script>\n" +
"window.opener.postMessage(\'" + binding.toJSONString() + "\', '*');\n" +
"window.open('','_self','');\n" +
"window.close();\n" + "</script>");
out.close();
} catch (Exception e) {
}
}
public ActionResult logout() {
return super.logout();
}
public int getOrder() {
return ORDER;
}
@Override
protected void loginSuccess(UserInfo userInfo, BaseSystemInfo baseSystemInfo) {
}
@Override
protected void loginFailure(UserInfo userInfo, BaseSystemInfo baseSystemInfo, Exception e) {
super.loginFailure(userInfo, baseSystemInfo, e);
}
protected void outError(String message) {
updateTicketError(message);
}
@Override
protected String getUserDetailKey() {
return AuthConsts.USERDETAIL_USER_ID;
}
protected LoginTicketModel updateTicketUnbind(String socialType, String socialUnionid, String socialName) {
LoginTicketModel loginTicketModel = null;
SocialUnbindModel obj = new SocialUnbindModel(socialType, socialUnionid, socialName);
String ticket = this.getYunzhupaasTicket();
if (!ticket.isEmpty()) {
loginTicketModel = (new LoginTicketModel()).setStatus(LoginTicketStatus.UnBind.getStatus())
.setValue(JSONObject.toJSONString(obj));
TicketUtil.updateTicket(ticket, loginTicketModel, (Long) 300L);
}
return loginTicketModel;
}
protected LoginTicketModel updateTicketMultitenancy(JSONArray jsonArray) {
LoginTicketModel loginTicketModel = null;
String ticket = this.getYunzhupaasTicket();
if (!ticket.isEmpty()) {
loginTicketModel = (new LoginTicketModel()).setStatus(LoginTicketStatus.Multitenancy.getStatus())
.setValue(jsonArray.toJSONString());
TicketUtil.updateTicket(ticket, loginTicketModel, (Long) null);
}
return loginTicketModel;
}
protected LoginTicketModel updateTicketSuccessReturn(UserInfo userInfo) {
LoginTicketModel loginTicketModel = null;
String ticket = getYunzhupaasTicket();
if (!ticket.isEmpty()) {
loginTicketModel = new LoginTicketModel()
.setStatus(LoginTicketStatus.Success.getStatus())
.setValue(StpUtil.getTokenValueNotCut())
.setTheme(userInfo.getTheme());
TicketUtil.updateTicket(ticket, loginTicketModel, null);
}
return loginTicketModel;
}
protected LoginVO getLoginVo(UserInfo userInfo) {
LoginVO loginVO = new LoginVO();
loginVO.setTheme(userInfo.getTheme());
loginVO.setToken(userInfo.getToken());
return loginVO;
}
/**
* 小程序登录微信授权
* app微信qq唤醒
*
* @param code
* @throws LoginException
*/
protected ActionResult loginByCode(String source, String code, String state, String uuid, String socialName)
throws LoginException {
log.debug("Auth2 Code: {}", code);
SocialsUserInfo socialsUserInfo = null;
if (StringUtil.isNotEmpty(code)) {
socialsUserInfo = socialsUserApi.getSocialsUserInfo(source, code, state);
} else if (StringUtil.isNotEmpty(uuid)) {// 微信和qq唤醒
socialsUserInfo = socialsUserApi.getUserInfo(source, uuid, state);
if (StringUtil.isEmpty(socialsUserInfo.getSocialName()) && StringUtil.isNotEmpty(socialName)) {
socialsUserInfo.setSocialName(socialName);// 小程序名称前端传递
}
}
if (configValueUtil.isMultiTenancy()) {
if (socialsUserInfo == null || CollectionUtil.isEmpty(socialsUserInfo.getTenantUserInfo())) {
updateTicketUnbind(source, socialsUserInfo.getSocialUnionid(), socialsUserInfo.getSocialName());// 第三方未绑定账号!
return ActionResult.fail(MsgCode.OA017.get());
}
if (socialsUserInfo.getTenantUserInfo().size() == 1) {
UserInfo userInfo = socialsUserInfo.getUserInfo();
// 切换租户
switchTenant(userInfo);
// 获取系统配置
BaseSystemInfo baseSystemInfo = getSysconfig(userInfo);
// 登录账号
super.loginAccount(userInfo, baseSystemInfo);
// 返回登录信息
LoginTicketModel loginTicketModel = updateTicketSuccessReturn(userInfo);
return ActionResult.success(loginTicketModel);
} else {
JSONArray tenantUserInfo = socialsUserInfo.getTenantUserInfo();
LoginTicketModel loginTicketModel = updateTicketMultitenancy(tenantUserInfo);
return ActionResult.success(loginTicketModel);
}
} else {
if (socialsUserInfo == null || socialsUserInfo.getUserInfo() == null) {
updateTicketUnbind(source, socialsUserInfo.getSocialUnionid(), socialsUserInfo.getSocialName());// 第三方未绑定账号!
return ActionResult.fail(MsgCode.OA017.get());
}
UserInfo userInfo = socialsUserInfo.getUserInfo();
// 切换租户
switchTenant(userInfo);
// 获取系统配置
BaseSystemInfo baseSystemInfo = getSysconfig(userInfo);
// 登录账号
super.loginAccount(userInfo, baseSystemInfo);
LoginTicketModel loginTicketModel = updateTicketSuccessReturn(userInfo);
return ActionResult.success(loginTicketModel);
}
}
}

View File

@@ -0,0 +1,141 @@
package com.yunzhupaas.granter;
import com.yunzhupaas.base.ActionResult;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.consts.AuthConsts;
import com.yunzhupaas.consts.DeviceType;
import com.yunzhupaas.exception.LoginException;
import com.yunzhupaas.model.BaseSystemInfo;
import com.yunzhupaas.model.LoginVO;
import com.yunzhupaas.util.UserProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Map;
import static com.yunzhupaas.granter.TempUserTokenGranter.GRANT_TYPE;
/**
* 临时用户认证
* @user N
* @author 云筑产品开发平台组
* @copyright 深圳市乐程软件有限公司
* @date 2024/9/16 21:25
*/
@Slf4j
@Component(GRANT_TYPE)
public class TempUserTokenGranter extends AbstractTokenGranter{
public static final String GRANT_TYPE = "tempuser";
public static final Integer ORDER = 4;
private static final String URL_LOGIN = "";
public TempUserTokenGranter(){
super(URL_LOGIN);
}
/**
*
* @param loginParameters {userId, tenantId}
* @return
* @throws LoginException
*/
@Override
public ActionResult granter(Map<String, String> loginParameters) throws LoginException {
String token = loginParameters.get("token");
//验证是否由内部发起
if(!UserProvider.isValidInnerToken(token)){
throw new LoginException(MsgCode.OA018.get());
}
String userId = loginParameters.get("userId");
String tenantId = loginParameters.get("tenantId");
boolean limited = Boolean.valueOf(loginParameters.get("limited"));
String device = limited?DeviceType.TEMPUSERLIMITED.getDevice():DeviceType.TEMPUSER.getDevice();
//尝试获取已经登录的用户信息
UserInfo userInfo;
if(limited){
//只获取限制类型的TOKEN
userInfo = UserProvider.getUser(userId, tenantId, Arrays.asList(DeviceType.TEMPUSERLIMITED.getDevice()), null);
}else{
//排除限制类型的TOKEN
userInfo = UserProvider.getUser(userId, tenantId, null, Arrays.asList(DeviceType.TEMPUSERLIMITED.getDevice()));
}
if(userInfo.getUserId() != null){
return ActionResult.success(getLoginVo(userInfo));
}
userInfo = UserProvider.getUser();
userInfo.setUserAccount(tenantId);
userInfo.setUserId(userId);
//切换租户
switchTenant(userInfo);
//获取系统配置
BaseSystemInfo baseSystemInfo = getSysconfig(userInfo);
//先设置用户类型
userInfo.setLoginDevice(device);
//登录账号
super.loginAccount(userInfo, baseSystemInfo);
//返回登录信息
LoginVO loginResult = getLoginVo(userInfo);
return ActionResult.success(loginResult);
}
@Override
public int getOrder() {
return ORDER;
}
@Override
protected void preLogin(UserInfo userInfo, BaseSystemInfo baseSystemInfo) throws LoginException {
}
@Override
protected void login(UserInfo userInfo, BaseSystemInfo baseSystemInfo) throws LoginException {
UserProvider.loginNoRequest(userInfo, this.getLoginModel(userInfo, baseSystemInfo));
}
@Override
protected void loginSuccess(UserInfo userInfo, BaseSystemInfo baseSystemInfo) {
super.loginSuccess(userInfo, baseSystemInfo);
}
@Override
protected DeviceType getDeviceType() {
return DeviceType.TEMPUSER;
}
protected LoginVO getLoginVo(UserInfo userInfo){
LoginVO loginVO = new LoginVO();
loginVO.setTheme(userInfo.getTheme());
loginVO.setToken(userInfo.getToken());
return loginVO;
}
@Override
public ActionResult logout() {
//非临时用户不注销
UserInfo userInfo = UserProvider.getUser();
if(UserProvider.isTempUser(userInfo)){
UserProvider.logoutByToken(userInfo.getToken());
}
return ActionResult.success(MsgCode.OA014.get());
}
@Override
protected String getGrantType() {
return GRANT_TYPE;
}
@Override
protected String getUserDetailKey() {
return AuthConsts.USERDETAIL_USER_ID;
}
}

View File

@@ -0,0 +1,85 @@
package com.yunzhupaas.granter;
import com.yunzhupaas.base.ActionResult;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.consts.AuthConsts;
import com.yunzhupaas.database.util.TenantDataSourceUtil;
import com.yunzhupaas.exception.LoginException;
import com.yunzhupaas.model.BaseSystemInfo;
import com.yunzhupaas.model.LoginForm;
import com.yunzhupaas.model.LoginVO;
import com.yunzhupaas.permission.entity.UserEntity;
import com.yunzhupaas.util.JsonUtil;
import com.yunzhupaas.util.LoginHolder;
import com.yunzhupaas.util.UserProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
import static com.yunzhupaas.granter.YunzhupaasOfficialTokenGranter.GRANT_TYPE;
/**
* YUNZHUPAAS官网专用短信认证
*
* @author 云筑产品开发平台组
* @user N
* @copyright 深圳市乐程软件有限公司
* @date 2024/9/17 22:13
*/
@Slf4j
@Component(GRANT_TYPE)
public class YunzhupaasOfficialTokenGranter extends PasswordTokenGranter {
public static final String GRANT_TYPE = "official";
@Autowired
private UserDetailsServiceBuilder userDetailsServiceBuilder;
@Override
public ActionResult granter(Map<String, String> loginParameters) throws LoginException {
LoginForm loginForm = JsonUtil.getJsonToBean(loginParameters, LoginForm.class);
// 校验短信验证码
TenantDataSourceUtil.checkOfficialSmsCode(loginForm.getAccount(), loginForm.getCode(), 1);
UserInfo userInfo = UserProvider.getUser();
// 切换租户
switchTenant(userInfo);
// 获取系统配置
BaseSystemInfo baseSystemInfo = getSysconfig(userInfo);
// 预检信息
preAuthenticate(loginForm, userInfo, baseSystemInfo);
// 登录账号
super.loginAccount(userInfo, baseSystemInfo);
// 返回登录信息
LoginVO loginResult = getLoginVo(userInfo);
return ActionResult.success(loginResult);
}
/**
* 可重写实现邮箱、短信、TOTP验证
*
* @param loginForm
* @param sysConfigInfo
* @throws LoginException
*/
protected void preAuthenticate(LoginForm loginForm, UserInfo userInfo, BaseSystemInfo sysConfigInfo)
throws LoginException {
// 验证密码
UserEntity userEntity = userDetailsServiceBuilder.getUserDetailService(AuthConsts.USERDETAIL_ACCOUNT)
.loadUserEntity(userInfo);
try {
authenticateLock(userEntity, sysConfigInfo);
} catch (Exception e) {
authenticateFailure(userEntity, sysConfigInfo);
throw e;
}
LoginHolder.setUserEntity(userEntity);
}
@Override
protected String getGrantType() {
return GRANT_TYPE;
}
}

View File

@@ -0,0 +1,213 @@
package com.yunzhupaas.listener;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.base.model.module.ModuleModel;
import com.yunzhupaas.constant.EventConst;
import com.yunzhupaas.event.ProjectEventListener;
import com.yunzhupaas.module.ProjectEventInstance;
import com.yunzhupaas.permission.entity.RoleEntity;
import com.yunzhupaas.permission.model.authorize.AuthorizeVO;
import com.yunzhupaas.permission.service.RoleService;
import com.yunzhupaas.permissions.PermissionInterfaceImpl;
import com.yunzhupaas.util.JsonUtil;
import com.yunzhupaas.util.StringUtil;
import com.yunzhupaas.util.UserProvider;
import com.yunzhupaas.workflow.service.TemplateApi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
import static com.yunzhupaas.util.Constants.ADMIN_KEY;
/**
* 接口权限初始化
*/
@Slf4j
@Component
public class InterfacePermissionListener {
@Autowired
private RoleService roleApi;
@Autowired
private LockTemplate lockTemplate;
@Autowired
private TemplateApi templateApi;
@ProjectEventListener(channel = EventConst.EVENT_INIT_LOGIN_PERMISSION)
public void initPermission(ProjectEventInstance eventInstance) {
UserInfo userInfo = UserProvider.getUser();
//远程事件
// AuthorizeVO authorizeVO = ((JSONObject)eventInstance.getSource()).toJavaObject(AuthorizeVO.class);
//本地事件
AuthorizeVO authorizeVO = (AuthorizeVO) eventInstance.getSource();
initSecurityAuthorities(authorizeVO, userInfo);
}
/**
* 初始化接口鉴权用的账号权限
* 本接口插入权限缓存, SaInterfaceImpl中框架鉴权时动态调用获取权限列表
*
* @param authorizeModel
* @param userInfo
*/
private void initSecurityAuthorities(AuthorizeVO authorizeModel, UserInfo userInfo) {
//接口权限
Set<String> authorityList = new HashSet<>();
Set<String> roleAuthorityList = new HashSet<>();
Map<String, ModuleModel> moduleModelMap = new LinkedHashMap<>();
Map<String, String> flowFormMap = new HashMap<>();
Map<String, String> allFlowFormMap = templateApi.getFlowFormMap();
Map<String, List<Map<String, Object>>> columnsMap = new HashMap<>();
Map<String, List<Map<String, Object>>> formMap = new HashMap<>();
for (ModuleModel moduleModel : authorizeModel.getModuleList()) {
// 添加菜单权限
// 添加菜单ID, 代码生成用
authorityList.add(moduleModel.getId());
// 添加菜单编码
authorityList.add(moduleModel.getEnCode());
moduleModelMap.put(moduleModel.getId(), moduleModel);
//功能菜单 3功能菜单, 9: 流程菜单
if (moduleModel.getType() == 3 || moduleModel.getType() == 9 || moduleModel.getType() == 10) {
JSONObject propertyJSON = JSONObject.parseObject(Optional.of(moduleModel.getPropertyJson()).orElse("{}"));
//{"iconBackgroundColor":"","isTree":0,"moduleId":"395851986114733317"}
String moduleId = propertyJSON.getString("moduleId");
if (!StringUtil.isEmpty(moduleId)) {
authorityList.add(moduleId);
// 流程菜单 拥有流程菜单权限, 直接赋予流程下第一个表单的权限
if (moduleModel.getType() == 9) {
// 表单编码
String formId = flowFormMap.get(moduleId);
if (formId == null) {
formId = allFlowFormMap.get(moduleId);
}
if (StringUtil.isNotEmpty(formId)) {
flowFormMap.put(moduleId, formId);
authorityList.add(formId);
} else {
log.error("初始化流程菜单权限失败, 流程表单不存在:" + moduleModel.getFullName());
}
}
}
}
}
//按钮权限 菜单编码::按钮编码
authorizeModel.getButtonList().forEach(t -> {
ModuleModel m = moduleModelMap.get(t.getModuleId());
if (m != null) {
authorityList.add(m.getEnCode() + "::" + t.getEnCode());
//功能菜单的按钮权限 3功能菜单, 9: 流程菜单
if (m.getType() == 3 || m.getType() == 9) {
JSONObject propertyJSON = JSONObject.parseObject(Optional.of(m.getPropertyJson()).orElse("{}"));
//{"iconBackgroundColor":"","isTree":0,"moduleId":"395851986114733317"}
String moduleId = propertyJSON.getString("moduleId");
if (!StringUtil.isEmpty(moduleId)) {
authorityList.add(moduleId + "::" + t.getEnCode());
// 流程菜单, 直接赋予流程下第一个表单的对应流程按钮权限
if (m.getType() == 9) {
// 表单编码
String formId = flowFormMap.get(moduleId);
if (StringUtil.isNotEmpty(formId)) {
authorityList.add(formId + "::" + t.getEnCode());
}
}
}
}
}
});
//列表权限 菜单编码::列表编码
authorizeModel.getColumnList().forEach(t -> {
ModuleModel m = moduleModelMap.get(t.getModuleId());
if (m != null) {
authorityList.add(m.getEnCode() + "::" + t.getEnCode());
}
List<Map<String, Object>> list;
if (columnsMap.get(t.getModuleId()) == null) {
list = new ArrayList<>();
list.add(JsonUtil.entityToMap(t));
columnsMap.put(t.getModuleId(), list);
} else {
list = columnsMap.get(t.getModuleId());
list.add(JsonUtil.entityToMap(t));
columnsMap.put(t.getModuleId(), list);
}
});
//表单权限 菜单编码::表单编码
authorizeModel.getFormsList().forEach(t -> {
ModuleModel m = moduleModelMap.get(t.getModuleId());
if (m != null) {
authorityList.add(m.getEnCode() + "::" + t.getEnCode());
}
List<Map<String, Object>> list;
if (formMap.get(t.getModuleId()) == null) {
list = new ArrayList<>();
list.add(JsonUtil.entityToMap(t));
formMap.put(t.getModuleId(), list);
} else {
list = formMap.get(t.getModuleId());
list.add(JsonUtil.entityToMap(t));
formMap.put(t.getModuleId(), list);
}
});
if (userInfo.getRoleIds() != null && !userInfo.getRoleIds().isEmpty() || userInfo.getIsAdministrator()) {
List<RoleEntity> roles;
if (userInfo.getIsAdministrator()) {
roles = roleApi.getList(false);
} else {
roles = roleApi.getListByIds(userInfo.getRoleIds(), null, false);
}
roleAuthorityList = roles.stream().filter(r -> r.getEnabledMark().equals(1)).map(r -> "ROLE_" + r.getEnCode()).collect(Collectors.toSet());
}
//管理员都是用同一个缓存, 普通账号使用账号名,
//权限列表authorize_:租户_authorize_authorize_(admin|账号)
//角色列表authorize_:租户_authorize_role_(admin|账号)
String account = userInfo.getIsAdministrator() ? ADMIN_KEY : userInfo.getUserId();
LockInfo lockInstance = null;
try {
if (!authorityList.isEmpty() || !roleAuthorityList.isEmpty()) {
String loginId = UserProvider.splicingLoginId(account);
// 等待其他服务初始化权限完毕
// lockInstance = lockTemplate.lock(RedisConst.REDIS_LOCK4J_PREFIX + ModuleName.OAUTH_SERVER_NAME + loginId, 10000L, 10000L);
// if(lockInstance != null) {
if (!authorityList.isEmpty()) {
// 添加旧权限
authorityList.addAll(StpUtil.getPermissionList(loginId));
PermissionInterfaceImpl.setAuthorityList(account, authorityList, null);
}
if (!roleAuthorityList.isEmpty()) {
// 添加旧权限
roleAuthorityList.addAll(StpUtil.getRoleList(loginId));
PermissionInterfaceImpl.setRoleList(account, roleAuthorityList, null);
}
if (!columnsMap.isEmpty()) {
PermissionInterfaceImpl.setColumnMap(account, columnsMap, null);
}
if (!formMap.isEmpty()) {
PermissionInterfaceImpl.setFormMap(account, formMap, null);
}
// }
}
} catch (Exception e) {
log.error("初始化接口权限失败", e);
} finally {
// if(lockInstance != null) {
// lockTemplate.releaseLock(lockInstance);
// }
}
}
}

View File

@@ -0,0 +1,88 @@
package com.yunzhupaas.listener;
import cn.dev33.satoken.listener.SaTokenListenerForSimple;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.consts.DeviceType;
import com.yunzhupaas.message.service.MessageService;
import com.yunzhupaas.permission.entity.UserEntity;
import com.yunzhupaas.permission.service.UserService;
import com.yunzhupaas.service.LogService;
import com.yunzhupaas.util.DateUtil;
import com.yunzhupaas.util.IpUtil;
import com.yunzhupaas.util.LoginHolder;
import com.yunzhupaas.util.UserProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
*
* @author 云筑产品开发平台组
* @copyright 深圳市乐程软件有限公司
*/
@Slf4j
@Component
public class LoginListener extends SaTokenListenerForSimple {
@Autowired
private MessageService messageApi;
@Autowired
private UserService userApi;
@Override
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
println( MsgCode.OA001.get()+"{}, "+MsgCode.OA002.get()+"{}, "+MsgCode.OA003.get()+"{}", loginId, loginType, tokenValue);
UserInfo userInfo = UserProvider.getUser();
//临时用户登录不记录
if(!UserProvider.isTempUser(userInfo)) {
UserEntity entity = LoginHolder.getUserEntity();
entity.setLogErrorCount(0);
entity.setUnlockTime(null);
entity.setEnabledMark(1);
entity.setPrevLogIp(IpUtil.getIpAddr());
entity.setPrevLogTime(DateUtil.getNowDate());
entity.setLastLogIp(IpUtil.getIpAddr());
entity.setLastLogTime(DateUtil.getNowDate());
entity.setLogSuccessCount(entity.getLogSuccessCount() != null ? entity.getLogSuccessCount() + 1 : 1);
userApi.updateById(entity);
}
}
@Override
public void doLogout(String loginType, Object loginId, String tokenValue) {
println(MsgCode.OA004.get()+"{}, "+MsgCode.OA002.get()+"{}, "+MsgCode.OA003.get()+"{}", loginId, loginType, tokenValue);
}
@Override
public void doKickout(String loginType, Object loginId, String tokenValue) {
println(MsgCode.OA005.get()+"{}, "+MsgCode.OA002.get()+"{}, "+MsgCode.OA003.get()+"{}", loginId, loginType, tokenValue);
messageApi.logoutWebsocketByToken(tokenValue, null);
//删除用户信息缓存, 保留Token状态记录等待自动过期, 如果用户不在线下次打开浏览器会提示被踢下线
StpUtil.getTokenSessionByToken(tokenValue).logout();
}
@Override
public void doReplaced(String loginType, Object loginId, String tokenValue) {
println(MsgCode.OA006.get()+"{}, "+MsgCode.OA002.get()+"{}, "+MsgCode.OA003.get()+"{}", loginId, loginType, tokenValue);
messageApi.logoutWebsocketByToken(tokenValue, null);
StpUtil.getTokenSessionByToken(tokenValue).logout();
}
/**
* 打印指定字符串
* @param str 字符串
*/
public void println(String str, Object... params) {
if(log.isDebugEnabled()) {
log.debug(str, params);
}
}
}

View File

@@ -0,0 +1,30 @@
package com.yunzhupaas.listener;
import com.yunzhupaas.config.ConfigValueUtil;
import com.yunzhupaas.util.RedisUtil;
import com.yunzhupaas.util.context.SpringContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
/**
*
* @author 云筑产品开发平台组
* @version V3.1.0
* @copyright 深圳市乐程软件有限公司
* @date 2024/3/16 8:49
*/
@Component
public class YunzhupaasListener implements ApplicationListener<ContextRefreshedEvent> {
private ConfigValueUtil configValueUtil;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
configValueUtil = SpringContext.getBean(ConfigValueUtil.class);
if ("false".equals(configValueUtil.getTestVersion())) {
RedisUtil redisUtil = SpringContext.getBean(RedisUtil.class);
redisUtil.removeAll();
}
}
}

View File

@@ -0,0 +1,109 @@
package com.yunzhupaas.service.impl;
import com.yunzhupaas.base.ActionResult;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.config.ConfigValueUtil;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.exception.LoginException;
import com.yunzhupaas.exception.TenantDatabaseException;
import com.yunzhupaas.exception.TenantInvalidException;
import com.yunzhupaas.granter.TokenGranter;
import com.yunzhupaas.granter.TokenGranterBuilder;
import com.yunzhupaas.model.LoginVO;
import com.yunzhupaas.service.AuthService;
import com.yunzhupaas.service.LogService;
import com.yunzhupaas.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* 登录与退出服务 其他服务调用
*/
@Service
@Slf4j
public class AuthServiceImpl implements AuthService {
@Autowired
private TokenGranterBuilder tokenGranterBuilder;
@Autowired
private LogService logApi;
@Autowired
private ConfigValueUtil configValueUtil;
@Autowired
private RedisUtil redisUtil;
/**
* 登录
* @param parameters {grant_type}
* @return
* @throws LoginException
*/
@Override
public ActionResult<LoginVO> login(Map<String, String> parameters) throws LoginException{
long millis = System.currentTimeMillis();
TokenGranter tokenGranter = tokenGranterBuilder.getGranter(parameters.getOrDefault("grant_type", ""));
ActionResult<LoginVO> result;
UserInfo userInfo = new UserInfo();
try {
String account = parameters.get("account");
userInfo.setUserAccount(account);
UserProvider.setLocalLoginUser(userInfo);
result = tokenGranter.granter(parameters);
//写入日志
if (userInfo.getUserId() != null && !UserProvider.isTempUser(userInfo)) {
logApi.writeLogAsync(userInfo.getUserId(), userInfo.getUserName() + "/" + userInfo.getUserAccount(), MsgCode.OA015.get(), (System.currentTimeMillis() - millis));
}
} catch (TenantDatabaseException tdex){
throw tdex;
} catch (Exception e){
if(!(e instanceof LoginException || e instanceof TenantInvalidException)){
String msg = e.getMessage();
if(msg == null){
msg = MsgCode.OA007.get();
}
log.error(MsgCode.OA007.get()+", Account: {}, Error: {}", parameters.getOrDefault("account", ""), e.getMessage(), e);
throw new LoginException(msg);
}
String userName = StringUtil.isNotEmpty(userInfo.getUserName()) ? userInfo.getUserName()+"/"+userInfo.getUserAccount() : userInfo.getUserAccount();
logApi.writeLogAsync(userInfo.getUserId(), userName, e.getMessage(), userInfo, 0, null, (System.currentTimeMillis()-millis));
throw e;
}finally{
LoginHolder.clearUserEntity();
TenantProvider.clearBaseSystemIfo();
// 请求之后就删除验证码 不论结果
String imgCode = parameters.get("timestamp");
if(StringUtil.isNotEmpty(imgCode)) {
redisUtil.remove(imgCode);
}
}
return result;
}
/**
* 踢出用户, 用户将收到Websocket下线通知
* 执行流程:认证服务退出用户->用户踢出监听->消息服务发送Websocket推送退出消息
* @param tokens
*/
@Override
public ActionResult kickoutByToken(String... tokens){
UserProvider.kickoutByToken(tokens);
return ActionResult.success();
}
/**
* 踢出用户, 用户将收到Websocket下线通知
* 执行流程:认证服务退出用户->用户踢出监听->消息服务发送Websocket推送退出消息
* @param userId
* @param tenantId
*/
@Override
public ActionResult kickoutByUserId(String userId, String tenantId){
UserProvider.kickoutByUserId(userId, tenantId);
return ActionResult.success();
}
}

View File

@@ -0,0 +1,38 @@
package com.yunzhupaas.service.impl;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.consts.AuthConsts;
import com.yunzhupaas.exception.LoginException;
import com.yunzhupaas.permission.entity.UserEntity;
import com.yunzhupaas.permission.service.UserService;
import com.yunzhupaas.service.UserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 默认使用用户名获取用户信息
*/
@Service(AuthConsts.USERDETAIL_ACCOUNT)
public class UserDetailsByUserAccountServiceImpl implements UserDetailService {
@Autowired
private UserService userApi;
@Override
public UserEntity loadUserEntity(UserInfo userInfo) throws LoginException {
UserEntity userEntity = userApi.getUserByAccount(userInfo.getUserAccount());
if (userEntity == null) {
throw new LoginException(MsgCode.LOG101.get());
}
return userEntity;
}
@Override
public int getOrder() {
return Integer.MAX_VALUE;
}
}

View File

@@ -0,0 +1,40 @@
package com.yunzhupaas.service.impl;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.consts.AuthConsts;
import com.yunzhupaas.exception.LoginException;
import com.yunzhupaas.permission.entity.UserEntity;
import com.yunzhupaas.permission.service.UserService;
import com.yunzhupaas.service.UserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 使用用户ID获取用户信息
*/
@Service(AuthConsts.USERDETAIL_USER_ID)
public class UserDetailsByUserIdServiceImpl implements UserDetailService {
private static final Integer ORDER = 1;
@Autowired
private UserService userApi;
@Override
public UserEntity loadUserEntity(UserInfo userInfo) throws LoginException {
UserEntity userEntity = userApi.getInfo(userInfo.getUserId());
if (userEntity == null) {
throw new LoginException(MsgCode.LOG101.get());
}
return userEntity;
}
@Override
public int getOrder() {
return ORDER;
}
}

View File

@@ -0,0 +1,28 @@
package com.yunzhupaas.util;
import com.yunzhupaas.permission.entity.UserEntity;
/**
*
* @author 云筑产品开发平台组
* @copyright 深圳市乐程软件有限公司
*/
public class LoginHolder {
private static final ThreadLocal<UserEntity> USER_CACHE = new ThreadLocal<>();
public static UserEntity getUserEntity(){
return USER_CACHE.get();
}
public static void setUserEntity(UserEntity userEntity){
USER_CACHE.set(userEntity);
}
public static void clearUserEntity(){
USER_CACHE.remove();
}
}