新版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: {// PathsassetsSubDirectory: 'static',assetsPublicPath: '/',proxyTable: {},// Various Dev Server settingshost: 'localhost', // can be overwritten by process.env.HOSTport: 9528, // can be overwritten by process.env.PORT, if port is in use, a free one will be determinedautoOpenBrowser: 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/#developmentdevtool: '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#cachebustingcacheBusting: 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.htmlindex: path.resolve(__dirname, '../dist/index.html'),// PathsassetsRoot: 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/#productiondevtool: '#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-pluginproductionGzip: 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 offbundleAnalyzerReport: process.env.npm_config_report},build: {env: require('./prod.env'),// Template for index.htmlindex: path.resolve(__dirname, '../dist/index.html'),// PathsassetsRoot: 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/#productiondevtool: '#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-pluginproductionGzip: 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 offbundleAnalyzerReport: 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_urltimeout: 5000 // 请求超时时间
})// request拦截器
service.interceptors.request.use(config => {if (store.getters.token) {config.headers['token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改}return config
}, error => {// Do something with request errorconsole.log(error) // for debugPromise.reject(error)
})// respone拦截器
service.interceptors.response.use(response => {/*** code为非20000是抛错 可结合自己业务进行修改*/const res = response.dataif (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 debugMessage({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:userparams:{"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,//tokentoken: state => state.user.token,//用户名name: state => state.user.name,//角色roles: state => state.user.roles,//后台返回的权限codeauthorities: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.openedstate.sidebar.withoutAnimation = false},CLOSE_SIDEBAR: (state, withoutAnimation) => {Cookies.set('sidebarStatus', 1)state.sidebar.opened = falsestate.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 = routersstate.routers = constantRouterMap.concat(routers)}},actions: {GenerateRoutes({ commit }, data) {return new Promise(resolve => {const { authorities } = datalet accessedRoutersif (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.userNameconst pwd = user.passwordreturn new Promise((resolve, reject) => {login(user).then(response => {const data = response.datasetToken(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.menusvar permissions = []data.forEach(item=>{permissions.push(item);})debuggercommit('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"routerclass="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 controlVue.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 resetsimport 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 = falsenew 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 {@Autowiredprivate ParamInterceptor paramInterceptor;@Autowiredprivate ParamOneInterceptor paramOneInterceptor;@Autowiredprivate UrlTwoFilter twoFilter;//设置跨域@Beanpublic 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);}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(paramOneInterceptor);registry.addInterceptor(paramInterceptor).addPathPatterns("/**");//registry.addInterceptor(paramOneInterceptor);}@Beanpublic 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登录

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【OpenHarmony 实战开发】 做一个 loading加载动画

本篇文章介绍了如何实现一个简单的 loading 加载动画&#xff0c;并且在文末提供了一个 demo 工程供读者下载学习。作为一个 OpenHarmony 南向开发者&#xff0c;接触北向应用开发并不多。北向开发 ArkUI 老是改来改去&#xff0c;对笔者这样的入门选手来说学习成本其实非常大&…

【每日力扣】98. 验证二叉搜索树 与 108. 将有序数组转换为二叉搜索树

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害 98. 验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&a…

【C++】适配器模式

文章目录 前言 1. 适配器的介绍2. 仿函数2.1 sort函数的模板参数2.2 priority_queue类的模板参数 3. priority_queue模拟实现3. stack & queue 模拟实现3.1 deque的介绍3.2 deque的优点与缺陷3.3 STL标准库中对于stack和queue的模拟实现 前言 C中的适配器是一种设计模式&am…

物联网实战--平台篇之(四)账户后台交互

目录 一、交互逻辑 二、请求验证码 三、帐号注册 四、帐号/验证码登录 五、重置密码 本项目的交流QQ群:701889554 物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html 物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631…

线程安全的概念及原因

1.观察线程不安全 public class ThreadDemo {static class Counter {public int count 0;void increase() {count;}}public static void main(String[] args) throws InterruptedException {final Counter counter new Counter();Thread t1 new Thread(() -> {for (int …

进一步分析并彻底解决 Flink container exit 143 问题

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

AlibabaCloud微服务下的链路追踪系统实战详解

&#x1f680; 作者 &#xff1a;“二当家-小D” &#x1f680; 博主简介&#xff1a;⭐前荔枝FM架构师、阿里资深工程师||曾任职于阿里巴巴担任多个项目负责人&#xff0c;8年开发架构经验&#xff0c;精通java,擅长分布式高并发架构,自动化压力测试&#xff0c;微服务容器化k…

如何利用AI技术提升内容生产的效率和质量

目录 前言1 自动化内容生成1.1 文章生成1.2 视频制作1.3 音频合成 2 内容分发与推广2.1 智能内容推荐2.2 社交媒体管理 3 内容分析与优化3.1 用户反馈分析3.2 内容效果评估 结语 前言 在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;技术对内容生产、分发和优…

MFC实现点击列表头进行排序

MFC实现点击列表头排序 1、添加消息处理函数 在列表窗口右键&#xff0c;类向导。选择 IDC_LIST1&#xff08;我的列表控件的ID&#xff09;&#xff0c;消息选择LVN_COLUMNCLICK。 2、消息映射如下 然后会在 cpp 文件中生成以下函数 void CFLashSearchDlg::OnLvnColumnclic…

C++中的右值引用和移动语义

目录 1 左值引用和右值引用 2 左值引用与右值引用比较 3 右值引用使用场景和意义 4 右值引用引用左值及其一些更深入的使用场景分析 5 完美转发 6.常数右边引用 1 左值引用和右值引用 传统的C语法中就有引用的语法&#xff0c;而C11中新增了的右值引用语法特性&#xff0c…

顶级开源Kubernetes管理工具有哪些?好用Kubernetes工具推荐

Kubernetes已经成为容器编排领域颠覆性的技术&#xff0c;而充满活力的开源社区是其成功背后的推动力。本文将为大家推荐好用的Kubernetes工具&#xff0c;围绕Kubernetes发展的生态系统的广度和深度。 从自动化和监控到网络和安全性&#xff0c;这些工具为管理容器化应用程序…

数据库系统原理实验报告5 | 数据查询

整理自博主本科《数据库系统原理》专业课自己完成的实验报告&#xff0c;以便各位学习数据库系统概论的小伙伴们参考、学习。 专业课本&#xff1a; ———— 本次实验使用到的图形化工具&#xff1a;Heidisql 目录 一、实验目的 二、实验内容 1.找出读者所在城市是“shangh…

最佳实践 | 八爪鱼采集器如何用PartnerShare做全民分销?

在数字化时代&#xff0c;数据采集和分析已经成为企业运营和决策的重要一环。八爪鱼采集器作为一款领先的SaaS产品&#xff0c;凭借其强大的数据采集和处理能力&#xff0c;成为了众多企业和个人用户的心头好。为了进一步拓展市场份额&#xff0c;提升品牌影响力&#xff0c;八…

Web 安全基础理论

Web 安全基础理论 培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 移动应用开发群&#xff1a;548238632 短视频制作群&#xff1a; 744125867极安云…

云动态摘要 2024-05-09

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 [免费试用]即刻畅享自研SaaS产品 腾讯云 2024-04-25 涵盖办公协同、营销拓客、上云安全保障、数据分析处理等多场景 云服务器ECS试用产品续用 阿里云 2024-04-14 云服务器ECS试用产品续用…

springcloud服务间调用 feign 的使用

引入依赖包 <!-- 服务调用feign --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>创建调用外部服务的接口 需要使用的地方注入 使用 启动类增…

华为eNSP小型园区网络配置(上)

→跟着大佬学习的b站直通车← 目标1&#xff1a;dhcp分配ip地址 目标2&#xff1a;内网用户访问www.yzy.com sw1 # vlan batch 10 # interface Ethernet0/0/1port link-type accessport default vlan 10 # interface Ethernet0/0/2port link-type trunkport trunk allow-pass…

经验浅谈!伦敦银如何交易?

近期&#xff0c;伦敦银价格出现很强的上涨&#xff0c;这促使一些新手投资者进入了市场&#xff0c;但由于缺乏经验&#xff0c;他们不知道该怎么在市场中交易&#xff0c;下面我们就从宏观上介绍一些方法&#xff0c;来讨论一下伦敦银如何交易。 首先我们要知道&#xff0c;要…

以目录创建的conda环境添加到jupyter的kernel中

场景&#xff1a;由于某些原因&#xff0c;服务器上的conda环境不能通过--name的方式创建&#xff0c;只能通过指定目录即-p的方式&#xff0c;在这种情况下该环境在conda env list中没有显示&#xff0c;无法在jupyter kernel中搜到&#xff0c;只能手动添加。 1.进入环境 # …

Unity VR在编辑器下开启Quest3透视(PassThrough)功能

现在有个需求是PC端串流在某些特定时候需要开启透视。我研究了两天发现一些坑,记录一下方便查阅,也给没踩坑的朋友一些思路方案。 先说结论,如果要打PC端或者在Unity编辑器中开启,那么OpenXR当前是不行的可能还需要一个长期的过程,必须需要切换到Oculus。当然Unity官方指…