diff --git a/powerjob-server/pom.xml b/powerjob-server/pom.xml index 5eaec38d..8bb0ed3b 100644 --- a/powerjob-server/pom.xml +++ b/powerjob-server/pom.xml @@ -22,6 +22,7 @@ powerjob-server-migrate powerjob-server-core powerjob-server-monitor + powerjob-server-auth diff --git a/powerjob-server/powerjob-server-auth/pom.xml b/powerjob-server/powerjob-server-auth/pom.xml new file mode 100644 index 00000000..79746431 --- /dev/null +++ b/powerjob-server/powerjob-server-auth/pom.xml @@ -0,0 +1,58 @@ + + + + + tech.powerjob + powerjob-server + 4.3.7 + + + 4.0.0 + + powerjob-server-auth + ${project.parent.version} + + + 8 + 8 + UTF-8 + 0.11.5 + 1.1.86 + + + + + tech.powerjob + powerjob-server-persistence + provided + + + io.jsonwebtoken + jjwt-api + ${jjwt.version} + + + io.jsonwebtoken + jjwt-impl + ${jjwt.version} + runtime + + + io.jsonwebtoken + jjwt-jackson + ${jjwt.version} + runtime + + + + com.aliyun + dingtalk + ${dingtalk.version} + + + + + + \ No newline at end of file diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/Permission.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/Permission.java new file mode 100644 index 00000000..e6fabb4a --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/Permission.java @@ -0,0 +1,40 @@ +package tech.powerjob.server.auth; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 权限 + * + * @author tjq + * @since 2023/3/20 + */ +@Getter +@AllArgsConstructor +public enum Permission { + + /** + * 不需要权限 + */ + NONE(1), + /** + * 读权限,查看控制台数据 + */ + READ(10), + /** + * 写权限,新增/修改任务等 + */ + WRITE(20), + /** + * 运维权限,比如任务的执行 + */ + OPS(30), + /** + * 超级权限 + */ + SU(100) + ; + + + private int v; +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/Role.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/Role.java new file mode 100644 index 00000000..9b0280ab --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/Role.java @@ -0,0 +1,53 @@ +package tech.powerjob.server.auth; + +import com.google.common.collect.Sets; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Set; + +import static tech.powerjob.server.auth.Permission.*; + +/** + * 角色 + * PowerJob 采用 RBAC 实现权限,出于实际需求的考虑,不决定采用动态权限模型。因此 RBAC 中的角色和权限均在此处定义。 + * 如果有自定义诉求,可以修改 Role 的定义 + * + * @author tjq + * @since 2023/3/20 + */ +@Getter +@AllArgsConstructor +public enum Role { + + /** + * 观察者,默认只读权限 + */ + OBSERVER(10, Sets.newHashSet(READ)), + /** + * 技术质量,读 + 操作权限 + */ + QA(20, Sets.newHashSet(READ, OPS)), + /** + * 开发者,读 + 编辑 + 操作权限 + */ + DEVELOPER(30, Sets.newHashSet(READ, WRITE, OPS)), + /** + * 管理员 + */ + ADMIN(40, Sets.newHashSet(READ, WRITE, OPS, SU)) + ; + + private final int v; + + private final Set permissions; + + public static Role of(int vv) { + for (Role role : values()) { + if (vv == role.v) { + return role; + } + } + throw new IllegalArgumentException("unknown role: " + vv); + } +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/RoleScope.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/RoleScope.java new file mode 100644 index 00000000..328d9acf --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/RoleScope.java @@ -0,0 +1,31 @@ +package tech.powerjob.server.auth; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 权限范围 + * + * @author tjq + * @since 2023/9/3 + */ +@Getter +@AllArgsConstructor +public enum RoleScope { + + /** + * NAMESPACE 权限 + */ + NAMESPACE(1), + /** + * APP 级别权限 + */ + APP(10), + /** + * 全局权限 + */ + GLOBAL(666) + ; + + private final int v; +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/LoginContext.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/LoginContext.java new file mode 100644 index 00000000..16839dd5 --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/LoginContext.java @@ -0,0 +1,25 @@ +package tech.powerjob.server.auth.login; + +import lombok.Data; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 登录上下文 + * + * @author tjq + * @since 2024/2/10 + */ +@Data +public class LoginContext { + + /** + * 原始参数,给第三方登录方式一个服务端和前端交互的数据通道。PowerJob 本身不感知其中的内容 + */ + private String originParams; + + private transient HttpServletRequest httpServletRequest; + + private transient HttpServletResponse httpServletResponse; +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/LoginTypeInfo.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/LoginTypeInfo.java new file mode 100644 index 00000000..0ecf38a1 --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/LoginTypeInfo.java @@ -0,0 +1,30 @@ +package tech.powerjob.server.auth.login; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 登录类型描述 + * + * @author tjq + * @since 2024/2/10 + */ +@Data +@Accessors(chain = true) +public class LoginTypeInfo implements Serializable { + + /** + * 登录类型,唯一标识 + */ + private String type; + /** + * 描述名称,前端展示用 + */ + private String name; + /** + * 展示用的 ICON + */ + private String iconUrl; +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/ThirdPartyLoginService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/ThirdPartyLoginService.java new file mode 100644 index 00000000..893eb851 --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/ThirdPartyLoginService.java @@ -0,0 +1,31 @@ +package tech.powerjob.server.auth.login; + +/** + * 第三方登录服务 + * + * @author tjq + * @since 2024/2/10 + */ +public interface ThirdPartyLoginService { + + /** + * 登陆服务的类型 + * @return 登陆服务类型,比如 PowerJob / DingTalk + */ + LoginTypeInfo loginType(); + + /** + * 生成登陆的重定向 URL + * @param loginContext 上下文 + * @return 重定向地址 + */ + String generateLoginUrl(LoginContext loginContext); + + /** + * 执行第三方登录 + * @param loginContext 上下文 + * @return 登录地址 + */ + ThirdPartyUser login(LoginContext loginContext); + +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/ThirdPartyUser.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/ThirdPartyUser.java new file mode 100644 index 00000000..e5d2e739 --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/ThirdPartyUser.java @@ -0,0 +1,38 @@ +package tech.powerjob.server.auth.login; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 第三方用户 + * + * @author tjq + * @since 2024/2/10 + */ +@Data +@Accessors(chain = true) +public class ThirdPartyUser { + + /** + * 用户的唯一标识,用于关联到 PowerJob 的 username + */ + private String username; + + /* ******** 以下全部选填即可,只是方便数据同步,后续都可以去 PowerJob 控制台更改 ******** */ + /** + * 用户昵称 + */ + private String nick; + /** + * 手机号 + */ + private String phone; + /** + * 邮箱地址 + */ + private String email; + /** + * 扩展字段 + */ + private String extra; +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DingTalkLoginService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DingTalkLoginService.java new file mode 100644 index 00000000..d3638917 --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/DingTalkLoginService.java @@ -0,0 +1,143 @@ +package tech.powerjob.server.auth.login.impl; + +import com.aliyun.dingtalkcontact_1_0.models.GetUserHeaders; +import com.aliyun.dingtalkcontact_1_0.models.GetUserResponseBody; +import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenRequest; +import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenResponse; +import com.aliyun.teaopenapi.models.Config; +import com.aliyun.teautil.models.RuntimeOptions; +import lombok.SneakyThrows; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import tech.powerjob.common.exception.PowerJobException; +import tech.powerjob.server.auth.login.LoginContext; +import tech.powerjob.server.auth.login.LoginTypeInfo; +import tech.powerjob.server.auth.login.ThirdPartyLoginService; +import tech.powerjob.server.auth.login.ThirdPartyUser; +import tech.powerjob.server.common.Loggers; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * 钉钉账号体系登录第三方网站 + * PowerJob 官方支持钉钉账号体系登录原因: + * 1. 钉钉作为当下用户体量最大的企业级办公软件,覆盖率足够高,提供钉钉支持能让更多开发者开箱即用 + * 2. 钉钉的 API 设计和 PowerJob 设想一致,算是个最佳实践,其他企业内部的账号体系可参考这套流程进行接入 + * - PowerJob 重定向到第三方账号体系登陆页 -> 第三方完成登陆 -> 跳转回调 PowerJob auth 接口 -> PowerJob 解析回调登陆信息,完整用户关联 + * + * @author tjq + * @since 2023/3/26 + */ +public class DingTalkLoginService implements ThirdPartyLoginService { + + /* + 配置示例 + oms.auth.dingtalk.appkey=dinggzqqzqqzqqzqq + oms.auth.dingtalk.appSecret=iY-FS8mzqqzqq_xEizqqzqqzqqzqqzqqzqqYEbkZOal + oms.auth.dingtalk.callbackUrl=http://localhost:7700/auth/loginCallback + */ + + /** + * 钉钉应用 AppKey + */ + @Value("${oms.auth.dingtalk.appkey:#{null}}") + private String dingTalkAppKey; + /** + * 钉钉应用 AppSecret + */ + @Value("${oms.auth.dingtalk.appSecret:#{null}}") + private String dingTalkAppSecret; + /** + * 回调地址,powerjob-server 地址 + /user/auth + * 比如本地调试时为 LocalDemoCallbackUrl + * 部署后则为 demoCallBackUrl + */ + @Value("${oms.auth.dingtalk.callbackUrl:#{null}}") + private String dingTalkCallbackUrl; + + private static final String DING_TALK = "DingTalk"; + + @Override + public LoginTypeInfo loginType() { + return new LoginTypeInfo() + .setType(DING_TALK) + .setName("钉钉登录") + ; + } + + @Override + @SneakyThrows + public String generateLoginUrl(LoginContext loginContext) { + if (StringUtils.isAnyEmpty(dingTalkAppKey, dingTalkAppSecret, dingTalkCallbackUrl)) { + throw new IllegalArgumentException("please config 'oms.auth.dingtalk.appkey', 'oms.auth.dingtalk.appSecret' and 'oms.auth.dingtalk.callbackUrl' in properties!"); + } + + String urlString = URLEncoder.encode(dingTalkCallbackUrl, StandardCharsets.UTF_8.name()); + String url = "https://login.dingtalk.com/oauth2/auth?" + + "redirect_uri=" + urlString + + "&response_type=code" + + "&client_id=" + dingTalkAppKey + + "&scope=openid" + + "&state=" + DING_TALK + + "&prompt=consent"; + Loggers.WEB.info("[DingTalkBizLoginService] login url: {}", url); + return url; + } + + @Override + @SneakyThrows + public ThirdPartyUser login(LoginContext loginContext) { + try { + com.aliyun.dingtalkoauth2_1_0.Client client = authClient(); + GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest() + //应用基础信息-应用信息的AppKey,请务必替换为开发的应用AppKey + .setClientId(dingTalkAppKey) + //应用基础信息-应用信息的AppSecret,,请务必替换为开发的应用AppSecret + .setClientSecret(dingTalkAppSecret) + .setCode(loginContext.getHttpServletRequest().getParameter("authCode")) + .setGrantType("authorization_code"); + GetUserTokenResponse getUserTokenResponse = client.getUserToken(getUserTokenRequest); + //获取用户个人 token + String accessToken = getUserTokenResponse.getBody().getAccessToken(); + // 查询钉钉用户 + final GetUserResponseBody dingUser = getUserinfo(accessToken); + // 将钉钉用户的唯一ID 和 PowerJob 账户体系的唯一键 username 关联 + if (dingUser != null) { + ThirdPartyUser bizUser = new ThirdPartyUser(); + bizUser.setUsername(dingUser.getUnionId()); + bizUser.setNick(dingUser.getNick()); + bizUser.setPhone(dingUser.getMobile()); + bizUser.setEmail(dingUser.getEmail()); + return bizUser; + } + } catch (Exception e) { + Loggers.WEB.error("[DingTalkBizLoginService] login by dingTalk failed!", e); + throw e; + } + throw new PowerJobException("login from dingTalk failed!"); + } + + /* 以下代码均拷自钉钉官网示例 */ + + private static com.aliyun.dingtalkoauth2_1_0.Client authClient() throws Exception { + Config config = new Config(); + config.protocol = "https"; + config.regionId = "central"; + return new com.aliyun.dingtalkoauth2_1_0.Client(config); + } + private static com.aliyun.dingtalkcontact_1_0.Client contactClient() throws Exception { + Config config = new Config(); + config.protocol = "https"; + config.regionId = "central"; + return new com.aliyun.dingtalkcontact_1_0.Client(config); + } + + private GetUserResponseBody getUserinfo(String accessToken) throws Exception { + com.aliyun.dingtalkcontact_1_0.Client client = contactClient(); + GetUserHeaders getUserHeaders = new GetUserHeaders(); + getUserHeaders.xAcsDingtalkAccessToken = accessToken; + //获取用户个人信息,如需获取当前授权人的信息,unionId参数必须传me + return client.getUserWithOptions("me", getUserHeaders, new RuntimeOptions()).getBody(); + } +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/PowerJobLoginService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/PowerJobLoginService.java new file mode 100644 index 00000000..0b37a608 --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/login/impl/PowerJobLoginService.java @@ -0,0 +1,85 @@ +package tech.powerjob.server.auth.login.impl; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import tech.powerjob.common.exception.PowerJobException; +import tech.powerjob.server.auth.login.LoginContext; +import tech.powerjob.server.auth.login.LoginTypeInfo; +import tech.powerjob.server.auth.login.ThirdPartyLoginService; +import tech.powerjob.server.auth.login.ThirdPartyUser; +import tech.powerjob.server.common.Loggers; +import tech.powerjob.server.common.SJ; +import tech.powerjob.server.common.utils.DigestUtils; +import tech.powerjob.server.persistence.remote.model.UserInfoDO; +import tech.powerjob.server.persistence.remote.repository.UserInfoRepository; + +import javax.annotation.Resource; +import java.util.Map; +import java.util.Optional; + +/** + * PowerJob 自带的登陆服务 + * 和应用主框架无关,依然属于第三方登录体系 + * + * @author tjq + * @since 2023/3/20 + */ +@Service +public class PowerJobLoginService implements ThirdPartyLoginService { + + @Resource + private UserInfoRepository userInfoRepository; + + private static final String POWER_JOB_LOGIN_SERVICE = "PowerJob"; + + private static final String KEY_USERNAME = "username"; + private static final String KEY_PASSWORD = "password"; + + @Override + public LoginTypeInfo loginType() { + return new LoginTypeInfo() + .setType(POWER_JOB_LOGIN_SERVICE) + .setName("PowerJob's built-in login system") + ; + } + + @Override + public String generateLoginUrl(LoginContext loginContext) { + // 前端实现跳转,服务端返回特殊指令 + return "FE-REDIRECT:PowerJob"; + } + + @Override + public ThirdPartyUser login(LoginContext loginContext) { + final String loginInfo = loginContext.getOriginParams(); + if (StringUtils.isEmpty(loginInfo)) { + throw new IllegalArgumentException("can't find login Info"); + } + + final Map loginInfoMap = SJ.splitKvString(loginInfo); + final String username = loginInfoMap.get(KEY_USERNAME); + final String password = loginInfoMap.get(KEY_PASSWORD); + + if (StringUtils.isAnyEmpty(username, password)) { + Loggers.WEB.debug("[PowerJobLoginService] username or password is empty, login failed!"); + throw new IllegalArgumentException("username or password is empty!"); + } + + final Optional userInfoOpt = userInfoRepository.findByUsername(username); + if (!userInfoOpt.isPresent()) { + Loggers.WEB.debug("[PowerJobLoginService] can't find user by username: {}", username); + throw new PowerJobException("can't find user by username: " + username); + } + + final UserInfoDO dbUser = userInfoOpt.get(); + + if (DigestUtils.rePassword(password, username).equals(dbUser.getPassword())) { + ThirdPartyUser bizUser = new ThirdPartyUser(); + bizUser.setUsername(username); + return bizUser; + } + + Loggers.WEB.debug("[PowerJobLoginService] user[{}]'s password is incorrect, login failed!", username); + throw new PowerJobException("password is incorrect"); + } +} diff --git a/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/Loggers.java b/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/Loggers.java new file mode 100644 index 00000000..20b0e58c --- /dev/null +++ b/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/Loggers.java @@ -0,0 +1,18 @@ +package tech.powerjob.server.common; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 统一定义日志 + * + * @author tjq + * @since 2023/3/25 + */ +public class Loggers { + + /** + * Web 层统一日志 + */ + public static final Logger WEB = LoggerFactory.getLogger("P_SERVER_LOGGER_WEB"); +} diff --git a/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/SJ.java b/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/SJ.java index 89a89730..eb2051a8 100644 --- a/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/SJ.java +++ b/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/SJ.java @@ -3,6 +3,8 @@ package tech.powerjob.server.common; import com.google.common.base.Joiner; import com.google.common.base.Splitter; +import java.util.Map; + /** * Splitter & Joiner * @@ -16,4 +18,9 @@ public class SJ { public static final Joiner MONITOR_JOINER = Joiner.on("|").useForNull("-"); + private static final Splitter.MapSplitter MAP_SPLITTER = Splitter.onPattern(";").withKeyValueSeparator(":"); + + public static Map splitKvString(String kvString) { + return MAP_SPLITTER.split(kvString); + } } diff --git a/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/utils/DigestUtils.java b/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/utils/DigestUtils.java new file mode 100644 index 00000000..9a6bbee8 --- /dev/null +++ b/powerjob-server/powerjob-server-common/src/main/java/tech/powerjob/server/common/utils/DigestUtils.java @@ -0,0 +1,41 @@ +package tech.powerjob.server.common.utils; + +import lombok.SneakyThrows; + +import java.math.BigInteger; +import java.security.MessageDigest; + +/** + * 加密工具 + * + * @author tjq + * @since 2023/3/25 + */ +public class DigestUtils { + + /** + * 32位小写 md5 + * @param input 输入 + * @return md5 + */ + @SneakyThrows + public static String md5(String input) { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(input.getBytes()); + byte[] byteArray = md5.digest(); + + BigInteger bigInt = new BigInteger(1, byteArray); + // 参数16表示16进制 + StringBuilder result = new StringBuilder(bigInt.toString(16)); + // 不足32位高位补零 + while(result.length() < 32) { + result.insert(0, "0"); + } + return result.toString(); + } + + public static String rePassword(String password, String salt) { + String f1 = String.format("%s_%s_z", salt, password); + return String.format("%s_%s_b", salt, md5(f1)); + } +} diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/UserInfoDO.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/UserInfoDO.java index e19bef8c..e13e5724 100644 --- a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/UserInfoDO.java +++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/UserInfoDO.java @@ -26,6 +26,11 @@ public class UserInfoDO { private Long id; private String username; + /** + * since 5.0.0 + * 昵称(第三方登陆的 username 很难识别,方便后续展示引入 nick) + */ + private String nick; private String password; /** diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/UserRoleDO.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/UserRoleDO.java new file mode 100644 index 00000000..22d7b629 --- /dev/null +++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/UserRoleDO.java @@ -0,0 +1,53 @@ +package tech.powerjob.server.persistence.remote.model; + +import lombok.Data; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.util.Date; + +/** + * 用户角色表 + * + * @author tjq + * @since 2023/3/20 + */ +@Data +@Entity +@Table(indexes = { + @Index(name = "uidx01_user_id", columnList = "userId") +}) +public class UserRoleDO { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") + @GenericGenerator(name = "native", strategy = "native") + private Long id; + + /** + * 授予角色的用户ID + */ + private Long userId; + + /** + * 权限范围,namespace 还是 app + */ + private Integer scope; + /** + * 和 scope 一起组成授权目标,比如某个 app 或 某个 namespace + */ + private Long target; + + /** + * 角色,比如 Observer + */ + private Integer role; + /** + * 扩展字段 + */ + private String extra; + + private Date gmtCreate; + + private Date gmtModified; +} diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/UserInfoRepository.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/UserInfoRepository.java index 2a11d207..3e70cef4 100644 --- a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/UserInfoRepository.java +++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/UserInfoRepository.java @@ -4,6 +4,7 @@ import tech.powerjob.server.persistence.remote.model.UserInfoDO; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; +import java.util.Optional; /** * 用户信息表数据库访问层 @@ -13,6 +14,8 @@ import java.util.List; */ public interface UserInfoRepository extends JpaRepository { + Optional findByUsername(String username); + List findByUsernameLike(String username); List findByIdIn(List userIds); diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/UserRoleRepository.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/UserRoleRepository.java new file mode 100644 index 00000000..18949f08 --- /dev/null +++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/UserRoleRepository.java @@ -0,0 +1,19 @@ +package tech.powerjob.server.persistence.remote.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import tech.powerjob.server.persistence.remote.model.UserRoleDO; + +import java.util.List; + +/** + * UserRoleRepository + * + * @author tjq + * @since 2023/3/20 + */ +public interface UserRoleRepository extends JpaRepository { + + List findAllByUserId(Long userId); + + List findAllByScopeAndTarget(Integer scope, Long target); +}