zrj
2024-06-05 d06a882e870f2aa26934ccd4b552d5292679320f
登录调整(集成单点登录)
5 files modified
1 files added
650 ■■■■■ changed files
src/api/oauth/oauth.js 38 ●●●●● patch | view | raw | blame | history
src/api/user.js 36 ●●●●● patch | view | raw | blame | history
src/page/login/userlogin.vue 507 ●●●●● patch | view | raw | blame | history
src/router/axios.js 11 ●●●●● patch | view | raw | blame | history
src/store/modules/user.js 40 ●●●●● patch | view | raw | blame | history
vue.config.js 18 ●●●●● patch | view | raw | blame | history
src/api/oauth/oauth.js
New file
@@ -0,0 +1,38 @@
import request from '@/router/axios';
export const authorize = ( params) => {
  return request({
    url: '/api/oauth/authorize',
    method: 'get',
    params: params
  })
}
export const getPage = (current, size, params) => {
  return request({
    url: '/api/blade-convenienceHotline/convenienceHotline/page',
    method: 'get',
    params: {
      ...params,
      current,
      size,
    }
  })
}
export const add = (row) => {
  return request({
    url: '/api/blade-convenienceHotline/convenienceHotline/submit',
    method: 'post',
    data: row
  })
}
export const update = (row) => {
  return request({
    url: '/api/blade-convenienceHotline/convenienceHotline/submit',
    method: 'post',
    data: row
  })
}
src/api/user.js
@@ -17,9 +17,7 @@
    password,
    grant_type:"password",
    scope: "all",
    type,
    // 查全部-暂定
    loginType:1
    type
  }
});
@@ -70,6 +68,38 @@
  }
});
export const loginForm = (tenant_id, username, password,client_id,response_type,redirect_uri) => request({
  url: '/api/oauth/form',
  method: 'post',
  headers:{
    'Content-Type':"application/x-www-form-urlencoded",
    'client_id':client_id,
    'response_type':response_type,
    'redirect_uri':redirect_uri
  },
  params: {
    tenant_id,
    username,
    password
  }
});
export const loginTo = (tenantId, username, password,client_id,response_type,redirect_uri) => request({
  url: '/api/oauth/loginTo',
  method: 'post',
  headers:{
    'Content-Type':"application/x-www-form-urlencoded",
    'client_id':client_id,
    'response_type':response_type,
    'redirect_uri':redirect_uri
  },
  params: {
    tenantId,
    username,
    password
  }
});
export const registerGuest = (form, oauthId) => request({
  url: '/api/blade-system/user/register-guest',
  method: 'post',
src/page/login/userlogin.vue
@@ -1,6 +1,13 @@
<template>
    <el-form class="login-form" status-icon :rules="loginRules" ref="loginForm" :model="loginForm" label-width="0">
        <!-- <el-form-item v-if="tenantMode" prop="tenantId">
  <el-form
    class="login-form"
    status-icon
    :rules="loginRules"
    ref="loginForm"
    :model="loginForm"
    label-width="0"
  >
    <!-- <el-form-item v-if="tenantMode" prop="tenantId">
      <el-input size="small"
                @keyup.enter.native="handleLogin"
                v-model="loginForm.tenantId"
@@ -9,20 +16,35 @@
        <i slot="prefix" class="icon-quanxian"/>
      </el-input>
    </el-form-item> -->
        <el-form-item prop="username">
            <el-input size="small" @keyup.enter.native="handleLogin" v-model="loginForm.username" auto-complete="off"
                :placeholder="$t('login.username')">
                <i slot="prefix" class="icon-yonghu" />
            </el-input>
        </el-form-item>
        <el-form-item prop="password">
            <el-input size="small" @keyup.enter.native="handleLogin" :type="passwordType" v-model="loginForm.password"
                auto-complete="off" :placeholder="$t('login.password')">
                <i class="el-icon-view el-input__icon" slot="suffix" @click="showPassword" />
                <i slot="prefix" class="icon-mima" />
            </el-input>
        </el-form-item>
        <!-- <el-form-item v-if="this.website.captchaMode" prop="code">
    <el-form-item prop="username">
      <el-input
        size="small"
        @keyup.enter.native="handleLogin"
        v-model="loginForm.username"
        auto-complete="off"
        :placeholder="$t('login.username')"
      >
        <i slot="prefix" class="icon-yonghu" />
      </el-input>
    </el-form-item>
    <el-form-item prop="password">
      <el-input
        size="small"
        @keyup.enter.native="handleLogin"
        :type="passwordType"
        v-model="loginForm.password"
        auto-complete="off"
        :placeholder="$t('login.password')"
      >
        <i
          class="el-icon-view el-input__icon"
          slot="suffix"
          @click="showPassword"
        />
        <i slot="prefix" class="icon-mima" />
      </el-input>
    </el-form-item>
    <!-- <el-form-item v-if="this.website.captchaMode" prop="code">
      <el-row :span="24">
        <el-col :span="16">
          <el-input size="small"
@@ -41,221 +63,276 @@
        </el-col>
      </el-row>
    </el-form-item> -->
        <el-form-item>
            <el-button type="primary" size="small" @click.native.prevent="handleLogin" class="login-submit">{{
                $t('login.submit') }}
            </el-button>
        </el-form-item>
        <el-dialog title="用户信息选择" append-to-body :visible.sync="userBox" width="350px">
            <avue-form :option="userOption" v-model="userForm" @submit="submitLogin" />
        </el-dialog>
    </el-form>
    <el-form-item>
      <el-button
        type="primary"
        size="small"
        @click.native.prevent="handleLogin"
        class="login-submit"
        >{{ $t("login.submit") }}
      </el-button>
    </el-form-item>
    <el-dialog
      title="用户信息选择"
      append-to-body
      :visible.sync="userBox"
      width="350px"
    >
      <avue-form
        :option="userOption"
        v-model="userForm"
        @submit="submitLogin"
      />
    </el-dialog>
  </el-form>
</template>
<script>
import Layout from "@/page/index/"
import { mapGetters } from "vuex"
import { info } from "@/api/system/tenant"
import Layout from "@/page/index/";
import { mapGetters } from "vuex";
import { info } from "@/api/system/tenant";
import { authorize } from "@/api/oauth/oauth";
// import {getCaptcha} from "@/api/user";
import { getTopUrl } from "@/util/util"
import { getTopUrl } from "@/util/util";
import axios from "axios";
export default {
    name: "userlogin",
    data () {
        return {
            tenantMode: this.website.tenantMode,
            loginForm: {
                //租户ID
                tenantId: "000000",
                //部门ID
                deptId: "",
                //角色ID
                roleId: "",
                //用户名
                username: "",
                //密码
                password: "",
                //账号类型
                type: "account",
                //验证码的值
                code: "",
                //验证码的索引
                key: "",
                //预加载白色背景
                image: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
  name: "userlogin",
  data() {
    return {
      params: {
        // URL 上的 client_id、scope 等参数
        response_type: undefined,
        client_id: undefined,
        redirect_uri: undefined,
      },
      tenantMode: this.website.tenantMode,
      loginForm: {
        //租户ID
        tenantId: "000000",
        //部门ID
        deptId: "",
        //角色ID
        roleId: "",
        //用户名
        username: "",
        //密码
        password: "",
        //账号类型
        type: "account",
        //验证码的值
        code: "",
        //验证码的索引
        key: "",
        //预加载白色背景
        image:
          "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
      },
      loginRules: {
        // tenantId: [
        //   {required: false, message: "请输入租户ID", trigger: "blur"}
        // ],
        username: [
          { required: true, message: "请输入用户名", trigger: "blur" },
        ],
        password: [
          { required: true, message: "请输入密码", trigger: "blur" },
          { min: 1, message: "密码长度最少为6位", trigger: "blur" },
        ],
      },
      passwordType: "password",
      userBox: false,
      userForm: {
        deptId: "",
        roleId: "",
      },
      userOption: {
        labelWidth: 70,
        submitBtn: true,
        emptyBtn: false,
        submitText: "登录",
        column: [
          {
            label: "部门",
            prop: "deptId",
            type: "select",
            props: {
              label: "deptName",
              value: "id",
            },
            loginRules: {
                // tenantId: [
                //   {required: false, message: "请输入租户ID", trigger: "blur"}
                // ],
                username: [
                    { required: true, message: "请输入用户名", trigger: "blur" }
                ],
                password: [
                    { required: true, message: "请输入密码", trigger: "blur" },
                    { min: 1, message: "密码长度最少为6位", trigger: "blur" }
                ]
            dicUrl: "/api/blade-system/dept/select",
            span: 24,
            display: false,
            rules: [
              {
                required: true,
                message: "请选择部门",
                trigger: "blur",
              },
            ],
          },
          {
            label: "角色",
            prop: "roleId",
            type: "select",
            props: {
              label: "roleName",
              value: "id",
            },
            passwordType: "password",
            userBox: false,
            userForm: {
                deptId: '',
                roleId: ''
            },
            userOption: {
                labelWidth: 70,
                submitBtn: true,
                emptyBtn: false,
                submitText: '登录',
                column: [
                    {
                        label: '部门',
                        prop: 'deptId',
                        type: 'select',
                        props: {
                            label: 'deptName',
                            value: 'id'
                        },
                        dicUrl: '/api/blade-system/dept/select',
                        span: 24,
                        display: false,
                        rules: [{
                            required: true,
                            message: "请选择部门",
                            trigger: "blur"
                        }],
                    },
                    {
                        label: '角色',
                        prop: 'roleId',
                        type: 'select',
                        props: {
                            label: 'roleName',
                            value: 'id'
                        },
                        dicUrl: '/api/blade-system/role/select',
                        span: 24,
                        display: false,
                        rules: [{
                            required: true,
                            message: "请选择角色",
                            trigger: "blur"
                        }],
                    },
                ]
            }
        }
            dicUrl: "/api/blade-system/role/select",
            span: 24,
            display: false,
            rules: [
              {
                required: true,
                message: "请选择角色",
                trigger: "blur",
              },
            ],
          },
        ],
      },
    };
  },
  created() {
    this.getTenant();
    //   this.refreshCode();
    this.params.response_type = this.$route.query.response_type;
    this.params.client_id = this.$route.query.client_id;
    this.params.redirect_uri = this.$route.query.redirect_uri;
    this.loginForm["response_type"] = this.$route.query.response_type;
    this.loginForm["client_id"] = this.$route.query.client_id;
    this.loginForm["redirect_uri"] = this.$route.query.redirect_uri;
  },
  mounted() {},
  watch: {
    "loginForm.deptId"() {
      const column = this.findObject(this.userOption.column, "deptId");
      if (this.loginForm.deptId.includes(",")) {
        column.dicUrl = `/api/blade-system/dept/select?deptId=${this.loginForm.deptId}`;
        column.display = true;
      } else {
        column.dicUrl = "";
      }
    },
    created () {
        this.getTenant()
        //   this.refreshCode();
    "loginForm.roleId"() {
      const column = this.findObject(this.userOption.column, "roleId");
      if (this.loginForm.roleId.includes(",")) {
        column.dicUrl = `/api/blade-system/role/select?roleId=${this.loginForm.roleId}`;
        column.display = true;
      } else {
        column.dicUrl = "";
      }
    },
    mounted () {
  },
  computed: {
    ...mapGetters(["tagWel", "userInfo"]),
  },
  props: [],
  methods: {
    async getAuthorize(params) {
      const response = await axios.get(
        "http://localhost:8100/oauth/authorize?client_id=sword&response_type=code&redirect_uri=https://www.baidu.com"
      );
      this.headers = response.headers;
    },
    watch: {
        'loginForm.deptId' () {
            const column = this.findObject(this.userOption.column, "deptId")
            if (this.loginForm.deptId.includes(",")) {
                column.dicUrl = `/api/blade-system/dept/select?deptId=${this.loginForm.deptId}`
                column.display = true
            } else {
                column.dicUrl = ''
            }
        },
        'loginForm.roleId' () {
            const column = this.findObject(this.userOption.column, "roleId")
            if (this.loginForm.roleId.includes(",")) {
                column.dicUrl = `/api/blade-system/role/select?roleId=${this.loginForm.roleId}`
                column.display = true
            } else {
                column.dicUrl = ''
            }
        }
    // refreshCode () {
    //     if (this.website.captchaMode) {
    //         getCaptcha().then(res => {
    //             const data = res.data
    //             this.loginForm.key = data.key
    //             this.loginForm.image = data.image
    //         })
    //     }
    // },
    showPassword() {
      this.passwordType === ""
        ? (this.passwordType = "password")
        : (this.passwordType = "");
    },
    computed: {
        ...mapGetters(["tagWel", "userInfo"])
    submitLogin(form, done) {
      if (form.deptId !== "") {
        this.loginForm.deptId = form.deptId;
      }
      if (form.roleId !== "") {
        this.loginForm.roleId = form.roleId;
      }
      this.handleLogin();
      done();
    },
    props: [],
    methods: {
        // refreshCode () {
        //     if (this.website.captchaMode) {
        //         getCaptcha().then(res => {
        //             const data = res.data
        //             this.loginForm.key = data.key
        //             this.loginForm.image = data.image
        //         })
        //     }
        // },
        showPassword () {
            this.passwordType === ""
                ? (this.passwordType = "password")
                : (this.passwordType = "")
        },
        submitLogin (form, done) {
            if (form.deptId !== '') {
                this.loginForm.deptId = form.deptId
            }
            if (form.roleId !== '') {
                this.loginForm.roleId = form.roleId
            }
            this.handleLogin()
            done()
        },
        handleLogin () {
            this.$refs.loginForm.validate(valid => {
                if (valid) {
                    const loading = this.$loading({
                        lock: true,
                        text: '登录中,请稍后。。。',
                        spinner: "el-icon-loading"
                    })
                    this.$store.dispatch("LoginByUsername", this.loginForm).then((res) => {
                        if (res.error) {
                            loading.close()
                            return
                        }
                        if (this.website.switchMode) {
                            const deptId = this.userInfo.dept_id
                            const roleId = this.userInfo.role_id
                            if (deptId.includes(",") || roleId.includes(",")) {
                                this.loginForm.deptId = deptId
                                this.loginForm.roleId = roleId
                                this.userBox = true
                                this.$store.dispatch("LogOut").then(() => {
                                    loading.close()
                                })
                                return false
                            }
                        }
                        this.$router.$avueRouter.formatRoutes(res.menuData, true)
                        this.$router.push({ path: res.fistMenu.path })
                        loading.close()
                    }).catch(() => {
                        loading.close()
                        // this.refreshCode()
                    })
    handleLogin() {
      this.$refs.loginForm.validate((valid) => {
        if (valid) {
          const loading = this.$loading({
            lock: true,
            text: "登录中,请稍后。。。",
            spinner: "el-icon-loading",
          });
          if (
            this.$route.query.response_type &&
            this.$route.query.client_id &&
            this.$route.query.redirect_uri
          ) {
            this.$store
              .dispatch("loginForm", this.loginForm)
              .then((res) => {
                loading.close();
              })
              .catch(() => {
                loading.close();
                // this.refreshCode()
              });
          } else {
            this.$store
              .dispatch("LoginByUsername", this.loginForm)
              .then((res) => {
                if (res.error) {
                  loading.close();
                  return;
                }
            })
        },
        getTenant () {
            let domain = getTopUrl()
            // 临时指定域名,方便测试
            //domain = "https://bladex.cn";
            info(domain).then(res => {
                const data = res.data
                if (data.success && data.data.tenantId) {
                    this.tenantMode = false
                    this.loginForm.tenantId = data.data.tenantId
                    this.$parent.$refs.login.style.backgroundImage = `url(${data.data.backgroundUrl})`
                if (this.website.switchMode) {
                  const deptId = this.userInfo.dept_id;
                  const roleId = this.userInfo.role_id;
                  if (deptId.includes(",") || roleId.includes(",")) {
                    this.loginForm.deptId = deptId;
                    this.loginForm.roleId = roleId;
                    this.userBox = true;
                    this.$store.dispatch("LogOut").then(() => {
                      loading.close();
                    });
                    return false;
                  }
                }
            })
                this.$router.$avueRouter.formatRoutes(res.menuData, true);
                this.$router.push({ path: "/404" });
                loading.close();
              })
              .catch(() => {
                loading.close();
                // this.refreshCode()
              });
          }
        }
    }
}
      });
    },
    getTenant() {
      let domain = getTopUrl();
      // 临时指定域名,方便测试
      //domain = "https://bladex.cn";
      info(domain).then((res) => {
        const data = res.data;
        if (data.success && data.data.tenantId) {
          this.tenantMode = false;
          this.loginForm.tenantId = data.data.tenantId;
          this.$parent.$refs.login.style.backgroundImage = `url(${data.data.backgroundUrl})`;
        }
      });
    },
  },
};
</script>
<style></style>
src/router/axios.js
@@ -69,23 +69,32 @@
});
//http response 拦截
axios.interceptors.response.use(res => {
  // console.log(res,33)
  //关闭 progress bar
  NProgress.done();
  //获取状态码
  const status = res.data.code || res.status;
  const statusWhiteList = website.statusWhiteList || [];
  const message = res.data.msg || res.data.error_description || '未知错误';
  if(res.config.url=='/api/oauth/form' && res.status == 200){
    window.open(res.request.responseURL,'_self')
    return
  }
  //如果在白名单里则自行catch逻辑处理
  if (statusWhiteList.includes(status)) return Promise.reject(res);
  //如果是401则跳转到登录页面
  if (status === 401) store.dispatch('FedLogOut').then(() => router.push({path: '/login'}));
  // 如果请求为非200否者默认统一处理
  if (status !== 200) {
    // console.log(message,888)
    Message({
      message: message,
      type: 'error'
    });
    return Promise.reject(new Error(message))
    // NProgress.done();
    if(res.config.url=='/api/oauth/token'){
      return Promise.reject(new Error(message))
    }
  }
  return res;
}, error => {
src/store/modules/user.js
@@ -10,6 +10,8 @@
import { deepClone } from "@/util/util";
import website from "@/config/website";
import {
  loginTo,
  loginForm,
  loginByUsername,
  loginBySocial,
  loginBySso,
@@ -71,6 +73,44 @@
    homeFirstTagPage: getStore({ name: "firstTagPage" }) || {},
  },
  actions: {
    //自定义表单登录
    loginTo({ dispatch, commit }, userInfo) {
      return new Promise((resolve) => {
        loginTo(
          userInfo.tenantId,
          userInfo.username,
          md5(userInfo.password),
          userInfo.client_id,
          userInfo.response_type,
          userInfo.redirect_uri
        ).then((res) => {
          // const data = res.data.data;
          // commit("SET_TOKEN", data);
          // commit("DEL_ALL_TAG");
          // commit("CLEAR_LOCK");
          resolve();
        });
      });
    },
    //表单登录
    loginForm({ dispatch, commit }, userInfo) {
      return new Promise((resolve) => {
        loginForm(
          userInfo.tenantId,
          userInfo.username,
          md5(userInfo.password),
          userInfo.client_id,
          userInfo.response_type,
          userInfo.redirect_uri
        ).then((res) => {
          // const data = res.data.data;
          // commit("SET_TOKEN", data);
          // commit("DEL_ALL_TAG");
          // commit("CLEAR_LOCK");
          resolve();
        });
      });
    },
    //根据用户名登录
    LoginByUsername({ dispatch, commit }, userInfo) {
      return new Promise((resolve, reject) => {
vue.config.js
@@ -38,7 +38,7 @@
        proxy: {
            "/api": {
                //本地服务接口地址
                target:"http://192.168.1.50:8110/",
                target:"http://192.168.1.50:8100",
                // target: "https://srgdjczzxtpt.com:2080/api",
                // target: "https://kt39592615.goho.co",
                // target: "http://z4042833u6.wicp.vip",
@@ -52,6 +52,22 @@
                    "^/api": "/",
                },
            },
            "/www.baidu.com": {
                //本地服务接口地址
                target:"https://www.baidu.com",
                // target: "https://srgdjczzxtpt.com:2080/api",
                // target: "https://kt39592615.goho.co",
                // target: "http://z4042833u6.wicp.vip",
                // target: "http://localhost:9528",
                // target: "http://192.168.2.109:9528",
                //远程演示服务地址,可用于直接启动项目
                //target: 'https://saber.bladex.cn/api',
                changeOrigin: true,
                ws: true,
                // pathRewrite: {
                //     "^/api": "/",
                // },
            },
        },
    },
}