新版security demo(二)前端

写这篇博客,刚好换了台电脑,那就借着这个demo复习下VUE环境的搭建。

一、前端项目搭建

1、安装node

官网下载安装即可。

2、安装脚手架
npm install -g vue-cli

使用脚手架搭建一个demo前端项目

vue init webpack 项目名称
3、安装依赖

这里安装了用到的element、jsonp等。

cnpm  i  element-ui -S
npm install vue-jsonp --save
npm install axios
npm install nprogress --save
npm install js-cookie
 npm install --save vuex

 完整的package.json依赖文件: (

{
  "name": "demo",
  "version": "1.0.0",
  "description": "A Vue.js project",
  "author": "wtyy",
  "private": true,
  "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "build": "node build/build.js"
  },
  "dependencies": {
    "axios": "0.17.1",
    "crypto-js": "^4.0.0",
    "echarts": "^4.9.0",
    "element-ui": "2.3.4",
    "js-cookie": "2.2.0",
    "nprogress": "0.2.0",
    "vue": "2.5.10",
    "vue-bus": "^1.2.1",
    "vue-jsonp": "^0.1.8",
    "vue-router": "3.0.1",
    "vuedraggable": "^2.24.3",
    "vuex": "3.0.1",
    "vuex-persist": "^2.2.0"
  },
  "devDependencies": {
    "autoprefixer": "^7.1.2",
    "babel-core": "^6.22.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.3",
    "babel-loader": "^7.1.1",
    "babel-plugin-syntax-jsx": "^6.18.0",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-plugin-transform-vue-jsx": "^3.5.0",
    "babel-preset-env": "^1.3.2",
    "babel-preset-stage-2": "^6.22.0",
    "chalk": "^2.0.1",
    "copy-webpack-plugin": "^4.0.1",
    "css-loader": "^0.28.0",
    "extract-text-webpack-plugin": "^3.0.0",
    "file-loader": "^1.1.4",
    "friendly-errors-webpack-plugin": "^1.6.1",
    "html-webpack-plugin": "^2.30.1",
    "node-notifier": "^5.1.2",
    "optimize-css-assets-webpack-plugin": "^3.2.0",
    "ora": "^1.2.0",
    "portfinder": "^1.0.13",
    "postcss-import": "^11.0.0",
    "postcss-loader": "^2.0.8",
    "postcss-url": "^7.2.1",
    "rimraf": "^2.6.0",
    "semver": "^5.3.0",
    "shelljs": "^0.7.6",
    "uglifyjs-webpack-plugin": "^1.1.1",
    "url-loader": "^0.5.8",
    "vue-loader": "^13.3.0",
    "vue-style-loader": "^3.0.1",
    "vue-template-compiler": "2.5.10",
    "webpack": "^3.6.0",
    "webpack-bundle-analyzer": "^2.9.0",
    "webpack-dev-server": "^2.9.1",
    "webpack-merge": "^4.1.0"
  },
  "engines": {
    "node": ">= 6.0.0",
    "npm": ">= 3.0.0"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

二、项目代码

1、环境

主要配置后端接口地址、前端端口号

(1)config/dev.env.js

'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  BASE_API: '"http://localhost:2222/securityDemo/"',
})

(2)test.env.js

'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  BASE_API: '"http://localhost:2222/securityDemo/"',
})

(3)prod.env.js

'use strict'
module.exports = {
  NODE_ENV: '"production"',
  BASE_API: '"http://xxx.com/mydemo/"',
}

(4)index

'use strict'
// Template version: 1.2.6
// see http://vuejs-templates.github.io/webpack for documentation.

const path = require('path')

