feat: add support for Casdoor authentication (#39)

diff --git a/backend/pom.xml b/backend/pom.xml
index fb5d805..a7d67a9 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -145,6 +145,12 @@
     </dependency>
 
     <dependency>
+      <groupId>org.casbin</groupId>
+      <artifactId>casdoor-spring-boot-starter</artifactId>
+      <version>1.3.0</version>
+    </dependency>
+
+    <dependency>
       <groupId>org.apache.iotdb</groupId>
       <artifactId>iotdb-session</artifactId>
       <version>0.12.5</version>
diff --git a/backend/src/main/java/org/apache/iotdb/admin/controller/UserController.java b/backend/src/main/java/org/apache/iotdb/admin/controller/UserController.java
index 93adb9a..9a001b1 100644
--- a/backend/src/main/java/org/apache/iotdb/admin/controller/UserController.java
+++ b/backend/src/main/java/org/apache/iotdb/admin/controller/UserController.java
@@ -33,6 +33,8 @@
 import io.jsonwebtoken.Claims;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.casbin.casdoor.entity.CasdoorUser;
+import org.casbin.casdoor.service.CasdoorAuthService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -53,6 +55,8 @@
 
   private static final Logger logger = LoggerFactory.getLogger(UserController.class);
 
+  @Autowired private CasdoorAuthService casdoorAuthService;
+
   @PostMapping("/login")
   @ApiOperation("login")
   public BaseVO<ConnectionVO> login(
@@ -71,6 +75,35 @@
     return BaseVO.success("Login  successful", connectionVO);
   }
 
+  @PostMapping("/getCasdoorUrl")
+  @ApiOperation("Get Casdoor Url")
+  public BaseVO<String> getCasdoorUrl(HttpServletRequest request, HttpServletResponse response)
+      throws BaseException {
+    String origin = request.getParameter("origin");
+    String url = casdoorAuthService.getSigninUrl(origin);
+    return BaseVO.success("Get Url successful", url);
+  }
+
+  @PostMapping("/loginWithCasdoor")
+  @ApiOperation("loginWithCasdoor")
+  public BaseVO<ConnectionVO> loginWithCasdoor(
+      @RequestParam("code") String code,
+      @RequestParam("state") String state,
+      HttpServletResponse response)
+      throws BaseException {
+    String token = casdoorAuthService.getOAuthToken(code, state);
+    CasdoorUser casdoorUser = casdoorAuthService.parseJwtToken(token);
+    User user = new User();
+    user.setId(casdoorUser.getRanking());
+    user.setName(casdoorUser.getName());
+    int userId = user.getId();
+    String name = user.getName();
+    List<ConnVO> connVOs = connectionService.getAllConnections(userId);
+    ConnectionVO connectionVO = new ConnectionVO(connVOs, userId, name);
+    response.addHeader("Authorization", JJwtTool.generateToken(user));
+    return BaseVO.success("Login  successful", connectionVO);
+  }
+
   @PostMapping("/save")
   @ApiOperation("Create user (not used)")
   public BaseVO save(@RequestBody User user) throws BaseException {
diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties
index e9e7335..e3fdf60 100644
--- a/backend/src/main/resources/application.properties
+++ b/backend/src/main/resources/application.properties
@@ -18,4 +18,11 @@
 #
 
 # Designate the configuration file
-spring.profiles.active=dev
\ No newline at end of file
+spring.profiles.active=dev
+
+casdoor.endpoint =
+casdoor.clientId = 
+casdoor.clientSecret =
+casdoor.certificate =
+casdoor.organizationName =
+casdoor.applicationName = 
\ No newline at end of file
diff --git a/frontend/.env b/frontend/.env
new file mode 100644
index 0000000..de09d64
--- /dev/null
+++ b/frontend/.env
@@ -0,0 +1 @@
+VUE_APP_CASDOOR=false
\ No newline at end of file
diff --git a/frontend/src/i18n/cn.js b/frontend/src/i18n/cn.js
index 03d2620..63ec22b 100644
--- a/frontend/src/i18n/cn.js
+++ b/frontend/src/i18n/cn.js
@@ -86,6 +86,7 @@
       placeholderPassword: '请输入密码',
       forgetPassWord: '忘记密码',
       signIn: '登录',
+      signInWithCasdoor: 'Casdoor登录',
       forgetPassword: '忘记密码',
       forgetPasswordTip: '请联系系统管理员 WeChat:loveher147',
       accountEmptyTip: '账号不能为空',
diff --git a/frontend/src/i18n/de.js b/frontend/src/i18n/de.js
index 6634c75..b988b5a 100644
--- a/frontend/src/i18n/de.js
+++ b/frontend/src/i18n/de.js
@@ -85,6 +85,7 @@
       placeholderPassword: 'Passwort eingeben',
       forgetPassWord: 'Passwort vergessen',
       signIn: 'Anmelden',
+      signInWithCasdoor: 'Loggen Sie sich mit casdoor ein',
       forgetPassword: 'Passwort vergessen',
       forgetPasswordTip: 'Bitte kontaktieren Sie Ihren Administrator WeChat:loveher147',
       accountEmptyTip: 'Benutzername darf nicht leer sein',
diff --git a/frontend/src/i18n/en.js b/frontend/src/i18n/en.js
index 87113a6..4008863 100644
--- a/frontend/src/i18n/en.js
+++ b/frontend/src/i18n/en.js
@@ -85,6 +85,7 @@
       placeholderPassword: 'Please Input Password',
       forgetPassWord: 'Forget Password',
       signIn: 'Sign In',
+      signInWithCasdoor: 'Sign In With Casdoor',
       forgetPassword: 'Forget Password',
       forgetPasswordTip: 'Please Contact System Administrator WeChat:loveher147',
       accountEmptyTip: 'Account Can Not Be Empty',
diff --git a/frontend/src/util/axios.js b/frontend/src/util/axios.js
index 193f878..e44c18f 100644
--- a/frontend/src/util/axios.js
+++ b/frontend/src/util/axios.js
@@ -22,7 +22,7 @@
 import router from '../router';
 
 const instance = axios.create({});
-const headerUrls = ['/api/login', '/api/downloadFile/template', '/api/downloadQueryLogFile'];
+const headerUrls = ['/api/login', '/api/loginWithCasdoor', '/api/downloadFile/template', '/api/downloadQueryLogFile'];
 const exportUrl = '/exportData';
 const downUrl = '/downloadFile';
 instance.defaults.withCredentials = true;
diff --git a/frontend/src/views/Login/index.vue b/frontend/src/views/Login/index.vue
index 3b18b4f..3dd06f2 100644
--- a/frontend/src/views/Login/index.vue
+++ b/frontend/src/views/Login/index.vue
@@ -60,6 +60,9 @@
               <el-button class="submit-btn" type="primary" @click="submitForm('ruleForm')">{{ $t('loginPage.signIn') }}</el-button>
             </el-form-item>
           </el-form>
+          <el-form-item v-if="casdoorSwitch === 'true'">
+            <el-button class="submit-btn" type="primary" @click="getLoginUrl()">{{ $t('loginPage.signInWithCasdoor') }}</el-button>
+          </el-form-item>
         </div>
       </div>
     </div>
@@ -90,6 +93,7 @@
     const formNameRef = ref(null);
     const dialogVisible = ref(false);
     const { t } = useI18n();
+    const casdoorSwitch = process.env.VUE_APP_CASDOOR;
     const ruleForm = reactive({
       account: '',
       passport: '',
@@ -145,6 +149,7 @@
       //   if (store.state.isLogin) {
       //     router.push({ name: "Root" });
       //   }
+      LoginWithCasdoor();
     });
 
     const submitForm = () => {
@@ -164,6 +169,31 @@
       });
     };
 
