diff --git a/snowy-admin-web/package.json b/snowy-admin-web/package.json
index fb0f217f..33284090 100644
--- a/snowy-admin-web/package.json
+++ b/snowy-admin-web/package.json
@@ -44,6 +44,7 @@
"js-pinyin": "0.2.7",
"lodash-es": "4.17.21",
"nprogress": "0.2.0",
+ "path-to-regexp": "8.2.0",
"pinia": "2.2.2",
"screenfull": "6.0.2",
"qs": "6.13.0",
diff --git a/snowy-admin-web/src/assets/images/snowy-iam.png b/snowy-admin-web/src/assets/images/snowy-iam.png
new file mode 100644
index 00000000..0e721282
Binary files /dev/null and b/snowy-admin-web/src/assets/images/snowy-iam.png differ
diff --git a/snowy-admin-web/src/router/index.js b/snowy-admin-web/src/router/index.js
index 5d3155e6..2aac8595 100644
--- a/snowy-admin-web/src/router/index.js
+++ b/snowy-admin-web/src/router/index.js
@@ -23,6 +23,7 @@ import { NextLoading } from '@/utils/loading'
import { useMenuStore } from '@/store/menu'
import { useUserStore } from '@/store/user'
import { useDictStore } from '@/store/dict'
+import { pathToRegexp } from 'path-to-regexp'
// 进度条配置
NProgress.configure({ showSpinner: false, speed: 500 })
@@ -51,7 +52,14 @@ const isGetRouter = ref(false)
// 白名单校验
const exportWhiteListFromRouter = (router) => {
const res = []
- for (const item of router) res.push(item.path)
+ for (const item of router) {
+ // 生成路由的路径正则表达式(解构出正则表达式对象)
+ const { regexp } = pathToRegexp(item.path)
+ res.push({
+ path: item.path,
+ regex: regexp // 使用解构后的正则表达式
+ })
+ }
return res
}
const whiteList = exportWhiteListFromRouter(whiteListRouters)
@@ -66,7 +74,7 @@ router.beforeEach(async (to, from, next) => {
: `${sysBaseConfig.SNOWY_SYS_NAME}`
// 过滤白名单
- if (whiteList.includes(to.path)) {
+ if (whiteList.some(currentRoute => currentRoute.regex.test(to.path))) {
next()
// NProgress.done()
return false
diff --git a/snowy-admin-web/src/views/auth/login/callback.vue b/snowy-admin-web/src/views/auth/login/callback.vue
index aa025aca..60d83aad 100644
--- a/snowy-admin-web/src/views/auth/login/callback.vue
+++ b/snowy-admin-web/src/views/auth/login/callback.vue
@@ -146,8 +146,8 @@
argLength += 1
params[key] = value
})
- // 当然了,不可能只有一个参数
- if (argLength < 2) {
+ // 参数不能为空
+ if (argLength === 0) {
showError(proxy.$t('login.paramError'), true)
return
}
diff --git a/snowy-admin-web/src/views/auth/login/threeLogin.vue b/snowy-admin-web/src/views/auth/login/threeLogin.vue
index c0bfd9db..d9407a1e 100644
--- a/snowy-admin-web/src/views/auth/login/threeLogin.vue
+++ b/snowy-admin-web/src/views/auth/login/threeLogin.vue
@@ -2,6 +2,9 @@
{{ $t('login.signInOther') }}
+
+
+
diff --git a/snowy-admin-web/src/views/dev/config/thirdConfig/iamThirdForm.vue b/snowy-admin-web/src/views/dev/config/thirdConfig/iamThirdForm.vue
new file mode 100644
index 00000000..ede7f6c3
--- /dev/null
+++ b/snowy-admin-web/src/views/dev/config/thirdConfig/iamThirdForm.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 保存
+ formRef.resetFields()">重置
+
+
+
+
+
+
+
+
+
diff --git a/snowy-admin-web/src/views/dev/config/thirdConfig/index.vue b/snowy-admin-web/src/views/dev/config/thirdConfig/index.vue
index bf0d7b80..859b152e 100644
--- a/snowy-admin-web/src/views/dev/config/thirdConfig/index.vue
+++ b/snowy-admin-web/src/views/dev/config/thirdConfig/index.vue
@@ -1,16 +1,20 @@
-
-
+
+
+
+
+
diff --git a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/enums/AuthThirdPlatformEnum.java b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/enums/AuthThirdPlatformEnum.java
index 8d5b609a..f6825881 100644
--- a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/enums/AuthThirdPlatformEnum.java
+++ b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/enums/AuthThirdPlatformEnum.java
@@ -24,6 +24,11 @@ import vip.xiaonuo.common.exception.CommonException;
@Getter
public enum AuthThirdPlatformEnum {
+ /**
+ * IAM
+ */
+ IAM("IAM"),
+
/**
* GITEE
*/
@@ -41,7 +46,7 @@ public enum AuthThirdPlatformEnum {
}
public static void validate(String value) {
- boolean flag = GITEE.getValue().equals(value) || WECHAT.getValue().equals(value);
+ boolean flag = IAM.getValue().equals(value) || GITEE.getValue().equals(value) || WECHAT.getValue().equals(value);
if(!flag) {
throw new CommonException("不支持的第三方平台:{}", value);
}
diff --git a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/param/AuthThirdCallbackParam.java b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/param/AuthThirdCallbackParam.java
index 921eecc1..efef8467 100644
--- a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/param/AuthThirdCallbackParam.java
+++ b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/param/AuthThirdCallbackParam.java
@@ -38,7 +38,6 @@ public class AuthThirdCallbackParam {
private String code;
/** 第三方回调state */
- @Schema(description = "第三方回调state", requiredMode = Schema.RequiredMode.REQUIRED)
- @NotBlank(message = "state不能为空")
+ @Schema(description = "第三方回调state")
private String state;
}
diff --git a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/request/AuthThirdIamCommonSource.java b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/request/AuthThirdIamCommonSource.java
new file mode 100644
index 00000000..70f6b907
--- /dev/null
+++ b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/request/AuthThirdIamCommonSource.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.auth.modular.third.request;
+
+import me.zhyd.oauth.config.AuthSource;
+import me.zhyd.oauth.request.AuthDefaultRequest;
+
+/**
+ * 山信通认证源通用源
+ *
+ * @author xuyuxiang
+ * @date 2025/2/6 17:07
+ **/
+public record AuthThirdIamCommonSource(String authorizeUrl, String accessTokenUrl, String userInfoUrl) implements AuthSource {
+
+ @Override
+ public String authorize() {
+ return this.authorizeUrl;
+ }
+
+ @Override
+ public String accessToken() {
+ return this.accessTokenUrl;
+ }
+
+ @Override
+ public String userInfo() {
+ return this.userInfoUrl;
+ }
+
+ @Override
+ public Class extends AuthDefaultRequest> getTargetClass() {
+ return AuthThirdIamRequest.class;
+ }
+}
diff --git a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/request/AuthThirdIamRequest.java b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/request/AuthThirdIamRequest.java
new file mode 100644
index 00000000..ac55bac6
--- /dev/null
+++ b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/request/AuthThirdIamRequest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.auth.modular.third.request;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.Getter;
+import me.zhyd.oauth.config.AuthConfig;
+import me.zhyd.oauth.enums.AuthUserGender;
+import me.zhyd.oauth.enums.scope.AuthGiteeScope;
+import me.zhyd.oauth.exception.AuthException;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthToken;
+import me.zhyd.oauth.model.AuthUser;
+import me.zhyd.oauth.request.AuthDefaultRequest;
+import me.zhyd.oauth.utils.AuthScopeUtils;
+import me.zhyd.oauth.utils.UrlBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.security.Security;
+import java.util.Map;
+
+/**
+ * 山信通认证源通用请求
+ *
+ * @author xuyuxiang
+ * @date 2025/1/24 15:09
+ **/
+@Getter
+public class AuthThirdIamRequest extends AuthDefaultRequest {
+
+ private final Map authSourceOidcBaseJson;
+
+ static {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ public AuthThirdIamRequest(AuthConfig config, Map authSourceOidcBaseJson) {
+ super(config, new AuthThirdIamCommonSource(authSourceOidcBaseJson.get("authorizeUrl"),
+ authSourceOidcBaseJson.get("accessTokenUrl"),
+ authSourceOidcBaseJson.get("userInfoUrl")));
+ this.authSourceOidcBaseJson = authSourceOidcBaseJson;
+ }
+
+ @Override
+ public AuthToken getAccessToken(AuthCallback authCallback) {
+ String response = this.doPostAuthorizationCode(authCallback.getCode());
+ com.alibaba.fastjson.JSONObject accessTokenObject = com.alibaba.fastjson.JSONObject.parseObject(response);
+ this.checkResponse(accessTokenObject);
+ return AuthToken.builder().accessToken(accessTokenObject.getString("access_token")).refreshToken(accessTokenObject.getString("refresh_token")).scope(accessTokenObject.getString("scope")).tokenType(accessTokenObject.getString("token_type")).expireIn(accessTokenObject.getIntValue("expires_in")).build();
+ }
+
+ @Override
+ public AuthUser getUserInfo(AuthToken authToken) {
+ String userInfo = this.doGetUserInfo(authToken);
+ com.alibaba.fastjson.JSONObject userInfoObject = com.alibaba.fastjson.JSONObject.parseObject(userInfo);
+ this.checkResponse(userInfoObject);
+ return AuthUser.builder().rawUserInfo(userInfoObject).uuid(userInfoObject.getString("sub")).nickname(userInfoObject.getString("name")).username(userInfoObject.getString("account")).avatar(userInfoObject.getString("picture")).email(userInfoObject.getString("email")).gender(AuthUserGender.UNKNOWN).token(authToken).source(this.source.toString()).build();
+ }
+
+ private void checkResponse(JSONObject object) {
+ if (object.getIntValue("code") != 200) {
+ throw new AuthException(object.getString("msg"));
+ }
+ }
+
+ @Override
+ public String authorize(String state) {
+ return UrlBuilder.fromBaseUrl(super.authorize(state)).queryParam("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthGiteeScope.values()))).build();
+ }
+}
diff --git a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/service/impl/AuthThirdServiceImpl.java b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/service/impl/AuthThirdServiceImpl.java
index 259a724a..75dcc208 100644
--- a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/service/impl/AuthThirdServiceImpl.java
+++ b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/service/impl/AuthThirdServiceImpl.java
@@ -13,6 +13,7 @@
package vip.xiaonuo.auth.modular.third.service.impl;
import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
@@ -45,6 +46,7 @@ import vip.xiaonuo.auth.modular.third.param.AuthThirdBindAccountParam;
import vip.xiaonuo.auth.modular.third.param.AuthThirdCallbackParam;
import vip.xiaonuo.auth.modular.third.param.AuthThirdRenderParam;
import vip.xiaonuo.auth.modular.third.param.AuthThirdUserPageParam;
+import vip.xiaonuo.auth.modular.third.request.AuthThirdIamRequest;
import vip.xiaonuo.auth.modular.third.result.AuthThirdRenderResult;
import vip.xiaonuo.auth.modular.third.service.AuthThirdService;
import vip.xiaonuo.common.cache.CommonCacheOperator;
@@ -53,6 +55,8 @@ import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.common.page.CommonPageRequest;
import vip.xiaonuo.dev.api.DevConfigApi;
+import java.util.Map;
+
/**
* 第三方登录Service接口实现类
*
@@ -65,6 +69,13 @@ public class AuthThirdServiceImpl extends ServiceImpl authResponse = authRequest.login(authCallback);
if (authResponse.ok()) {
@@ -240,12 +258,26 @@ public class AuthThirdServiceImpl extends ServiceImpl