You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by wu...@apache.org on 2022/11/14 02:40:47 UTC

[shardingsphere-elasticjob-ui] branch master updated: feat: add support for Casdoor authentication (#174)

This is an automated email from the ASF dual-hosted git repository.

wuweijie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere-elasticjob-ui.git


The following commit(s) were added to refs/heads/master by this push:
     new 764d1c2  feat: add support for Casdoor authentication (#174)
764d1c2 is described below

commit 764d1c2eb672eb0673b96fccbc537962b1bd2a4f
Author: jakiuncle <88...@users.noreply.github.com>
AuthorDate: Mon Nov 14 10:40:41 2022 +0800

    feat: add support for Casdoor authentication (#174)
    
    * feat: add support for Casdoor authentication
---
 .../pom.xml                                        |  5 ++
 .../lite/ui/security/AuthenticationFilter.java     | 82 ++++++++++++++++++----
 .../elasticjob/lite/ui/security/OAuthCode.java}    | 19 +++--
 .../src/main/resources/application.properties      | 36 ++++++++++
 .../src/views/login/api.js                         |  4 +-
 .../src/views/login/{api.js => casdoor.js}         | 23 +++++-
 .../src/views/login/index.vue                      | 18 +++++
 7 files changed, 167 insertions(+), 20 deletions(-)

diff --git a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/pom.xml b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/pom.xml
index 0acf54a..5f57dce 100644
--- a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/pom.xml
+++ b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/pom.xml
@@ -79,6 +79,11 @@
             <groupId>com.auth0</groupId>
             <artifactId>java-jwt</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.casbin</groupId>
+            <artifactId>casdoor-spring-boot-starter</artifactId>
+            <version>1.3.0</version>
+        </dependency>
     </dependencies>
     
     <build>
diff --git a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/java/org/apache/shardingsphere/elasticjob/lite/ui/security/AuthenticationFilter.java b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/java/org/apache/shardingsphere/elasticjob/lite/ui/security/AuthenticationFilter.java
index 98541d5..c65de49 100644
--- a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/java/org/apache/shardingsphere/elasticjob/lite/ui/security/AuthenticationFilter.java
+++ b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/java/org/apache/shardingsphere/elasticjob/lite/ui/security/AuthenticationFilter.java
@@ -21,13 +21,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.base.Strings;
 import lombok.Setter;
 import org.apache.shardingsphere.elasticjob.lite.ui.web.response.ResponseResultUtil;
+import org.casbin.casdoor.config.CasdoorConfiguration;
+import org.casbin.casdoor.entity.CasdoorUser;
+import org.casbin.casdoor.service.CasdoorAuthService;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
 
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
+import javax.servlet.*;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
@@ -40,14 +40,25 @@ import java.util.Map;
 public final class AuthenticationFilter implements Filter {
     
     private static final String LOGIN_URI = "/api/login";
-    
+
+    private static final String CASDOOR_LOGIN_URL = "/api/casdoor-login-url";
+
+    private static final String CASDOOR_LOGIN = "/api/casdoor-login";
     private final ObjectMapper objectMapper = new ObjectMapper();
     
     @Setter
     private UserAuthenticationService userAuthenticationService;
-    
+
+    private CasdoorAuthService casdoorAuthService;
+
+    private String loginMethod = "default";
+
     @Override
     public void init(final FilterConfig filterConfig) {
+        ServletContext servletContext = filterConfig.getServletContext();
+        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
+        CasdoorConfiguration config = ctx.getBean(CasdoorConfiguration.class);
+        casdoorAuthService = new CasdoorAuthService(config);
     }
     
     @Override
@@ -58,11 +69,26 @@ public final class AuthenticationFilter implements Filter {
             handleLogin(httpRequest, httpResponse);
             return;
         }
-        String accessToken = httpRequest.getHeader("Access-Token");
-        if (Strings.isNullOrEmpty(accessToken) || !userAuthenticationService.isValidToken(accessToken)) {
-            respondWithUnauthorized(httpResponse);
+        if (CASDOOR_LOGIN_URL.equals(httpRequest.getRequestURI())) {
+            handleCasdoorLoginUrl(httpRequest, httpResponse);
+            return;
+        }
+        if (CASDOOR_LOGIN.equals(httpRequest.getRequestURI())) {
+            handleCasdoorLogin(httpRequest, httpResponse);
             return;
         }
+        String accessToken = httpRequest.getHeader("Access-Token");
+        if(loginMethod == "default"){
+            if (Strings.isNullOrEmpty(accessToken) || !userAuthenticationService.isValidToken(accessToken)) {
+                respondWithUnauthorized(httpResponse);
+                return;
+            }
+        }else{
+            if(casdoorAuthService.parseJwtToken(accessToken) == null){
+                respondWithUnauthorized(httpResponse);
+                return;
+            }
+        }
         filterChain.doFilter(httpRequest, httpResponse);
     }
     
@@ -83,12 +109,44 @@ public final class AuthenticationFilter implements Filter {
             Map<String, Object> result = new HashMap<>(2, 1);
             result.put("username", authenticationResult.getUsername());
             result.put("accessToken", userAuthenticationService.getToken(authenticationResult.getUsername()));
+            loginMethod = "default";
             objectMapper.writeValue(httpResponse.getWriter(), ResponseResultUtil.build(result));
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
-    
+
+    private void handleCasdoorLoginUrl(final HttpServletRequest httpRequest, final HttpServletResponse httpResponse) {
+        try {
+            String origin = httpRequest.getParameter("origin");
+            String url = casdoorAuthService.getSigninUrl(origin + "/login/casdoor");
+            httpResponse.setContentType("application/json");
+            httpResponse.setCharacterEncoding("UTF-8");
+            Map<String, Object> result = new HashMap<>(1, 1);
+            result.put("casdoorLoginUrl", url);
+            loginMethod = "casdoor";
+            objectMapper.writeValue(httpResponse.getWriter(), ResponseResultUtil.build(result));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void handleCasdoorLogin(final HttpServletRequest httpRequest, final HttpServletResponse httpResponse) {
+        try {
+            OAuthCode code = objectMapper.readValue(httpRequest.getReader(), OAuthCode.class);
+            String token = casdoorAuthService.getOAuthToken(code.getCode(), code.getState());
+            CasdoorUser user = casdoorAuthService.parseJwtToken(token);
+            httpResponse.setContentType("application/json");
+            httpResponse.setCharacterEncoding("UTF-8");
+            Map<String, Object> result = new HashMap<>(4, 1);
+            result.put("username", user.getName());
+            result.put("accessToken", token);
+            objectMapper.writeValue(httpResponse.getWriter(), ResponseResultUtil.build(result));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
     private void respondWithUnauthorized(final HttpServletResponse httpResponse) throws IOException {
         httpResponse.setContentType("application/json");
         httpResponse.setCharacterEncoding("UTF-8");
diff --git a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/api.js b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/java/org/apache/shardingsphere/elasticjob/lite/ui/security/OAuthCode.java
similarity index 77%
copy from shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/api.js
copy to shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/java/org/apache/shardingsphere/elasticjob/lite/ui/security/OAuthCode.java
index 26ff819..0336478 100644
--- a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/api.js
+++ b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/java/org/apache/shardingsphere/elasticjob/lite/ui/security/OAuthCode.java
@@ -15,8 +15,19 @@
  * limitations under the License.
  */
 
-import API from '@/utils/api'
+package org.apache.shardingsphere.elasticjob.lite.ui.security;
 
-export default {
-  getLogin: (params = {}) => API.post(`/api/login`, params)
-}
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * OAuth code
+ **/
+@Getter
+@Setter
+public final class OAuthCode {
+
+    private String code;
+
+    private String state;
+}
\ No newline at end of file
diff --git a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/resources/application.properties b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/resources/application.properties
index e2177b2..b8ce26e 100644
--- a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/resources/application.properties
+++ b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/resources/application.properties
@@ -29,3 +29,39 @@ spring.jpa.show-sql=false
 
 ## Uncomment the following property to allow adding DataSource dynamically.
 # dynamic.datasource.allowed-driver-classes={'org.h2.Driver','org.postgresql.Driver'}
+
+casdoor.endpoint=http://localhost:7001
+casdoor.client-id=3ed79fa530645fbd3653
+casdoor.client-secret=54633c82b7796a4332c6976864c6c16bc3b05556
+casdoor.certificate=\
+-----BEGIN CERTIFICATE-----\n\
+MIIE+TCCAuGgAwIBAgIDAeJAMA0GCSqGSIb3DQEBCwUAMDYxHTAbBgNVBAoTFENh\n\
+c2Rvb3IgT3JnYW5pemF0aW9uMRUwEwYDVQQDEwxDYXNkb29yIENlcnQwHhcNMjEx\n\
+MDE1MDgxMTUyWhcNNDExMDE1MDgxMTUyWjA2MR0wGwYDVQQKExRDYXNkb29yIE9y\n\
+Z2FuaXphdGlvbjEVMBMGA1UEAxMMQ2FzZG9vciBDZXJ0MIICIjANBgkqhkiG9w0B\n\
+AQEFAAOCAg8AMIICCgKCAgEAsInpb5E1/ym0f1RfSDSSE8IR7y+lw+RJjI74e5ej\n\
+rq4b8zMYk7HeHCyZr/hmNEwEVXnhXu1P0mBeQ5ypp/QGo8vgEmjAETNmzkI1NjOQ\n\
+CjCYwUrasO/f/MnI1C0j13vx6mV1kHZjSrKsMhYY1vaxTEP3+VB8Hjg3MHFWrb07\n\
+uvFMCJe5W8+0rKErZCKTR8+9VB3janeBz//zQePFVh79bFZate/hLirPK0Go9P1g\n\
+OvwIoC1A3sarHTP4Qm/LQRt0rHqZFybdySpyWAQvhNaDFE7mTstRSBb/wUjNCUBD\n\
+PTSLVjC04WllSf6Nkfx0Z7KvmbPstSj+btvcqsvRAGtvdsB9h62Kptjs1Yn7GAuo\n\
+I3qt/4zoKbiURYxkQJXIvwCQsEftUuk5ew5zuPSlDRLoLByQTLbx0JqLAFNfW3g/\n\
+pzSDjgd/60d6HTmvbZni4SmjdyFhXCDb1Kn7N+xTojnfaNkwep2REV+RMc0fx4Gu\n\
+hRsnLsmkmUDeyIZ9aBL9oj11YEQfM2JZEq+RVtUx+wB4y8K/tD1bcY+IfnG5rBpw\n\
+IDpS262boq4SRSvb3Z7bB0w4ZxvOfJ/1VLoRftjPbLIf0bhfr/AeZMHpIKOXvfz4\n\
+yE+hqzi68wdF0VR9xYc/RbSAf7323OsjYnjjEgInUtRohnRgCpjIk/Mt2Kt84Kb0\n\
+wn8CAwEAAaMQMA4wDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAn2lf\n\
+DKkLX+F1vKRO/5gJ+Plr8P5NKuQkmwH97b8CS2gS1phDyNgIc4/LSdzuf4Awe6ve\n\
+C06lVdWSIis8UPUPdjmT2uMPSNjwLxG3QsrimMURNwFlLTfRem/heJe0Zgur9J1M\n\
+8haawdSdJjH2RgmFoDeE2r8NVRfhbR8KnCO1ddTJKuS1N0/irHz21W4jt4rxzCvl\n\
+2nR42Fybap3O/g2JXMhNNROwZmNjgpsF7XVENCSuFO1jTywLaqjuXCg54IL7XVLG\n\
+omKNNNcc8h1FCeKj/nnbGMhodnFWKDTsJcbNmcOPNHo6ixzqMy/Hqc+mWYv7maAG\n\
+Jtevs3qgMZ8F9Qzr3HpUc6R3ZYYWDY/xxPisuKftOPZgtH979XC4mdf0WPnOBLqL\n\
+2DJ1zaBmjiGJolvb7XNVKcUfDXYw85ZTZQ5b9clI4e+6bmyWqQItlwt+Ati/uFEV\n\
+XzCj70B4lALX6xau1kLEpV9O1GERizYRz5P9NJNA7KoO5AVMp9w0DQTkt+LbXnZE\n\
+HHnWKy8xHQKZF9sR7YBPGLs/Ac6tviv5Ua15OgJ/8dLRZ/veyFfGo2yZsI+hKVU5\n\
+nCCJHBcAyFnm1hdvdwEdH33jDBjNB6ciotJZrf/3VYaIWSalADosHAgMWfXuWP+h\n\
+8XKXmzlxuHbTMQYtZPDgspS5aK+S4Q9wb8RRAYo=\n\
+-----END CERTIFICATE-----
+casdoor.organization-name=built-in
+casdoor.application-name=sharding
\ No newline at end of file
diff --git a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/api.js b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/api.js
index 26ff819..eedc934 100644
--- a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/api.js
+++ b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/api.js
@@ -18,5 +18,7 @@
 import API from '@/utils/api'
 
 export default {
-  getLogin: (params = {}) => API.post(`/api/login`, params)
+  getLogin: (params = {}) => API.post(`/api/login`, params),
+  getCasdoorLoginUrl: (params = {}) => API.get(`/api/casdoor-login-url`, params),
+  getCasdoorLogin: (params = {}) => API.post(`/api/casdoor-login`, params)
 }
diff --git a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/api.js b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/casdoor.js
similarity index 56%
copy from shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/api.js
copy to shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/casdoor.js
index 26ff819..25f561a 100644
--- a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/api.js
+++ b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/casdoor.js
@@ -15,8 +15,25 @@
  * limitations under the License.
  */
 
-import API from '@/utils/api'
+import API from './api'
 
 export default {
-  getLogin: (params = {}) => API.post(`/api/login`, params)
-}
+  loginByCasdoor: () => {
+    // callback
+    const urlSearchParams = new URLSearchParams(window.location.search)
+    const params = {
+      code: urlSearchParams.get('code'),
+      state: urlSearchParams.get('state')
+    }
+    if (params.code != null && params.state != null) {
+      API.getCasdoorLogin(params).then(res => {
+        const data = res.model
+        const store = window.localStorage
+        store.setItem('Access-Token', data.accessToken)
+        store.setItem('username', data.username)
+        store.setItem('isGuest', data.isGuest)
+        location.href = '/#/registry-center'
+      })
+    }
+  }
+}
\ No newline at end of file
diff --git a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/index.vue b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/index.vue
index cd7ebc0..b95cd70 100644
--- a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/index.vue
+++ b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/login/index.vue
@@ -56,6 +56,14 @@
           @click.native.prevent="handleLogin"
         >{{ $t("login.btnTxt") }}</el-button>
       </el-form-item>
+      <el-form-item class="btn-login">
+        <el-button
+          :loading="loading"
+          type="primary"
+          style="width:100%;"
+          @click.native.prevent="toLoginUrl()"
+        >{{ $t("Login with Casdoor") }}</el-button>
+      </el-form-item>
     </el-form>
     <s-footer style="position: fixed;bottom: 0;" />
   </div>
@@ -64,6 +72,9 @@
 <script>
 import SFooter from '../../components/Footer/index'
 import API from './api'
+import casdoor from './casdoor'
+
+casdoor.loginByCasdoor()
 export default {
   name: 'Login',
   components: {
@@ -106,6 +117,13 @@ export default {
         store.setItem('username', data.username)
         location.href = '#/registry-center'
       })
+    },
+    toLoginUrl() {
+    // redirect to casdoor login page
+      API.getCasdoorLoginUrl({ origin: window.location.origin }).then(res => {
+        const data = res.model
+        window.location.href = data.casdoorLoginUrl
+      })
     }
   }
 }