+    const getLoginUrl = () => {
+      axios.post('/getCasdoorUrl', {}, { params: { origin: window.location.origin } }).then((res) => {
+        window.location.href = res.data;
+      });
+    };
+
+    const LoginWithCasdoor = () => {
+      const url = window.document.location.href;
+      const u = new URL(url);
+      let codes = u.searchParams.get('code');
+      let state = u.searchParams.get('state');
+      if (codes != null && state != null) {
+        axios.post('/loginWithCasdoor', {}, { params: { code: codes, state: state } }).then((res) => {
+          if (res?.data?.code === '0') {
+            localStorage.setItem('authorization', res?.headers?.authorization);
+            store.commit('setLogin', true);
+            store.commit('setUserInfo', res.data || {});
+            router.push({ name: 'Root' });
+          } else {
+            ElMessage.error(t(`loginPage.loginErrorTip`));
+          }
+        });
+      }
+    };
+
     const showDialog = () => {
       dialogVisible.value = true;
     };
@@ -177,6 +207,9 @@
       rules,
       submitForm,
       showDialog,
+      getLoginUrl,
+      LoginWithCasdoor,
+      casdoorSwitch,
     };
   },
   components: { ElForm, ElFormItem, ElInput, ElButton, ElDialog, ElDropdown, ElDropdownMenu, ElDropdownItem },