module.exports = {
  dev: {

    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {},

    // Various Dev Server settings
    host: 'localhost', // can be overwritten by process.env.HOST
    port: 9528, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
    autoOpenBrowser: true,
    errorOverlay: true,
    notifyOnErrors: false,
    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-

    // Use Eslint Loader?
    // If true, your code will be linted during bundling and
    // linting errors and warnings will be shown in the console.
    useEslint: true,
    // If true, eslint errors and warnings will also be shown in the error overlay
    // in the browser.
    showEslintErrorsInOverlay: false,

    /**
     * Source Maps
     */
    // https://webpack.js.org/configuration/devtool/#development
    devtool: 'cheap-source-map',

    // If you have problems debugging vue-files in devtools,
    // set this to false - it *may* help
    // https://vue-loader.vuejs.org/en/options.html#cachebusting
    cacheBusting: true,

    // CSS Sourcemaps off by default because relative paths are "buggy"
    // with this option, according to the CSS-Loader README
    // (https://github.com/webpack/css-loader#sourcemaps)
    // In our experience, they generally work as expected,
    // just be aware of this issue when enabling this option.
    cssSourceMap: false,
  },

  test: {
    env: require('./test.env'),
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'),

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',

    /**
     * You can set by youself according to actual condition
     * You will need to set this if you plan to deploy your site under a sub path,
     * for example GitHub pages. If you plan to deploy your site to https://foo.github.io/bar/,
     * then assetsPublicPath should be set to "/bar/".
     * In most cases please use '/' !!!
     */
    assetsPublicPath: 'http://test.xxx.com/mydemo/', // If you are deployed on the root path, please use '/'

    /**
     * Source Maps
     */
    productionSourceMap: false,
    // https://webpack.js.org/configuration/devtool/#production
    devtool: '#source-map',

    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],

    // Run the build command with an extra argument to
    // View the bundle analyzer report after build finishes:
    // `npm run build --report`
    // Set to `true` or `false` to always turn it on or off
    bundleAnalyzerReport: process.env.npm_config_report
  },
  build: {
    env: require('./prod.env'),
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'),

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',

    /**
     * You can set by youself according to actual condition
     * You will need to set this if you plan to deploy your site under a sub path,
     * for example GitHub pages. If you plan to deploy your site to https://foo.github.io/bar/,
     * then assetsPublicPath should be set to "/bar/".
     * In most cases please use '/' !!!
     */
    assetsPublicPath: 'http://xxx.com/mydemo/',

    /**
     * Source Maps
     */
    productionSourceMap: false,
    // https://webpack.js.org/configuration/devtool/#production
    devtool: '#source-map',

    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],

    // Run the build command with an extra argument to
    // View the bundle analyzer report after build finishes:
    // `npm run build --report`
    // Set to `true` or `false` to always turn it on or off
    bundleAnalyzerReport: process.env.npm_config_report
  }
}
2、util

src下新建utils目录,封装工具类

(1)auth.js封装token操作方法
import Cookies from 'js-cookie'

const TokenKey = 'Admin-Token'

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}
(2)request.js封装axois请求
import axios from 'axios'
// 配置前端跨域
axios.defaults.withCredentials = true
import { Message, MessageBox } from 'element-ui'
import store from '../store'
import { getToken } from '@/utils/auth'

// 创建axios实例
const service = axios.create({
  baseURL: process.env.BASE_API, // api的base_url
  timeout: 5000 // 请求超时时间
})

// request拦截器
service.interceptors.request.use(config => {
  if (store.getters.token) {
    config.headers['token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
  }
  return config
}, error => {
  // Do something with request error
  console.log(error) // for debug
  Promise.reject(error)
})

// respone拦截器
service.interceptors.response.use(
  response => {
    /**
     * code为非20000是抛错 可结合自己业务进行修改
     */
    const res = response.data
    if (res.code !== 200 && res.code !=300) {
      Message({
        message: res.message,
        type: 'error',
        duration: 5 * 1000
      })

      // 401 token失效
      if (res.code === 401) {
        // MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
        //   confirmButtonText: '重新登录',
        //   cancelButtonText: '取消',
        //   type: 'warning'
        // }).then(() => {
        //   store.dispatch('FedLogOut').then(() => {
        //     location.reload()// 为了重新实例化vue-router对象 避免bug
        //   })
        // })
        store.dispatch('FedLogOut').then(() => {
          location.reload()// 为了重新实例化vue-router对象 避免bug
        })
      }
      return Promise.reject('error')
    } else {
      return response.data
    }
  },
  error => {
    console.log('err' + error)// for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service
3、api

src下新增api目录,封装后台接口调用

(1)login.js

import request from '@/utils/request'

//获取验证码
export function getCode(){
  return request({
    url: '/code/getCode',
    method: 'get'
  })
}

//登录
export function login(user) {
  return request({
    url: '/login',
    method: 'post',
    datatype:'application/json',
    //data:user
    params:{
      "userName":user.userName,
      "passWord": user.password
    }
  })
}


//获取我的权限列表
export function getMyAuthorities() {
  return request({
    url: '/user/getCurrentUser',
    method: 'post'
  })
}


//退出登录
export function logout() {
  return request({
    url: '/user/logout',
    method: 'get'
  })
}

(2)user.js:

import request from '@/utils/request'

export function getAllUsers() {
  return request({
    url: '/user/getAllUsers',
    method: 'get'
  })
}
4、store

src下新增store目录,store下按照以下示例新建文件

(1)index.js:

import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import permission from './modules/permission'
import user from './modules/user'
import getters from './getters'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    app,
    permission,
    user
  },
  getters
})

