Ver Fonte

ext login

star7th há 4 anos atrás
pai
commit
3d759210e5

+ 55 - 2
server/Application/Api/Controller/AdminSettingController.class.php

@@ -53,7 +53,7 @@ class AdminSettingController extends BaseController {
 
     }
 
-    //保存配置
+    //保存Ldap配置
     public function saveLdapConfig(){
         $login_user = $this->checkLogin();
         $this->checkAdmin();
@@ -101,7 +101,7 @@ class AdminSettingController extends BaseController {
 
     }
 
-    //加载配置
+    //加载Ldap配置
     public function loadLdapConfig(){
         $login_user = $this->checkLogin();
         $this->checkAdmin();
@@ -122,6 +122,59 @@ class AdminSettingController extends BaseController {
 
     }
 
+    //保存Oauth2配置
+    public function saveOauth2Config(){
+        $login_user = $this->checkLogin();
+        $this->checkAdmin();
+        $oauth2_open = intval(I("oauth2_open")) ;
+        $oauth2_form = I("oauth2_form") ;
+        D("Options")->set("oauth2_form" , json_encode( $oauth2_form)) ;
+        D("Options")->set("oauth2_open" ,$oauth2_open) ;
+        $this->sendResult(array());
+
+    }
+
+    //加载Oauth2配置
+    public function loadOauth2Config(){
+        $login_user = $this->checkLogin();
+        $this->checkAdmin();
+        $oauth2_open = D("Options")->get("oauth2_open" ) ;
+        $oauth2_form = D("Options")->get("oauth2_form" ) ;
+        $oauth2_form = json_decode($oauth2_form,1);
+        
+        //如果强等于false,那就是尚未有数据。关闭注册应该是有数据且数据为字符串0
+        if ($register_open === false) {
+            $this->sendResult(array());
+        }else{
+            $array = array(
+                "oauth2_open"=>$oauth2_open ,
+                "oauth2_form"=>$oauth2_form ,
+                );
+            $this->sendResult($array);
+        }
+
+    }
+
+    public function getLoginSecretToken(){
+        $login_user = $this->checkLogin();
+        $this->checkAdmin();
+        $login_secret_token = D("Options")->get("login_secret_token") ;
+        if(!$login_secret_token){
+            $login_secret_token = md5("rgrsfsrfsrf".time().rand());
+            D("Options")->set("login_secret_token",$login_secret_token) ;
+        }
+        $this->sendResult(array("login_secret_token"=>$login_secret_token));
+
+    }
+
+    public function resetLoginSecretToken(){
+        $login_user = $this->checkLogin();
+        $this->checkAdmin();
+        $login_secret_token = md5("rgrsfsrfsrf".time().rand());
+        D("Options")->set("login_secret_token",$login_secret_token) ;
+        $this->sendResult(array("login_secret_token"=>$login_secret_token));
+
+    }
 
 
     public function checkLdapLogin(){

+ 42 - 23
server/Application/Api/Controller/ExtLoginController.class.php

@@ -4,23 +4,32 @@ use Think\Controller;
 class ExtLoginController extends BaseController {
 
 
-    // 根据用户名和密码串登录
-    public function byName(){
+    // 根据用户名和LoginSecretToken登录
+    public function bySecretKey(){
         $username = I("username") ;
-        $password_md5 = strtolower(I("password_md5")); // 密码md5之后的加密串
+        $key = I("key") ;
+        $time = I("time") ;
+        $token = I("token") ;
         $redirect = I("redirect") ;
-        
 
-        //防止枚举破解。检查密码的次数。如果错误超过1000次,则不允许。
-        $key= 'login_fail_times_'.$username;
-        if(!D("VerifyCode")->_check_times($key,1000)){
-            $this->sendError(10101,"密码错误太频繁,请24小时后再试");
+        if($time < (time() - 60) ){
+            $this->sendError(10101,"已过期");
+            return ;
+        }
+        $login_secret_token = D("Options")->get("login_secret_token") ;
+
+        $new_token = md5($username.$login_secret_token.$time);
+        if($token !=  $new_token){
+            $this->sendError(10101,"token不正确");
             return ;
         }
+        
+        $res = D("User")->where("( username='%s' ) ",array($username))->find();
+        if(!$res){
+            D("User")->register($username,md5("savsnyjh".time().rand()));
+            $res = D("User")->where("( username='%s' ) ",array($username))->find();
 
-        $password = md5(base64_encode($password_md5).'576hbgh6');
-        $where=array($username,$password);
-        $res = D("User")->where("( username='%s'  and password='%s' ) ",$where)->find();
+        }
         if($res){
             // var_dump($res); return ;
             if($res['groupid'] == 1){
@@ -38,22 +47,30 @@ class ExtLoginController extends BaseController {
                 header("location:../web/#/item/index");
             }
             
-        }else{
-            D("VerifyCode")->_ins_times($key);//输错密码则设置输错次数
         }
     }
 
     public function oauth2(){
         $redirect = I("redirect") ;
         session('redirect',$redirect) ;
-        $clientId = 'a36df4c9-5ed4-440b-8f69-7535d2947213';
-        $clientSecret = 'F2m6MjIwNTIwMjEyMjE3NDYxMTM8Lr';
-        $oauthUrl = 'https://192.168.8.160:8443/maxkey/authz/oauth/v20';
-        $redirectUri = 'http://192.168.8.160/showdoc/server/?s=/api/ExtLogin/oauth2';
-        $urlAuthorize = $oauthUrl.'/authorize';
-        $urlAccessToken = $oauthUrl.'/token';
-        $urlResourceOwnerDetails = $oauthUrl.'/resource' ;
-        $urlUserInfo = 'https://192.168.8.160:8443/maxkey/api/oauth/v20/me';
+        $oauth2_open = D("Options")->get("oauth2_open" ) ;
+        $oauth2_form = D("Options")->get("oauth2_form" ) ;
+        $oauth2_form = json_decode($oauth2_form,1);
+
+        if(!$oauth2_open){
+            echo "尚未启用oauth2";
+            return ;
+        }
+        
+
+        $clientId = $oauth2_form['client_id'] ;
+        $clientSecret = $oauth2_form['client_secret']  ;
+        $redirectUri = $oauth2_form['redirectUri'];
+        $urlAuthorize = $oauth2_form['protocol']."://".$oauth2_form['host'].$oauth2_form['authorize_path'] ;
+        $urlAccessToken = $oauth2_form['protocol']."://".$oauth2_form['host'].$oauth2_form['token_path'] ;
+        $urlResourceOwnerDetails = $oauth2_form['protocol']."://".$oauth2_form['host'].$oauth2_form['resource_path'] ;
+        $urlUserInfo = $oauth2_form['protocol']."://".$oauth2_form['host'].$oauth2_form['userinfo_path'] ;
+    
         $provider = new \League\OAuth2\Client\Provider\GenericProvider([
             'clientId'                => $clientId,    // The client ID assigned to you by the provider
             'clientSecret'            => $clientSecret,    // The client password assigned to you by the provider
@@ -108,8 +125,8 @@ class ExtLoginController extends BaseController {
                 $res = http_post($urlUserInfo,array(
                     "access_token"=>$accessToken->getToken()
                 ));
-                if($res){
-                    $res_array = json_decode($res, true);
+                $res_array = json_decode($res, true);
+                if($res_array){
                     $username = $res_array['username'] ;
                     $info = D("User")->where("username='%s'" ,array($username))->find();
                     if(!$info){
@@ -130,6 +147,8 @@ class ExtLoginController extends BaseController {
                         header("location:../web/#/item/index");
                     }
 
+                }else{
+                    echo "登录成功但无法获取用户信息";
                 }
 
 

+ 18 - 0
server/Application/Api/Controller/UserController.class.php

@@ -339,6 +339,24 @@ class UserController extends BaseController {
 
     }
 
+    public function oauthInfo(){
+        $oauth2_open = D("Options")->get("oauth2_open" ) ;
+        $oauth2_form = D("Options")->get("oauth2_form" ) ;
+        $oauth2_entrance_tips = '';
+        if($oauth2_form){
+            $oauth2_form = json_decode($oauth2_form,1);
+            if($oauth2_form && $oauth2_form['entrance_tips']){
+                $oauth2_entrance_tips = $oauth2_form['entrance_tips'] ;
+            }
+        }
+        $this->sendResult(array(
+            "oauth2_open" => $oauth2_open ,
+            "oauth2_entrance_tips" => $oauth2_entrance_tips ,
+        ));
+
+    }
+
+
 
 
 }

+ 131 - 21
web_src/src/components/admin/extLogin/Index.vue

@@ -2,7 +2,7 @@
   <div class="hello">
     <el-tabs type="border-card">
       <el-tab-pane label="LDAP">
-        <el-form ref="form" :model="form" label-width="150px">
+        <el-form ref="form" label-width="150px">
           <el-form-item :label="$t('ldap_open_label')">
             <el-switch v-model="form.ldap_open"></el-switch>
           </el-form-item>
@@ -72,21 +72,32 @@
         </el-form>
       </el-tab-pane>
       <el-tab-pane label="OAuth2">
-        <el-form ref="form" :model="form" label-width="150px">
-          <el-form-item label="启动OAuth2登录">
-            <el-switch v-model="form.ldap_open"></el-switch>
+        <el-form ref="form" label-width="150px">
+          <el-form-item :label="$t('enable_oauth')">
+            <el-switch v-model="form.oauth2_open"></el-switch>
+          </el-form-item>
+          <el-form-item label="callback url">
+            <el-input
+              v-model="form.oauth2_form.redirectUri"
+              class="form-el"
+            ></el-input>
+          </el-form-item>
+          <el-form-item :label="$t('callback_eg')">
+            http://{{
+              $t('your_showdoc_server')
+            }}/server/?s=/api/ExtLogin/oauth2
           </el-form-item>
           <div>
-            <el-form-item label="入口文字">
+            <el-form-item :label="$t('入口文字提示')">
               <el-input
-                v-model="form.ldap_form.host"
-                placeholder="eg: 使用公司OA登录"
+                v-model="form.oauth2_form.entrance_tips"
+                placeholder=""
                 class="form-el"
               ></el-input>
               <el-tooltip
                 class="item"
                 effect="dark"
-                content="当启动OAuth2登录时候,登录界面将在输入框的下方出现此入口。你可以填上如'使用公司OA登录'这样的提示"
+                :content="$t('entrance_tips_content')"
                 placement="top"
               >
                 <i class="el-icon-question"></i>
@@ -94,61 +105,97 @@
             </el-form-item>
             <el-form-item label="Client id">
               <el-input
-                v-model="form.ldap_form.host"
+                v-model="form.oauth2_form.client_id"
                 class="form-el"
               ></el-input>
             </el-form-item>
             <el-form-item label="Client secret">
               <el-input
-                v-model="form.ldap_form.host"
+                v-model="form.oauth2_form.client_secret"
                 class="form-el"
               ></el-input>
             </el-form-item>
+
             <el-form-item label="Oauth host">
-              <el-select style="width:100px;">
+              <el-select
+                v-model="form.oauth2_form.protocol"
+                style="width:100px;"
+              >
                 <el-option label="http://" value="http"></el-option>
                 <el-option label="https://" value="https"></el-option>
               </el-select>
               <el-input
+                v-model="form.oauth2_form.host"
                 class="form-el"
                 placeholder="eg:  sso.your-site.com"
               ></el-input>
             </el-form-item>
             <el-form-item label="Authorize path">
               <el-input
-                v-model="form.ldap_form.host"
+                v-model="form.oauth2_form.authorize_path"
                 placeholder="eg:  /oauth/v2/authorize"
                 class="form-el"
               ></el-input>
             </el-form-item>
             <el-form-item label="AccessToken path">
               <el-input
-                v-model="form.ldap_form.host"
+                v-model="form.oauth2_form.token_path"
                 placeholder="eg:  /oauth/v2/token"
                 class="form-el"
               ></el-input>
             </el-form-item>
             <el-form-item label="Resource path">
               <el-input
-                v-model="form.ldap_form.host"
+                v-model="form.oauth2_form.resource_path"
                 placeholder="eg:  /oauth/v2/resource"
                 class="form-el"
               ></el-input>
             </el-form-item>
+            <el-form-item label="User info path">
+              <el-input
+                v-model="form.oauth2_form.userinfo_path"
+                placeholder="eg:  /oauth/v2/me"
+                class="form-el"
+              ></el-input>
+              <el-tooltip
+                class="item"
+                effect="dark"
+                :content="$t('userinfo_path_content')"
+                placement="top"
+              >
+                <i class="el-icon-question"></i>
+              </el-tooltip>
+            </el-form-item>
           </div>
-
           <br />
           <el-form-item>
-            <el-button type="primary" @click="saveLdapConfig">{{
+            <el-button type="primary" @click="saveOauth2Config">{{
               $t('save')
             }}</el-button>
             <el-button>{{ $t('cancel') }}</el-button>
           </el-form-item>
         </el-form>
       </el-tab-pane>
-      <el-tab-pane label="通用接入">
-        <div style="min-height:600px;margin-top:20px;">
-          通用接入提供的是一种自动登录showdoc的能力,需要自己根据文档开发集成,详情请看:这里
+      <el-tab-pane label="通用接入" v-if="lang == 'zh-cn'">
+        <div style="min-height:600px;margin-top:50px;margin-left:30px;">
+          <p>
+            LoginSecretKey:&nbsp;
+            <el-input
+              readonly
+              v-model="login_secret_token"
+              class="form-el"
+            ></el-input>
+            <el-button @click="resetLoginSecretToken">{{
+              $t('reset')
+            }}</el-button>
+          </p>
+          <p>
+            通用接入提供的是一种自动登录showdoc的能力,需要自己根据文档开发集成。<a
+              href="https://www.showdoc.com.cn/p/0fb2753c5a48acc7c3fbbb00f9504e6b"
+              target="_blank"
+              >点击这里查看文档</a
+            >
+          </p>
         </div>
       </el-tab-pane>
     </el-tabs>
@@ -157,7 +204,7 @@
 
 <style scoped>
 .form-el {
-  width: 230px;
+  width: 400px;
 }
 </style>
 
@@ -175,9 +222,24 @@ export default {
           bind_dn: '',
           bind_password: '',
           user_field: ''
+        },
+        oauth2_open: false,
+        oauth2_form: {
+          redirectUri: '',
+          entrance_tips: '',
+          client_id: '',
+          client_secret: '',
+          protocol: 'https',
+          host: '',
+          authorize_path: '',
+          token_path: '',
+          resource_path: '',
+          userinfo_path: ''
         }
       },
-      itemList: []
+      login_secret_token: '',
+      itemList: [],
+      lang: ''
     }
   },
   methods: {
@@ -206,10 +268,58 @@ export default {
           this.$alert(response.data.error_message)
         }
       })
+    },
+    saveOauth2Config() {
+      var url = DocConfig.server + '/api/adminSetting/saveOauth2Config'
+      this.axios.post(url, this.form).then(response => {
+        if (response.data.error_code === 0) {
+          this.$alert(this.$t('success'))
+        } else {
+          this.$alert(response.data.error_message)
+        }
+      })
+    },
+    loadOauth2Config() {
+      var url = DocConfig.server + '/api/adminSetting/loadOauth2Config'
+      this.axios.post(url, this.form).then(response => {
+        if (response.data.error_code === 0) {
+          if (response.data.data.length === 0) {
+            return
+          }
+          this.form.oauth2_open = response.data.data.oauth2_open > 0
+          this.form.oauth2_form = response.data.data.oauth2_form
+            ? response.data.data.oauth2_form
+            : this.form.oauth2_form
+        } else {
+          this.$alert(response.data.error_message)
+        }
+      })
+    },
+
+    getLoginSecretToken() {
+      this.request('/api/adminSetting/getLoginSecretToken', {}).then(data => {
+        this.login_secret_token = data.data.login_secret_token
+      })
+    },
+    resetLoginSecretToken() {
+      this.$confirm(this.$t('confirm') + '?', ' ', {
+        confirmButtonText: this.$t('confirm'),
+        cancelButtonText: this.$t('cancel'),
+        type: 'warning'
+      }).then(() => {
+        this.request('/api/adminSetting/resetLoginSecretToken', {}).then(
+          data => {
+            this.login_secret_token = data.data.login_secret_token
+          }
+        )
+      })
     }
   },
   mounted() {
     this.loadLdapConfig()
+    this.loadOauth2Config()
+    this.getLoginSecretToken()
+    this.lang = DocConfig.lang
   },
   beforeDestroy() {
     this.$message.closeAll()

+ 15 - 1
web_src/src/components/user/Login.vue

@@ -54,6 +54,7 @@
               $t('register_new_account')
             }}</router-link
             >&nbsp;&nbsp;&nbsp;
+            <a :href="oauth2_url">{{ oauth2_entrance_tips }}</a>
           </el-form-item>
         </el-form>
       </el-card>
@@ -74,7 +75,9 @@ export default {
       v_code: '',
       v_code_img: DocConfig.server + '/api/common/verify',
       show_v_code: false,
-      is_show_alert: false
+      is_show_alert: false,
+      oauth2_entrance_tips: '',
+      oauth2_url: DocConfig.server + '/api/ExtLogin/oauth2'
     }
   },
   methods: {
@@ -130,6 +133,16 @@ export default {
     checkDb() {
       var url = DocConfig.server + '/api/update/checkDb'
       this.axios.get(url)
+    },
+    getOauth() {
+      var url = DocConfig.server + '/api/user/oauthInfo'
+      this.axios.get(url).then(response => {
+        if (response.data.error_code === 0) {
+          if (response.data.data.oauth2_open > 0) {
+            this.oauth2_entrance_tips = response.data.data.oauth2_entrance_tips
+          }
+        }
+      })
     }
   },
   mounted() {
@@ -147,6 +160,7 @@ export default {
 
     this.script_cron()
     this.checkDb()
+    this.getOauth()
   },
   watch: {
     $route(to, from) {

+ 10 - 2
web_src/static/lang/en.js

@@ -435,6 +435,14 @@ exports.default = {
   manage_item_group: 'Manage group',
   draggable_tips: 'Press and hold to drag sort',
   item_group_empty_tips: 'There is no grouping data. You can click the top right corner to create a new group',
-  select_item: 'Select item'
-
+  select_item: 'Select item',
+
+  ext_login:'Ext login',
+  enable_oauth: 'OAuth Login',
+  your_showdoc_server: '[your-showdoc-server]',
+  callback_eg: ' callback url example',
+  entrance_tips_label: 'Entrance text prompt',
+  entrance_tips_content: "When OAuth2 login is enabled, this entry will appear below the input box on the login interface. You can fill in a prompt such as 'Log in with company OA'",
+  userinfo_path_content:'The interface path for obtaining user information after successful login. The interface should return a json string and include the username field',
+  reset:'reset',
 }

+ 22 - 25
web_src/static/lang/zh-CN.js

@@ -9,37 +9,25 @@ exports.default = {
   section_description1_1: '一个非常适合IT团队的',
   section_description1_2: '在线API文档、技术文档工具',
   section_title2: 'API文档',
-  section_description2_1:
-    'APP、web前端与服务器常用API来进行交互',
-  section_description2_2:
-    'ShowDoc可以非常方便快速地编写出美观的API文档',
+  section_description2_1: 'APP、web前端与服务器常用API来进行交互',
+  section_description2_2: 'ShowDoc可以非常方便快速地编写出美观的API文档',
   section_title3: '数据字典',
-  section_description3_1:
-    '好的数据字典可以方便地向别人描述你的数据库结构',
-  section_description3_2:
-    '用ShowDoc可以编辑出美观的数据字典',
+  section_description3_1: '好的数据字典可以方便地向别人描述你的数据库结构',
+  section_description3_2: '用ShowDoc可以编辑出美观的数据字典',
   section_title4: '说明文档',
-  section_description4_1:
-    '你完全可以使用 ShowDoc来编写一些工具的说明书',
-  section_description4_2:
-    '也可以编写一些技术规范说明文档以供团队查阅',
+  section_description4_1: '你完全可以使用 ShowDoc来编写一些工具的说明书',
+  section_description4_2: '也可以编写一些技术规范说明文档以供团队查阅',
   section_title5: '团队协作',
   section_description5: '团队权限管理机制让团队良好地协同编写文档',
   section_title6: '文档自动化',
-  section_description6_1:
-    '可从代码注释中自动生成文档',
-  section_description6_2:
-    '搭配的RunApi客户端,可调试接口和自动生成文档',
+  section_description6_1: '可从代码注释中自动生成文档',
+  section_description6_2: '搭配的RunApi客户端,可调试接口和自动生成文档',
   section_title7: '免费开源',
-  section_description7_1:
-    'ShowDoc提供免费开源的版本',
-  section_description7_2:
-    '你可以选择将ShowDoc部署到你的服务器',
+  section_description7_1: 'ShowDoc提供免费开源的版本',
+  section_description7_2: '你可以选择将ShowDoc部署到你的服务器',
   section_title8: '在线托管',
-  section_description8_1:
-    'www.showdoc.com.cn 安全稳定的在线文档托管服务',
-  section_description8_2:
-    '你可以放心地选择托管你的文档数据在云端',
+  section_description8_1: 'www.showdoc.com.cn 安全稳定的在线文档托管服务',
+  section_description8_2: '你可以放心地选择托管你的文档数据在云端',
   section_title9: '立即体验',
   section_description9: '超过100000+互联网团队正在使用ShowDoc',
 
@@ -425,5 +413,14 @@ exports.default = {
   manage_item_group: '项目分组管理',
   draggable_tips: '按住可拖动排序',
   item_group_empty_tips: '暂无分组数据。可点击右上角新建分组',
-  select_item: '选择项目'
+  select_item: '选择项目',
+
+  ext_login:'集成登录',
+  enable_oauth: '启动OAuth2登录',
+  your_showdoc_server: '【你的showdoc地址】',
+  callback_eg: ' callback url填写示例',
+  entrance_tips_label: '入口文字提示',
+  entrance_tips_content: "当启用OAuth2登录时候,登录界面将在输入框的下方出现此入口。你可以填上如'使用公司OA登录'这样的提示",
+  userinfo_path_content: '登录成功后获取用户信息的接口路径。该接口应当返回json字符串且包含username字段',
+  reset:'重新生成',
 }