新版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登录

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w_t_y_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值