export default store

(2)getters.js:

const getters = {
  sidebar: state => state.app.sidebar,
  //token
  token: state => state.user.token,
  //用户名
  name: state => state.user.name,
  //角色
  roles: state => state.user.roles,
  //后台返回的权限code
  authorities:state =>state.user.authorities,
  //动态权限路由
  permission_routers: state => state.permission.routers,
  //固定权限路由
  addRouters: state => state.permission.addRouters
}
export default getters

(3)/mudules/app.js

import Cookies from 'js-cookie'

const app = {
  state: {
    sidebar: {
      opened: !+Cookies.get('sidebarStatus'),
      withoutAnimation: false
    },
    device: 'desktop'
  },
  mutations: {
    TOGGLE_SIDEBAR: state => {
      if (state.sidebar.opened) {
        Cookies.set('sidebarStatus', 1)
      } else {
        Cookies.set('sidebarStatus', 0)
      }
      state.sidebar.opened = !state.sidebar.opened
      state.sidebar.withoutAnimation = false
    },
    CLOSE_SIDEBAR: (state, withoutAnimation) => {
      Cookies.set('sidebarStatus', 1)
      state.sidebar.opened = false
      state.sidebar.withoutAnimation = withoutAnimation
    },
    TOGGLE_DEVICE: (state, device) => {
      state.device = device
    }
  },
  actions: {
    ToggleSideBar: ({ commit }) => {
      commit('TOGGLE_SIDEBAR')
    },
    CloseSideBar({ commit }, { withoutAnimation }) {
      commit('CLOSE_SIDEBAR', withoutAnimation)
    },
    ToggleDevice({ commit }, device) {
      commit('TOGGLE_DEVICE', device)
    }
  }
}

export default app

(4)/mudules/permission.js

import { asyncRouterMap, constantRouterMap } from '@/router/index'

/**
 * 通过meta.authority判断是否与当前用户权限匹配
 * @param authorities
 * @param route
 */
function hasPermission(authorities, route) {
  if (route.meta && route.meta.authority) {
    return authorities.some(authority => route.meta.authority.indexOf(authority) >= 0)
  } else {
    return true
  }
}

/**
 * 递归过滤异步路由表,返回符合用户角色权限的路由表
 * @param asyncRouterMap
 * @param authorities
 */
function filterAsyncRouter(asyncRouterMap, authorities) {
  const accessedRouters = asyncRouterMap.filter(route => {
    if (hasPermission(authorities, route)) {
      if (route.children && route.children.length) {
        route.children = filterAsyncRouter(route.children, authorities)
      }
      return true
    }
    return false
  })
  return accessedRouters
}

const permission = {
  state: {
    routers: constantRouterMap,
    addRouters: []
  },
  mutations: {
    SET_ROUTERS: (state, routers) => {
      state.addRouters = routers
      state.routers = constantRouterMap.concat(routers)
    }
  },
  actions: {
    GenerateRoutes({ commit }, data) {
      return new Promise(resolve => {
        const { authorities } = data
        let accessedRouters
        if (authorities.indexOf('admin') >= 0) {
          accessedRouters = asyncRouterMap
        } else {
          accessedRouters = filterAsyncRouter(asyncRouterMap, authorities)
        }
        commit('SET_ROUTERS', accessedRouters)
        resolve()
      })
    }
  }
}

export default permission

(5)/mudules/user.js

import { login, logout, getMyAuthorities } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'

