vue sso 单点登录

sso单点登录有很多种实现方式,我这只是采用一种最简单的实现方式
vue2 + 采用mock.js来模拟后台接口验证

  1. 实现的思路
    sso-serve 一个独立的登录系统 http://localhost:8090/
    sso_client 子级系统 ,所有子系统的逻辑大概相同
  • 子系统通过截取浏览器的url 地址来判断当前系统是否登录

  • 如果未登录则跳转到sso登录界面并会带上当前系统的url地址

  • sso登录界面验证账号密码,通过后会根据之前带过来的子系统url地址跳转,并带回参数st,st=后面的值在子系统在调用后台接口去验证,验证通过则会在浏览器set一个Cookies,后面打开子系统的话会根据这个Cookies来验证登录状态

  1. sso界面获取当前的url地址 做逻辑判断
    sso 登录页代码
<template>
  <div class="container login" v-if="isMounted">
    <div v-if="needLogin">
      <el-form :model="loginFormData">
        <el-form-item label="用户名" required>
          <el-input v-model="loginFormData.username"></el-input>
        </el-form-item>
        <el-form-item label="密码" required>
          <el-input v-model="loginFormData.password"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button @click="hanldLoginInfo">登录</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
import { login, loginInfo } from '@/api/sso.js'
import axios from 'axios'
export default {
  name: 'App',
  data() {
    return {
      isMounted: false,
      needLogin: true,
      loginFormData: {
        username: '',
        password: '',
      },
      https: '',
    }
  },

  mounted() {
        let hash = window.location.href
    if (hash == '' || hash.indexOf('?') == -1) {
      // url 没有带参数则不执行任何操作,显示表单填写账号信息
      this.isMounted = true
    } else {
      // 携带参数的话则截取 ?号后面的参数
      let str = hash.split('?')[1]
      this.https = str.split('=')[1] // 可直接回去 =号后面
      // 解析参数乱码问题
      this.https = decodeURIComponent(this.https)
      //  调用 /sso/info 验证cookie
      this.getinfo()
    }

  },
  methods: {
  
    hanldLoginInfo() {
      //点击登录,调用接口,去验证账号密码,通过之后返回 st,
      axios.post('/sso/login', this.loginFormData).then((infos) => {
        if (infos.data.code == 0) {
          let st = infos.data.data.st
          // url 没有参数的时候填写表单默认一个子系统地址,赋值this.https
          if(this.https == ''){
            this.https = 'http://localhost:8080/'
          }
          if (st !== '' && this.https !== '') {
            window.location.href = `${this.https}?&st=${st}`
          }
          // 验证cookie 通过则跳转到对应的项目地址
        } else {
          // code: 10403 未通过则显示表单输入账号密码
          console.log(infos, 'infos异常')
        }
      })
    },

   

    getinfo() {
      axios.post('/sso/info').then((res) => {
        if (res.data.code == 0) {
          console.log(res, '/sso/info验证成功')
          // 验证cookie 通过则跳转到对应的项目地址
          // if(this.https == ''){
          //   this.https = 'http://localhost:8080/'
          // }
          let ist = res.data.data.st
          window.location.href = `${this.https}?&st=${ist}`
        } else {
          // code: 10403 未通过则显示表单输入账号密码 返回
          console.log(res, 'infos异常')
          this.isMounted = true
        }
      })
    },
  },
}
</script>
<style lang="scss" scoped>
.container {
  height: 1000px;
  width: 800px;
  margin: auto;
  background: pink;
}
.login {
  font-size: 30px;
  text-align: center;
  line-height: 800px;
}
</style>


sso的mock.js

const bodyParser = require('body-parser')
const jsonParser = bodyParser.json()
const cookieParser = require('cookie-parser')

module.exports = function (app) {
  app.use(jsonParser)
  app.use(cookieParser())
  app.post('/client/serviceValidate', function(req, res) {
    if(req.body.st == '1234567890') {
      res.cookie("S1ID",'1234567890',{maxAge: 900000, httpOnly: true});
      res.send({
        "code": 0,
        "msg": "验证成功",
        "data": {
            "id": null,
            "userName": "小明",
            "phone": "13123553333",
            "certificateCode": "2637482368437562847365",
            "st": null,
            "removeTag": null,
            "createTime": null,
            "createUser": null,
            "lastModifyTime": null,
            "lastOperator": null
        }
      })
    } else {
      res.send({
        code: 10403,
        msg: '未登录',
        data: null
      })
    }
  })

  

}

sso_client 子系统代码

<template>
  <div id="app">
    <div v-if="isLogin">ssoLOgin===未登录</div>
    <div v-else>已登录===</div>
  </div>
  
</template>

