This is an automated email from the ASF dual-hosted git repository.
casion pushed a commit to branch dev-1.8.0
in repository https://gitbox.apache.org/repos/asf/linkis.git
The following commit(s) were added to refs/heads/dev-1.8.0 by this push:
new 7d58d354f6 Add OAuth2 authentication support (#5253)
7d58d354f6 is described below
commit 7d58d354f62bf6edf7418d4354a1a625efd62c89
Author: Kazuto Iris <[email protected]>
AuthorDate: Sun Sep 28 13:39:32 2025 +0800
Add OAuth2 authentication support (#5253)
* feat(mg-gateway): add OAuth2 authentication support
- Add OAuth2 authentication configuration to GatewayConfiguration
- Implement OAuth2Authentication
- Update `SecurityFilter` and `UserRestful` to process OAuth2 request
Signed-off-by: kazutoiris <[email protected]>
* feat(mg-gateway): add OAuth configuration
- Add OAuth-related properties to `linkis-mg-gateway.properties`
- Include support for GitHub OAuth as an example
Signed-off-by: kazutoiris <[email protected]>
* style: reformat code
Signed-off-by: kazutoiris <[email protected]>
* feat(mg-gateway): add OAuth in frontend
- Add OAuth login option to the login page
- Implement OAuth callback route and component
- Add translations for OAuth login text
Signed-off-by: kazutoiris <[email protected]>
* docs: add OAuth authentication documentation
---------
Signed-off-by: kazutoiris <[email protected]>
---
docs/configuration/linkis-gateway-core.md | 8 +
.../package/conf/linkis-mg-gateway.properties | 9 +
.../gateway/config/GatewayConfiguration.scala | 9 +
.../linkis/gateway/security/SecurityFilter.scala | 3 +
.../linkis/gateway/security/UserRestful.scala | 15 +
.../security/oauth/OAuth2Authentication.scala | 340 +++++++++++++++++++++
linkis-web/src/common/i18n/en.json | 1 +
linkis-web/src/common/i18n/zh.json | 1 +
linkis-web/src/dss/router.js | 10 +
linkis-web/src/dss/view/login/index.vue | 21 +-
linkis-web/src/dss/view/login/oauthCallback.vue | 55 ++++
11 files changed, 471 insertions(+), 1 deletion(-)
diff --git a/docs/configuration/linkis-gateway-core.md
b/docs/configuration/linkis-gateway-core.md
index be933b2a26..5a4f55a3d1 100644
--- a/docs/configuration/linkis-gateway-core.md
+++ b/docs/configuration/linkis-gateway-core.md
@@ -36,3 +36,11 @@
|linkis-gateway-core|wds.linkis.gateway.this.schema| | gateway.this.schema|
|linkis-gateway-core|wds.linkis.web.enable.water.mark|true|
web.enable.water.mark|
|linkis-gateway-core|wds.linkis.entrance.name| |linkis.entrance.name|
+|linkis-gateway-core|wds.linkis.gateway.conf.enable.oauth.auth| false
|wds.linkis.gateway.conf.enable.oauth.auth|
+|linkis-gateway-core|wds.linkis.gateway.auth.oauth.authentication.url|
|wds.linkis.gateway.auth.oauth.authentication.url|
+|linkis-gateway-core|wds.linkis.gateway.auth.oauth.exchange.url|
|wds.linkis.gateway.auth.oauth.exchange.url|
+|linkis-gateway-core|wds.linkis.gateway.auth.oauth.validate.url|
|wds.linkis.gateway.auth.oauth.validate.url|
+|linkis-gateway-core|wds.linkis.gateway.auth.oauth.validate.field|
|wds.linkis.gateway.auth.oauth.validate.field|
+|linkis-gateway-core|wds.linkis.gateway.auth.oauth.client.id|
|wds.linkis.gateway.auth.oauth.client.id|
+|linkis-gateway-core|wds.linkis.gateway.auth.oauth.client.secret|
|wds.linkis.gateway.auth.oauth.client.secret|
+|linkis-gateway-core|wds.linkis.gateway.auth.oauth.scope|
|wds.linkis.gateway.auth.oauth.scope|
diff --git a/linkis-dist/package/conf/linkis-mg-gateway.properties
b/linkis-dist/package/conf/linkis-mg-gateway.properties
index 1f1d2416b4..0e4275677c 100644
--- a/linkis-dist/package/conf/linkis-mg-gateway.properties
+++ b/linkis-dist/package/conf/linkis-mg-gateway.properties
@@ -30,6 +30,15 @@ wds.linkis.ldap.proxy.baseDN=
wds.linkis.ldap.proxy.userNameFormat=
wds.linkis.admin.user=hadoop
#wds.linkis.admin.password=
+##OAuth
+wds.linkis.oauth.enable=false
+wds.linkis.oauth.url=https://github.com/login/oauth/authorize
+wds.linkis.gateway.auth.oauth.exchange.url=https://github.com/login/oauth/access_token
+wds.linkis.gateway.auth.oauth.validate.url=https://api.github.com/user
+wds.linkis.gateway.auth.oauth.validate.field=login
+wds.linkis.gateway.auth.oauth.client.id=YOUR_CLIENT_ID
+wds.linkis.gateway.auth.oauth.client.secret=YOUR_CLIENT_SECRET
+wds.linkis.gateway.auth.oauth.scope=user
##Spring
spring.server.port=9001
diff --git
a/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/config/GatewayConfiguration.scala
b/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/config/GatewayConfiguration.scala
index 5fc80d7afc..ccb7325b57 100644
---
a/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/config/GatewayConfiguration.scala
+++
b/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/config/GatewayConfiguration.scala
@@ -42,6 +42,15 @@ object GatewayConfiguration {
val TOKEN_AUTHENTICATION_SCAN_INTERVAL =
CommonVars("wds.linkis.gateway.conf.token.auth.scan.interval", 1000 * 60 *
10)
+ val ENABLE_OAUTH_AUTHENTICATION =
CommonVars("wds.linkis.gateway.conf.enable.oauth.auth", false)
+ val OAUTH_AUTHENTICATION_URL =
CommonVars("wds.linkis.gateway.auth.oauth.authentication.url", "")
+ val OAUTH_EXCHANGE_URL =
CommonVars("wds.linkis.gateway.auth.oauth.exchange.url", "")
+ val OAUTH_VALIDATE_URL =
CommonVars("wds.linkis.gateway.auth.oauth.validate.url", "")
+ val OAUTH_VALIDATE_FIELD =
CommonVars("wds.linkis.gateway.auth.oauth.validate.field", "")
+ val OAUTH_CLIENT_ID = CommonVars("wds.linkis.gateway.auth.oauth.client.id",
"")
+ val OAUTH_CLIENT_SECRET =
CommonVars("wds.linkis.gateway.auth.oauth.client.secret", "")
+ val OAUTH_SCOPE = CommonVars("wds.linkis.gateway.auth.oauth.scope", "")
+
val PASS_AUTH_REQUEST_URI =
CommonVars("wds.linkis.gateway.conf.url.pass.auth",
"/dws/").getValue.split(",")
diff --git
a/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/security/SecurityFilter.scala
b/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/security/SecurityFilter.scala
index 150ae565ef..9f170e9dd2 100644
---
a/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/security/SecurityFilter.scala
+++
b/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/security/SecurityFilter.scala
@@ -23,6 +23,7 @@ import org.apache.linkis.common.utils.{Logging, Utils}
import org.apache.linkis.gateway.config.GatewayConfiguration
import org.apache.linkis.gateway.config.GatewayConfiguration._
import org.apache.linkis.gateway.http.GatewayContext
+import org.apache.linkis.gateway.security.oauth.OAuth2Authentication
import org.apache.linkis.gateway.security.sso.SSOInterceptor
import org.apache.linkis.gateway.security.token.TokenAuthentication
import org.apache.linkis.server.{validateFailed, Message}
@@ -127,6 +128,8 @@ object SecurityFilter extends Logging {
logger.info("No login needed for proxy uri: " +
gatewayContext.getRequest.getRequestURI)
} else if (TokenAuthentication.isTokenRequest(gatewayContext)) {
TokenAuthentication.tokenAuth(gatewayContext)
+ } else if (OAuth2Authentication.isOAuth2Request(gatewayContext)) {
+ OAuth2Authentication.OAuth2Entry(gatewayContext)
} else {
val userName =
Utils.tryCatch(GatewaySSOUtils.getLoginUser(gatewayContext)) {
case n @ (_: NonLoginException | _: LoginExpireException) =>
diff --git
a/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/security/UserRestful.scala
b/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/security/UserRestful.scala
index 38d06b6b17..e79296c564 100644
---
a/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/security/UserRestful.scala
+++
b/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/security/UserRestful.scala
@@ -20,6 +20,7 @@ package org.apache.linkis.gateway.security
import org.apache.linkis.common.utils.{Logging, RSAUtils, Utils}
import org.apache.linkis.gateway.config.GatewayConfiguration
import org.apache.linkis.gateway.http.GatewayContext
+import org.apache.linkis.gateway.security.oauth.OAuth2Authentication
import org.apache.linkis.gateway.security.sso.SSOInterceptor
import org.apache.linkis.gateway.security.token.TokenAuthentication
import org.apache.linkis.protocol.usercontrol.{
@@ -87,6 +88,20 @@ abstract class AbstractUserRestful extends UserRestful with
Logging {
TokenAuthentication.tokenAuth(gatewayContext, true)
return
}
+ case "oauth-login" =>
+ Utils.tryCatch {
+ val loginUser = GatewaySSOUtils.getLoginUsername(gatewayContext)
+ Message
+ .ok(loginUser + " already logged in, please log out before signing
in(已经登录,请先退出再进行登录)!")
+ .data("userName", loginUser)
+ }(_ => {
+ OAuth2Authentication.OAuth2Auth(gatewayContext, true)
+ return
+ })
+ case "oauth-redirect" => {
+ OAuth2Authentication.OAuth2Redirect(gatewayContext)
+ return
+ }
case "logout" => logout(gatewayContext)
case "userInfo" => userInfo(gatewayContext)
case "publicKey" => publicKey(gatewayContext)
diff --git
a/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/security/oauth/OAuth2Authentication.scala
b/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/security/oauth/OAuth2Authentication.scala
new file mode 100644
index 0000000000..c62ab5b3be
--- /dev/null
+++
b/linkis-spring-cloud-services/linkis-service-gateway/linkis-gateway-core/src/main/scala/org/apache/linkis/gateway/security/oauth/OAuth2Authentication.scala
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.gateway.security.oauth
+
+import org.apache.linkis.common.exception.LinkisCommonErrorException
+import org.apache.linkis.common.utils.{Logging, Utils}
+import org.apache.linkis.gateway.config.GatewayConfiguration
+import org.apache.linkis.gateway.config.GatewayConfiguration._
+import org.apache.linkis.gateway.http.GatewayContext
+import org.apache.linkis.gateway.security.{GatewaySSOUtils, SecurityFilter}
+import org.apache.linkis.server.Message
+import org.apache.linkis.server.conf.ServerConfiguration
+
+import org.apache.commons.io.IOUtils
+import org.apache.commons.lang3.StringUtils
+
+import java.io.IOException
+import java.net.{HttpURLConnection, URL}
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
+
+object OAuth2Authentication extends Logging {
+
+ private val objectMapper = new ObjectMapper()
+ objectMapper.registerModule(DefaultScalaModule)
+
+ def isOAuth2Request(gatewayContext: GatewayContext): Boolean = {
+ val path = getMethod(gatewayContext)
+ path == "oauth_login" || path == "oauth_redirect"
+ }
+
+ def OAuth2Entry(gatewayContext: GatewayContext, login: Boolean = false):
Boolean = {
+ val path = getMethod(gatewayContext)
+ if (path == "oauth_redirect") {
+ OAuth2Redirect(gatewayContext)
+ } else if (path == "oauth_redirect") {
+ OAuth2Auth(gatewayContext, login)
+ } else {
+ val message =
+ Message.noLogin(s"未知 OAuth 请求") <<
gatewayContext.getRequest.getRequestURI
+ SecurityFilter.filterResponse(gatewayContext, message)
+ false
+ }
+ }
+
+ private def getMethod(gatewayContext: GatewayContext) = {
+ var userURI = ServerConfiguration.BDP_SERVER_USER_URI.getValue
+ if (!userURI.endsWith("/")) userURI += "/"
+ val path = gatewayContext.getRequest.getRequestURI.replace(userURI, "")
+ path
+ }
+
+ def OAuth2Redirect(gatewayContext: GatewayContext): Boolean = {
+ if (!ENABLE_OAUTH_AUTHENTICATION.getValue) {
+ val message =
+ Message.noLogin(
+ s"Gateway 未启用 OAuth 认证,请采用其他认证方式!"
+ ) << gatewayContext.getRequest.getRequestURI
+ SecurityFilter.filterResponse(gatewayContext, message)
+ return false
+ }
+ val message =
+ Message.ok("创建链接成功!").data("redirectUrl", generateAuthenticationUrl())
+ SecurityFilter.filterResponse(gatewayContext, message)
+ true
+ }
+
+ /**
+ * 生成OAuth认证的URL
+ *
+ * @note
+ * 认证完成回调链接需要在认证服务器上进行配置
+ * @return
+ */
+ private def generateAuthenticationUrl(): String = {
+ var oauthServerUrl =
+
s"${OAUTH_AUTHENTICATION_URL.getValue}?client_id=${OAUTH_CLIENT_ID.getValue}&response_type=code"
+ if (StringUtils.isNotBlank(OAUTH_SCOPE.getValue)) {
+ oauthServerUrl += s"&scope=${OAUTH_SCOPE.getValue}"
+ }
+ oauthServerUrl
+ }
+
+ def OAuth2Auth(gatewayContext: GatewayContext, login: Boolean = false):
Boolean = {
+ if (!ENABLE_OAUTH_AUTHENTICATION.getValue) {
+ val message =
+ Message.noLogin(
+ s"Gateway 未启用 OAuth 认证,请采用其他认证方式!"
+ ) << gatewayContext.getRequest.getRequestURI
+ SecurityFilter.filterResponse(gatewayContext, message)
+ return false
+ }
+
+ val code = extractCode(gatewayContext)
+ val host = gatewayContext.getRequest.getRequestRealIpAddr()
+
+ if (StringUtils.isBlank(code)) {
+ val message =
+ Message.noLogin(s"请在回调查询参数中返回code,以便完成OAuth认证!") <<
gatewayContext.getRequest.getRequestURI
+ SecurityFilter.filterResponse(gatewayContext, message)
+ return false
+ }
+
+ var authMsg: Message =
+ Message.noLogin(s"无效的访问令牌 $code,无法完成 OAuth 认证!") <<
gatewayContext.getRequest.getRequestURI
+
+ val accessToken = Utils.tryCatch(exchangeAccessToken(code, host))(t => {
+ authMsg = Message.noLogin(
+ s"OAuth exchange failed, code: $code, reason: ${t.getMessage}"
+ ) << gatewayContext.getRequest.getRequestURI
+ null
+ })
+
+ if (StringUtils.isNotBlank(accessToken)) {
+ val username = validateAccessToken(accessToken, host)
+ logger.info(
+ s"OAuth authentication succeed, uri:
${gatewayContext.getRequest.getRequestURI}, accessToken: $accessToken,
username: $username."
+ )
+
+ if (login) {
+ GatewaySSOUtils.setLoginUser(gatewayContext, username)
+ val msg =
+ Message
+ .ok("login successful(登录成功)!")
+ .data("userName", username)
+ .data("enableWatermark",
GatewayConfiguration.ENABLE_WATER_MARK.getValue)
+ .data("isAdmin", false)
+ SecurityFilter.filterResponse(gatewayContext, msg)
+ return true
+ }
+
+ GatewaySSOUtils.setLoginUser(gatewayContext.getRequest, username)
+ true
+ } else {
+ logger.info(
+ s"OAuth exchange fail, uri:
${gatewayContext.getRequest.getRequestURI}, code: $code, host: $host."
+ )
+ SecurityFilter.filterResponse(gatewayContext, authMsg)
+ false
+ }
+ }
+
+ private def extractCode(gatewayContext: GatewayContext): String = {
+ Utils.tryCatch(gatewayContext.getRequest.getQueryParams.get("code")(0))(_
=> null)
+ }
+
+ /**
+ * 验证访问码的有效性并获取访问令牌
+ *
+ * @param code
+ * 访问码
+ * @param host
+ * 客户端主机
+ * @return
+ * 访问令牌
+ */
+ private def exchangeAccessToken(code: String, host: String): String = {
+ val exchangeUrl = OAUTH_EXCHANGE_URL.getValue
+
+ if (StringUtils.isBlank(exchangeUrl)) {
+ logger.warn(s"OAuth exchange url is not set")
+ }
+ if (StringUtils.isBlank(code)) {
+ logger.warn(s"OAuth exchange code is empty")
+ }
+
+ Utils.tryCatch({
+ val response = HttpUtils.post(
+ exchangeUrl,
+ data = objectMapper.writeValueAsString(
+ Map(
+ "client_id" -> OAUTH_CLIENT_ID.getValue,
+ "client_secret" -> OAUTH_CLIENT_SECRET.getValue,
+ "code" -> code,
+ "host" -> host
+ )
+ )
+ )
+ objectMapper.readValue(response, classOf[Map[String,
String]]).get("access_token").orNull
+ })(t => {
+ logger.warn(s"OAuth exchange failed, url: $exchangeUrl, reason:
${t.getMessage}")
+ null
+ })
+ }
+
+ /**
+ * 验证访问令牌的有效性并兑换用户名
+ *
+ * @param accessToken
+ * 访问令牌
+ * @param host
+ * 客户端主机
+ * @return
+ * 用户名
+ */
+ private def validateAccessToken(accessToken: String, host: String): String =
{
+ val url = OAUTH_VALIDATE_URL.getValue
+
+ if (StringUtils.isBlank(url)) {
+ logger.warn(s"OAuth validate url is not set")
+ }
+
+ if (StringUtils.isBlank(accessToken)) {
+ logger.warn(s"OAuth validate accessToken is empty")
+ }
+
+ Utils.tryCatch({
+ val response = HttpUtils.get(url, headers = Map("Authorization" ->
s"Bearer $accessToken"))
+ objectMapper
+ .readValue(response, classOf[Map[String, String]])
+ .get(OAUTH_VALIDATE_FIELD.getValue)
+ .orNull
+ })(t => {
+ logger.warn(s"OAuth validate failed, url: $url, reason: ${t.getMessage}")
+ null
+ })
+ }
+
+}
+
+object HttpUtils extends Logging {
+
+ def get(
+ url: String,
+ headers: Map[String, String] = Map.empty,
+ params: Map[String, String] = Map.empty
+ ): String = {
+ Utils.tryCatch {
+ val fullUrl = url + (if (params.nonEmpty) {
+ "?" + params.map { case (key, value) =>
s"$key=$value" }.mkString("&")
+ } else {
+ ""
+ })
+ val connection = new
URL(fullUrl).openConnection().asInstanceOf[HttpURLConnection]
+ connection.setRequestMethod("GET")
+
+ headers.foreach { case (key, value) =>
+ connection.setRequestProperty(key, value)
+ }
+
+ if (!headers.contains("Accept")) {
+ connection.setRequestProperty("Accept", "application/json")
+ }
+
+ val responseCode = connection.getResponseCode
+ if (!(responseCode >= 200 && responseCode < 300)) {
+ throw new IOException(s"HTTP GET request failed for URL: $url -
$responseCode")
+ }
+
+ val inputStream = connection.getInputStream
+
+ try {
+ IOUtils.toString(inputStream, "UTF-8")
+ } finally {
+ inputStream.close()
+ connection.disconnect()
+ }
+ } { t =>
+ logger.warn(s"Failed to execute HTTP GET request to $url", t)
+ throw new LinkisCommonErrorException(
+ 0,
+ s"HTTP GET request failed for URL: $url, reason: ${t.getMessage}"
+ )
+ }
+ }
+
+ def post(url: String, data: String, headers: Map[String, String] =
Map.empty): String = {
+ Utils.tryCatch {
+ val connection = new
URL(url).openConnection().asInstanceOf[HttpURLConnection]
+ try {
+ connection.setRequestMethod("POST")
+ connection.setDoOutput(true)
+ connection.setDoInput(true)
+
+ headers.foreach { case (key, value) =>
+ connection.setRequestProperty(key, value)
+ }
+
+ if (!headers.contains("Content-Type")) {
+ connection.setRequestProperty("Content-Type", "application/json;
charset=UTF-8")
+ }
+
+ if (!headers.contains("Accept")) {
+ connection.setRequestProperty("Accept", "application/json")
+ }
+
+ if (data != null && data.nonEmpty) {
+ val outputStream = connection.getOutputStream
+ try {
+ IOUtils.write(data, outputStream, "UTF-8")
+ } finally {
+ outputStream.close()
+ }
+ }
+
+ val responseCode = connection.getResponseCode
+ if (!(responseCode >= 200 && responseCode < 300)) {
+ throw new IOException(s"HTTP POST request failed for URL: $url -
$responseCode")
+ }
+
+ val inputStream = connection.getInputStream
+
+ try {
+ if (inputStream != null) {
+ IOUtils.toString(inputStream, "UTF-8")
+ } else {
+ ""
+ }
+ } finally {
+ if (inputStream != null) inputStream.close()
+ }
+ } finally {
+ connection.disconnect()
+ }
+ } { t =>
+ logger.warn(s"Failed to execute HTTP POST request to $url", t)
+ throw new LinkisCommonErrorException(
+ 0,
+ s"HTTP POST request failed for URL: $url, reason: ${t.getMessage}"
+ )
+ }
+ }
+
+}
diff --git a/linkis-web/src/common/i18n/en.json
b/linkis-web/src/common/i18n/en.json
index aac078b18a..23b21bca44 100644
--- a/linkis-web/src/common/i18n/en.json
+++ b/linkis-web/src/common/i18n/en.json
@@ -265,6 +265,7 @@
"userName": "Please enter your username",
"remenber": "Remember me",
"login": "Login",
+ "oauthLogin": "OAuth Login",
"passwordHint": "Please enter your password",
"password": "Please enter password!",
"loginSuccess": "Login Success",
diff --git a/linkis-web/src/common/i18n/zh.json
b/linkis-web/src/common/i18n/zh.json
index 688153101e..cc4c24e0c2 100644
--- a/linkis-web/src/common/i18n/zh.json
+++ b/linkis-web/src/common/i18n/zh.json
@@ -266,6 +266,7 @@
"userName": "请输入用户名",
"remenber": "记住当前用户",
"login": "登录",
+ "oauthLogin": "OAuth 登录",
"passwordHint": "请输入密码!",
"loginSuccess": "登录成功",
"haveLogin": "您已经登录,请不要重复登录",
diff --git a/linkis-web/src/dss/router.js b/linkis-web/src/dss/router.js
index 01b5ede649..bac6af2994 100644
--- a/linkis-web/src/dss/router.js
+++ b/linkis-web/src/dss/router.js
@@ -61,6 +61,16 @@ export default [
component: () =>
import('./view/login/index.vue'),
},
+ {
+ path: '/login/oauth/callback',
+ name: 'OAuthCallback',
+ meta: {
+ title: 'OAuthCallback',
+ publicPage: true,
+ },
+ component: () =>
+ import('./view/login/oauthCallback.vue'),
+ },
// Public pages, not subject to permission control(公用页面,不受权限控制)
{
path: '/500',
diff --git a/linkis-web/src/dss/view/login/index.vue
b/linkis-web/src/dss/view/login/index.vue
index 81c6af0bdb..c3ec243b21 100644
--- a/linkis-web/src/dss/view/login/index.vue
+++ b/linkis-web/src/dss/view/login/index.vue
@@ -20,7 +20,7 @@
class="login"
@keyup.enter.stop.prevent="handleSubmit('loginForm')">
<i class="login-bg"/>
- <div class="login-main">
+ <div class="login-main" :style="{height: OAuthRedirectUrl ? '500px' : ''}">
<Form
ref="loginForm"
:model="loginForm"
@@ -56,6 +56,14 @@
size="large"
@click="handleSubmit('loginForm')">{{$t('message.common.login.login')}}</Button>
</FormItem>
+ <FormItem>
+ <Button
+ v-if="OAuthRedirectUrl"
+ type="primary"
+ long
+ size="large"
+
@click="handleOAuthLogin()">{{$t('message.common.login.oauthLogin')}}</Button>
+ </FormItem>
</Form>
</div>
</div>
@@ -71,6 +79,7 @@ export default {
data() {
return {
loading: false,
+ OAuthRedirectUrl: null,
loginForm: {
user: '',
password: '',
@@ -97,6 +106,7 @@ export default {
this.loginForm.password = userNameAndPass.split('&')[1];
}
this.getPublicKey();
+ this.checkOAuthStatus();
},
mounted() {
},
@@ -179,6 +189,15 @@ export default {
clearSession() {
storage.clear();
},
+ // check OAuth status(检查OAuth状态)
+ checkOAuthStatus() {
+ api.fetch('/user/oauth-redirect', {}, 'get').then((res) => {
+ this.OAuthRedirectUrl = res.redirectUrl;
+ })
+ },
+ handleOAuthLogin() {
+ window.location.href = this.OAuthRedirectUrl;
+ },
},
};
</script>
diff --git a/linkis-web/src/dss/view/login/oauthCallback.vue
b/linkis-web/src/dss/view/login/oauthCallback.vue
new file mode 100644
index 0000000000..e81bfe7bd8
--- /dev/null
+++ b/linkis-web/src/dss/view/login/oauthCallback.vue
@@ -0,0 +1,55 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<template>
+ <div></div>
+</template>
+<script>
+import api from '@/common/service/api';
+import storage from '@/common/helper/storage';
+export default {
+ data() {
+ return {};
+ },
+ created() {
+ if (this.$route.query.code) {
+ api.fetch('/user/oauth-login', { code: this.$route.query.code },
'post').then((res) => {
+ if (res) {
+ this.userName = res.userName;
+ storage.set('userName', res.userName, 'session')
+ storage.set('enableWatermark', res.enableWatermark ? true : false,
'session')
+ this.$router.push({ path: '/console' });
+ this.$Message.success(this.$t('message.common.login.loginSuccess'));
+ }
+ }).catch((err) => {
+ if (err.message.indexOf('已经登录,请先退出再进行登录') !== -1) {
+ this.getPageHomeUrl().then(() => {
+ this.$router.push({ path: '/' });
+ })
+ } else {
+ this.$Message.error(this.$t('message.common.login.vaildFaild'));
+ this.$router.push({ path: '/login' })
+ }
+ });
+ }
+ },
+ mounted() {
+ },
+ methods: {
+ },
+};
+</script>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]