const user = {
  state: {
    token: getToken(),
    name: '',
    authorities: [],
    roles: []
  },

  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
    },
    SET_NAME: (state, name) => {
      state.name = name
    },
    SET_PERMISSION: (state, authorities) => {
      state.authorities = authorities
    },
    SET_ROLES: (state, roles) => {
      state.roles = roles
    }
  },

  actions: {
    // 登录
    Login({ commit }, user) {
      const userName = user.userName
      const pwd = user.password
      return new Promise((resolve, reject) => {
        login(user).then(response => {
          const data = response.data
          setToken(data)
          commit('SET_TOKEN', data)
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },

    // 获取用户权限信息
    GetInfo({ commit, state }) {
      return new Promise((resolve, reject) => {
        getMyAuthorities().then(response => {
          console.info('res' + response)
          const data = response.data.menus
          var permissions = []
          data.forEach(item=>{
            permissions.push(item);
          })
          debugger
          commit('SET_PERMISSION', permissions)

          resolve(permissions)
        }).catch(error => {
          reject(error)
        })
      })
    },


    // 登出
    LogOut({ commit, state }) {
      return new Promise((resolve, reject) => {
        logout(state.token).then(() => {
          commit('SET_TOKEN', '')
          commit('SET_PERMISSION', [])
          removeToken()
          logout().then(response=>{})
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },

    // 前端 登出
    FedLogOut({ commit }) {
      return new Promise(resolve => {
        commit('SET_TOKEN', '')
        removeToken()
        resolve()
      })
    }
  }
}

export default user
5、router

asyncCodeMenu暂时没有用到,是准备存放各模块及其详情页路由的(避免index写的过长)。这里只贴index.js代码:
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)



export const constantRouterMap = [
 // { path: '/404', component: () => import('@/views/404'), hidden: true },
  { path: '/login', component: () => import('@/views/login/index'), hidden: true },

      // {
      //   path: 'mytest41',
      //   name: 'mytest41',
      //   component: () => import('@/views/mytest/mytest4/mytest41')
      //
      // },
      // {
      //   path: 'mytest42',
      //   name: 'mytest42',
      //   component: () => import('@/views/mytest/mytest4/mytest42')
      //
      // },
      // {
      //   path: 'mytest51',
      //   name: 'mytest51',
      //   component: () => import('@/views/mytest/mytest5/mytest51')
      //
      // },
      // {
      //   path: 'mytest521',
      //   name: 'mytest521',
      //   component: () => import('@/views/mytest/mytest5/mytest521')
      //
      // },
      // {
      //   path: 'mytest522',
      //   name: 'mytest522',
      //   component: () => import('@/views/mytest/mytest5/mytest522')
      //
      // }


  // {
  //   path: '/',
  //   component: Layout,
  //   redirect: '/dashboard',
  //   name: '首页',
  //   icon: '首页',
  //   hidden: true,
  //   children: [{
  //     path: '/dashboard',
  //     component: () => import('@/views/dashboard/index')
  //   }]
  // }
]
/**
 * hidden: true                   if `hidden:true` will not show in the sidebar(default is false)
 * alwaysShow: true               if set true, will always show the root menu, whatever its child routes length
 *                                if not set alwaysShow, only more than one route under the children
 *                                it will becomes nested mode, otherwise not show the root menu
 * redirect: noredirect           if `redirect:noredirect` will no redirct in the breadcrumb
 * name:'router-name'             the name is used by <keep-alive> (must set!!!)
 * meta : {
 title: 'title'               the name show in submenu and breadcrumb (recommend set)
 icon: 'svg-name'             the icon show in the sidebar,
 }
 **/
export const asyncRouterMap = [
  {
    path: '/mytest',
    name: 'mytest',
    hidden:true,
    component: () => import('@/views/mytest/index'),
    children: [
      {
        path: '/main',
        redirect: 'main'
      },
      {
        path: '/mytest/main',
        name: 'main',
        component: () => import('@/views/mytest/main'),
      },

      {
        path: '/mytest/usermanage',
        name: 'userManage',
        component: () => import('@/views/mytest/usermanage'),
      },

      {
        path: '/mytest/rolemanage',
        name: 'rolemanage',
        component: () => import('@/views/mytest/rolemanage'),
        //meta: { title: '角色管理', authority: ['role_manage'] },
      },
      {
        path: '/mytest/menumanage',
        name: 'menumanage',
        component: () => import('@/views/mytest/menumanage'),
        //meta: { title: '角色管理', authority: ['role_manage'] },
      },
      {
        path: '/mytest/schoolmanage',
        name: 'schoolmanage',
        component: () => import('@/views/mytest/schoolmanage'),
        //meta: { title: '角色管理', authority: ['role_manage'] },
      },
  ]}

]

export default new Router({
  // mode: 'history', // 后端支持可开
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRouterMap
})
6、页面

src下新增views目录,存放页面代码

6.1、login/index

<template>
  <el-form label-width="500px" class="demo-ruleForm loginform" align="center">
    <el-form-item label="用户名">
      <el-input v-model="user.userName"></el-input>
    </el-form-item>

    <el-form-item label="密    码" prop="pass">
      <el-input v-model="user.password" type="password" auto-complete="off"></el-input>
    </el-form-item>

<!--    <el-form-item label="验证码" prop="pass">-->
<!--      <el-input v-model = "code" readonly="readonly"></el-input>-->
<!--      <el-input v-model="user.code" type="code" auto-complete="off"></el-input>-->

<!--    </el-form-item>-->

    <el-form-item size="large">
      <el-button type="primary" @click="login()">登录</el-button>
      <el-button>取消</el-button>
    </el-form-item>
  </el-form>
</template>


<script>
import { login,getCode } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
//import { getDAes } from '@/utils/crypto';
export default {

  data() {
    return {
      code:'',
      user: {
        userName: '',
        password: '',
        code:''
      }

    }
  },

  created(){
    //this.getCode()
  },

  methods: {

    getCode(){
      getCode().then(res=>{
        this.code = res.data
      })
    },

    login() {
      //this.user.password = getDAes(this.user.password),
      this.$store.dispatch('Login', this.user).then(() => {

        this.$router.push({ path: '/mytest/main' })
      })
    }
  }
}
</script>


<style>
.loginform {
  float: left;
  margin: auto;
}
</style>
6.2、导航页面
(1)mytest/index菜单导航
<template>
  <div>
    <div>
      <el-row class="tac" style="height:100%">
        <el-col :span="4">
          <el-menu
            :default-active="$route.path"
            router
            class="el-menu-vertical-demo"
          >

            <el-menu-item index="/mytest/main" >
              <i class="el-icon-menu">个人中心</i>
              <router-link to="/mytest/main"></router-link>
            </el-menu-item>


            <el-menu-item index="/mytest/usermanage" v-if="authorities.includes('user_manage')">
              <i class="el-icon-menu">用户管理</i>
              <router-link to="/mytest/usermanage"></router-link>
            </el-menu-item>
            <el-menu-item index="/mytest/rolemanage" v-if="authorities.includes('role_manage')">
              <i class="el-icon-menu">角色管理</i>
              <router-link to="/mytest/mytest2"></router-link>
            </el-menu-item>
            <el-menu-item index="/mytest/menumanage" v-if="authorities.includes('menu_manage')">
              <i class="el-icon-menu">菜单管理</i>
              <router-link to="/mytest/menumanage"></router-link>
            </el-menu-item>

            <el-menu-item index="/mytest/schoolmanage" v-if="authorities.includes('school_manage')">
              <i class="el-icon-menu">学校管理</i>
              <router-link to="/mytest/schoolmanage"></router-link>
            </el-menu-item>
<!--            &lt;!&ndash;二级子菜单&ndash;&gt;-->
<!--            <el-submenu index="1">-->
<!--              <template slot="title">-->
<!--                <i class="el-icon-location"></i>-->
<!--                <span>二级菜单</span>-->
<!--              </template>-->
<!--              <el-menu-item-group>-->
<!--                <el-menu-item index="/mytest/mytest41">-->
<!--                  <i class="el-icon-menu"></i>-->
<!--                  <router-link to="/mytest/mytest41">mytest41</router-link>-->
<!--                </el-menu-item>-->
<!--                <el-menu-item index="/mytest/mytest42">-->
<!--                  <i class="el-icon-menu"></i>-->
<!--                  <router-link to="/mytest/mytest42">mytest42</router-link>-->
<!--                </el-menu-item>-->
<!--              </el-menu-item-group>-->
<!--            </el-submenu>-->
<!--            &lt;!&ndash;三级子菜单&ndash;&gt;-->
<!--            <el-submenu index="2">-->
<!--              <template slot="title">-->
<!--                <i class="el-icon-location"></i>-->
<!--                <span>三级菜单</span>-->
<!--              </template>-->
<!--              <el-menu-item-group>-->
<!--                <el-menu-item index="/mytest/mytest51">-->
<!--                  <i class="el-icon-menu"></i>-->
<!--                  <router-link to="/mytest/mytest51">mytest51</router-link>-->
<!--                </el-menu-item>-->

<!--                &lt;!&ndash;三级&ndash;&gt;-->
<!--                <el-submenu index="3">-->
<!--                  <template slot="title">-->
<!--                    <i class="el-icon-location"></i>-->
<!--                    <span>三级子菜单</span>-->
<!--                  </template>-->
<!--                  <el-menu-item-group>-->
<!--                    <el-menu-item index="/mytest/mytest521">-->
<!--                      <i class="el-icon-menu"></i>-->
<!--                      <router-link to="/mytest/mytest521">mytest521</router-link>-->
<!--                    </el-menu-item>-->
<!--                    <el-menu-item index="/mytest/mytest522">-->
<!--                      <i class="el-icon-menu"></i>-->
<!--                      <router-link to="/mytest/mytest522">mytest522</router-link>-->
<!--                    </el-menu-item>-->
<!--                  </el-menu-item-group>-->
<!--                </el-submenu>-->
<!--              </el-menu-item-group>-->
<!--            </el-submenu>-->
          </el-menu>
        </el-col>
        <el-col span="20">
          <!--主体内容部分-->
          <div class="main">
            <router-view></router-view>
          </div>
        </el-col>
      </el-row>
    </div>
  </div>
</template>
<script>
export default {
  data(){
    return {

    }
  },
  computed: {
    authorities() {
      return this.$store.state.user.authorities
    }
  }
}
</script>
(2)mytest/main首页
<template>
  <div>this is main</div>
</template>
(3)/mytest/usermanage
<template>
  <div>this is user manage</div>
</template>
(4)/mytest/menumanage
<template>
  <div>this is menu manage</div>
</template>

其他两个同上

7、权限拦截permission.js
import router from './router'
import store from './store'
import NProgress from 'nprogress' // Progress 进度条
import 'nprogress/nprogress.css'// Progress 进度条样式
import { Message } from 'element-ui'
import { getToken } from '@/utils/auth' // 验权

const whiteList = ['/login'] // 不重定向白名单
router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
    if (to.path === '/login') {
      next({ path: '/' })
      NProgress.done() // if current page is dashboard will not trigger	afterEach hook, so manually handle it
    } else {
      if (store.getters.authorities.length === 0) {
        // 拉取用户权限信息
        store.dispatch('GetInfo').then(res => {
          // 从后端获取的权限
          const authorities = res


          // 前端路由加载动态权限
          store.dispatch('GenerateRoutes', { authorities }).then(() => { // 生成可访问的路由表
            // alert('store.getters.addRouters' + store.getters.addRouters.length)
            router.addRoutes(store.getters.addRouters)// 添加动态路由

            next({ ...to })// hack方法 确保addRoutes已完成
          })
        }).catch((err) => {
          store.dispatch('FedLogOut').then(() => {
            Message.error(err || 'Verification failed, please login again')
            next({ path: '/' })
          })
        })
      } else {
        next()
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next('/login')
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  NProgress.done() // 结束Progress
})
8、项目入口

(1)main.js

/*

import Vue from 'vue'
import App from './App'
import router from './router'

//引入依赖
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import { VueJsonp }  from 'vue-jsonp'
//加载自定义文件
import '@/permission' // permission control


Vue.config.productionTip = false

//加载引入的依赖
Vue.use(VueJsonp)
Vue.use(ElementUI)

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})
*/
import Vue from 'vue'

//import 'normalize.css/normalize.css'// A modern alternative to CSS resets

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n

//import '@/styles/index.scss' // global css
import App from './App'
import router from './router'
import store from './store'
//import '@/icons' // icon
import '@/permission' // permission control
import VueJsonp from 'vue-jsonp'

Vue.use(VueJsonp)

Vue.use(ElementUI, { locale })

Vue.config.productionTip = false

new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})

(2)App.vue

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

三、测试

1、登录

输入admin/123,f12查看控制台报错

Access to XMLHttpRequest at 'http://localhost:2222/securityDemo/login' from origin 'http://localhost:9528' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

后端加上跨域配置即可

package com.demo.security.config;

import com.demo.security.filter.UrlTwoFilter;
import com.demo.security.interceptor.ParamInterceptor;
import com.demo.security.interceptor.ParamOneInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private ParamInterceptor paramInterceptor;

    @Autowired
    private ParamOneInterceptor paramOneInterceptor;


    @Autowired
    private UrlTwoFilter twoFilter;

    //设置跨域
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://localhost:9528");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(paramOneInterceptor);
        registry.addInterceptor(paramInterceptor).addPathPatterns("/**");
        //registry.addInterceptor(paramOneInterceptor);
    }

    @Bean
    public FilterRegistrationBean<UrlTwoFilter> getIpFilter() {
        FilterRegistrationBean<UrlTwoFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(twoFilter);
        registrationBean.setEnabled(true);
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
        registrationBean.setUrlPatterns(List.of("/*"));
        return registrationBean;
    }
}

重启后端再次访问即可成功登录。

2、菜单权限
(1)admin登录:

(2)zs/123登录

(3)ls/123登录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/590629.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV(三)—— 车牌筛选

本篇文章要介绍如何对从候选车牌中选出最终进行字符识别的车牌。 无论是通过 Sobel 还是 HSV 计算出的候选车牌都可能不止一个&#xff0c;需要对它们进行评分&#xff0c;选出最终要进行识别的车牌。这个过程中会用到两个理论知识&#xff1a;支持向量机和 HOG 特征。 1、支…

vivado Aurora 8B/10B IP核(9)- CRC、 Aurora 8B/10B内核的时钟接口端口

CRC 模块提供 16 位或 32 位 CRC&#xff0c;用于用户数据。 Aurora 8B/10B 内核的时钟接口端口 从相邻收发器四边形的时钟Xilinx 实现工具可以根据需要对南北路由和引脚交换到收发器时钟输入进行必要的调整&#xff0c;以将时钟从一个四线到另一个。 重要信息&#xff1a;共…

25计算机考研院校数据分析 | 哈尔滨工业大学

哈尔滨工业大学&#xff08;Harbin Institute of Technology&#xff09;&#xff0c;简称哈工大&#xff0c; 校本部位于黑龙江省哈尔滨市&#xff0c;是由工业和信息化部直属的全国重点大学&#xff0c;位列国家“双一流”、“985工程”、“211工程”&#xff0c;九校联盟 、…

【Java EE】Mybatis之XML详解

文章目录 &#x1f38d;配置数据库连接和MyBatis&#x1f340;写持久层代码&#x1f338;添加mapper接口&#x1f338;添加UserInfoXMLMapper.xml&#x1f338;单元测试 &#x1f332;CRUD&#x1f338;增(Insert)&#x1f338;删(Delete)&#x1f338;改(Update)&#x1f338;…

【MIT6.S081】Lab6: Copy-on-Write Fork for xv6(详细解答版)

实验内容网址&#xff1a;https://xv6.dgs.zone/labs/requirements/lab6.html 本实验的代码分支&#xff1a;https://gitee.com/dragonlalala/xv6-labs-2020/tree// Implement copy-on write 关键点: 内存引用计数、usertrap()、页表 思路: Copy on write 是为了优化在fork()时…

Benewake(北醒) 短距 TF-Luna 8m

北醒TF-Luna激光雷达在测距方面具有以下特点: 高精度测距:北醒TF-Luna激光雷达采用激光束对目标进行扫描和测量,可以获取目标的高精度位置信息。其工作原理保证了测距的稳定性和准确性。 小视场角:该雷达具有较小的视场角(FOV),这意味着它可以更精确地锁定和测量特定区域…

Go协程的底层原理(图文详解)

为什么要有协程 什么是进程 操作系统“程序”的最小单位进程用来占用内存空间进程相当于厂房&#xff0c;占用工厂空间 什么是线程 进程如果比作厂房&#xff0c;线程就是厂房里面的生产线&#xff1a; 每个进程可以有多个线程线程使用系统分配给进程的内存&#xff0c;线…

深入理解网络原理1

文章目录 前言一、网络初识1.1 IP地址1.2 端口号1.3 协议1.4 五元组1.5 协议分层 二、TCP/IP五层协议三、封装和分用四、客户端vs服务端4.1 交互模式4.2 常见的客户端服务端模型4.3 TCP和UDP差别 前言 随着时代的发展&#xff0c;越来越需要计算机之间互相通信&#xff0c;共享…

网络安全审计

一、什么叫网络安全审计 网络安全审计是按照一定的安全策略&#xff0c;利用记录、系统活动和用户活动等信息&#xff0c;检查、审查和检验操作时间的环境及活动&#xff0c;从而发现系统漏洞、入侵行为或改善系统性能的过程&#xff0c;它是提高系统安全性的重要手段。 系统…

linux下载压缩包

比如我要下载的压缩包地址为&#xff1a; http://calvin.inf.ed.ac.uk/wp-content/uploads/data/cocostuffdataset/cocostuff-10k-v1.1.zip 1.创建文件夹并切换到这个目录下 2.用wget获取压缩包 压缩包下好了 3.解压 如果是 tar.gz包解压 tar -zxvf 也可以解压到具体的目录…

Java的逻辑控制和方法的使用介绍

前言 程序的逻辑结构一共有三种&#xff1a;顺序结构、分支结构和循环结构。顺序结构就是按代码的顺序来执行相应的指令。这里主要讲述Java的分支结构和循环结构&#xff0c;由于和C语言是有相似性的&#xff0c;所以这里只会提及不同点和注意要点~~ 注意在C语言中&#xff0c;…

Docker部署nginx并启用https加密连接

前言 在当今互联网时代&#xff0c;安全性成为越来越重要的议题。随着网络攻击日益猖獗&#xff0c;保护数据和通信的安全性变得至关重要。在这种背景下&#xff0c;对于在 Docker 中运行 Nginx 是否需要使用 HTTPS 这一问题&#xff0c;我们需要考虑到网络安全的重要性以及 H…

如何低成本创建个人网站?

目录 前言 网站源代码 虚拟主机或服务器 域名注册或免费二级域名 域名解析 上传源代码压缩包 添加刚刚的域名 成功搭建 失败的解决方案 结语 前言 很多小白都非常想拥有自己的网站&#xff0c;但很多人虽然有了自己的源代码但苦于不知道怎么将其变成所有人都能够访…

YOLO自研模块:多尺度轻量化卷积模块

目录 一、原理 二、代码 三、配置文件 一、原理 不同大小的卷积核,提取目标特征的特征尺度不同,所以通过使用不同大小卷积核的卷积来提取特征就可以保证获取到目标的多尺度特征。 借鉴YOLOv8中,将通道数进行划分的操作,在卷积的输入过程中为了减小参数量,将输入通道数…

【题解】NC109 岛屿数量(BFS / DFS)

https://www.nowcoder.com/practice/0c9664d1554e466aa107d899418e814e?tpId196&tqId37167&ru/exam/oj dfs #include <vector> class Solution { public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可…

堆排序以及TOP-K问题

片头 嗨&#xff01;小伙伴们&#xff0c;大家好&#xff01;今天我们来深入理解堆这种数据结构&#xff0c;分析一下堆排序以及TOP-K问题&#xff0c;准备好了吗&#xff1f;我要开始咯&#xff01; 一、堆排序 这里我们先假设要排成升序&#xff0c;也就是从左到右&#xf…

变量内存和存储单位

基本数据类型及其占位符 存储单位 内存中的数据存储单元是由一个一个的二进制组成的&#xff0c;每个二进制只能存储0 和1 科学家为了更加方便存储更多的数据&#xff0c;把内存中8个二进制分为一组&#xff0c;叫做一个字节&#xff0c;Byte字节是最小的存储单位。(重点⭐⭐⭐…

数据结构与算法-双向链表

1.简介 在使用带头节点的单向链表查找时查找的方向只能是一个方向&#xff0c;而双向链表可以向前或向后查找。例如单向链表只能查到一个节点的下一个节点&#xff0c;但是双向链表不仅可以查到下一个节点&#xff0c;还可以查到上一个节点。 在删除节点时&#xff0c;单向链…

C语言 | Leetcode C语言题解之第66题加一

题目&#xff1a; 题解&#xff1a; /*** Note: The returned array must be malloced, assume caller calls free().*/ int* plusOne(int* digits, int digitsSize, int* returnSize){for(int i digitsSize - 1; i > 0; --i){digits[i] digits[i] 1;//最后元素1判断是不…

怎么用CAPL与Python交互

怎么用CAPL与其他应用程序交互 怎么用CAPL与Python交互 怎么用CAPL与Python交互 怎么用CAPL与其他应用程序交互前言1、CAPL怎么调Python&#xff1f;1.1CAPL调Python的命令1.2CAPL调用Python实例 2、怎么把python运行的结果返回给CAPL2.1通过环境变量 3、CAPL调Python的输入参…