<script>
import { serviceValidate, login, logout } from './sso.js'
import { getQuery } from './url.js'
import { gotoSsoserver } from './tools.js'
import axios from 'axios'
export default {
  name: 'App',
  data() {
    return {
      isLogin: true,
      content: '',
    }
  },
  mounted() {
    // 接收传递过来的st 参数
    let str = sessionStorage.getItem('st')
    console.log(str,'=====str');
    if (str != '' || str != null) {
      this.ssologins(str)
    }
  },

  methods: {
    async init() {
      const query = getQuery(location.href)
      if (query.st !== undefined) {
        serviceValidate(query.st).then((data) => {
          this.isLogin = true
          this.content = `用户:${data.data.userName},电话:${data.data.phone}`
          // 把地址栏中的st清除掉
          history.replaceState({}, '', location.origin)
        })
      } else {
        try {
          let data = await login()
          this.isLogin = true
          this.content = `用户:${data.data.userName},电话:${data.data.phone}`
          this.$message('您已登录')
          return
        } catch (e) {
          console.log(e)
          if (e.code) {
            this.$confirm('您未登录,是否跳转到SSO登录页面?', '提示')
              .then(() => {
                gotoSsoserver(location.href)
              })
              .catch(() => {})
          }
        }
      }
    },
    async logout() {
      try {
        await logout()
        this.$message('您已登出')
        setTimeout(() => {
          window.location.href = ''
        }, 2000)
      } catch (e) {
        this.$message('登出接口出错')
      }
    },
    async ssologins(val) {
      let st = val
      if (st == '' || null) return false
      const result = await axios.post('/client/serviceValidate', {
        st: st,
      })
      console.log(result)
      if (result.data.code == '10403') {
        // 登录失败跳转到sso登录页面
        // ssotoken.initsso()
        this.isLogin = true
        let hash = window.location.href
        setTimeout(() => {
          // 跳转的固定登录地址
          window.location.href = `http://localhost:8090/?st=` + encodeURIComponent(hash)
        }, 2000)
      } else if (result.data.code == 0) {
        console.log(result, '===result登录成功')
        this.isLogin = false
      }
    },
    getURl() {
      let hash = window.location.href
      if (hash == '' || hash.indexOf('?') == -1) {
        /**
         * 没有 ?号则没有传递过来的st,要跳转到sso登录去登录,同时还要带上当前的url地址
         */
        let redirectUrl = window.location.href
        console.log(redirectUrl, '没有url')
        if (redirectUrl !== '') {
          window.location.href =
            `http://localhost:8090/=` + encodeURIComponent(redirectUrl)
          // window.location.href =
          //   `http://192.168.1.4:8989/upt/sr/sys/admin/login.html?redirectUrl=` +
          //   encodeURIComponent(redirectUrl)
        }
      } else {
        let str = hash.split('?')[1]
        let st = str.split('=')[1]
        // 拿到 传递过来的st 存起来,到验证接口去验证st是否有效
        // sessionStorage.setItem('st', str.split('=')[1])
        this.ssologins(st)
      }
    },
  },
}
</script>
<style lang="scss" scoped>
.container {
  height: 1000px;
  width: 800px;
  margin: auto;
  background: grey;
}
.login {
  font-size: 30px;
  text-align: center;
  line-height: 800px;
}
</style>

sso_client 子系统的mock.js代码

const bodyParser = require('body-parser')
const jsonParser = bodyParser.json()
const cookieParser = require('cookie-parser')

module.exports = function (app) {
  app.use(jsonParser)
  app.use(cookieParser())
  app.post('/client/serviceValidate', function(req, res) {
    if(req.body.st == '1234567890') {
      res.cookie("S1ID",'1234567890',{maxAge: 900000, httpOnly: true});
      res.send({
        "code": 0,
        "msg": "验证成功",
        "data": {
            "id": null,
            "userName": "小明",
            "phone": "13123553333",
            "certificateCode": "2637482368437562847365",
            "st": null,
            "removeTag": null,
            "createTime": null,
            "createUser": null,
            "lastModifyTime": null,
            "lastOperator": null
        }
      })
    } else {
      res.send({
        code: 10403,
        msg: '未登录',
        data: null
      })
    }
  })
}
```javascript


在 sso_client 的mian.js 或者登录页引入 stoken.js 代码进行逻辑判断

import {
  Message
} from 'element-ui'
let hash = window.location.href
console.log(hash,'hash')
let st = ''
class ssotoken {
  constructor() {
    this.GetUrl()
    this.initsso()
  }
  initsso() {
    if (hash == '' || hash.indexOf('&st') == -1) {
      /**
       * 没有 &so字符 则没有传递过来的st,要跳转到sso登录页去登录,同时还要带上当前的url地址
       */
      let redirectUrl = window.location.href
      if (redirectUrl !== '') {
        Message.warning('账号未登录,正在跳转到sso登录页')
        setTimeout(() => {
           window.location.href = `http://localhost:8090/?st=` + encodeURIComponent(redirectUrl)
        }, 2000);
      }
    } else {
    
      // 拿到 传递过来的st 存起来,到验证接口去验证st是否有效
      console.log(this.GetUrl())
      st = this.GetUrl().st
      sessionStorage.setItem('st', st) // 这里为了方便直接使用本地存储
      // this.store.commit("TOOGLESIDEBAR");
    }
  }
  GetUrl() {
    let url = window.location.search
    let urlst = new Object()
    if (url.indexOf('?') != -1) {
      let str = url.substr(1)
      if (str.indexOf('&st') != -1) {
        let st = str.split('&st')
        for (let index = 0; index < st.length; index++) {
          // urlst[st[index].split("=")[0]] = unescape(st[index].split("=")[1])
          urlst = {st:unescape(st[index].split("=")[1])}
        }
      }
    }
    return urlst
  }
}
module.exports = new ssotoken
Logo

前往低代码交流专区

更多推荐