Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 精品在线99,亚洲欧美另类在线,欧美日韩综合精品一区二区三区

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          在SSM框架使用攔截器實現用戶登錄權限驗證

          在SSM框架使用攔截器實現用戶登錄權限驗證

          配置攔截器(不攔截登錄、注冊請求),對所有請求進行攔截,驗證session中保存的登錄用戶是否存在,如果不存在,說明用戶已經下線,清空session,強制返回登錄頁面。

          配置攔截器

          在SpringMVC配置文件中配置攔截器,對文件上傳、用戶登錄、注冊不作攔截。

          <!-- 配置攔截器 -->
          	<mvc:interceptors>
          		<!-- 使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截所有的請求 -->
          		<!-- session失效攔截器 -->
          		<mvc:interceptor>
          			<!-- 匹配的是url路徑, 如果不配置或/**,將攔截所有的Controller請求 -->
          			<mvc:mapping path="/**" />
          			<!-- 不需要攔截的請求地址 -->
          			<mvc:exclude-mapping path="/file/**.html" /><!-- 文件上傳請求 -->
          			<mvc:exclude-mapping path="/login.html" /><!-- 用戶登錄的請求 -->
          			<bean class="lxkj.zz07.interceptor.MyLoginInterceptor"></bean>
          		</mvc:interceptor>
          	</mvc:interceptors>
          12345678910111213

          定義攔截器類

          通過實現HandleInterceptor接口,來定義用戶攔截器類,重寫preHandle方法,該方法將在Controller處理之前進行調用,SpringMVC中的Interceptor攔截器是鏈式的,可以同時存在 多個Interceptor,然后SpringMVC會根據聲明的前后順序一個接一個的執行,而且所有的Interceptor中的preHandle方法都會在 Controller方法調用之前調用。
          SpringMVC的這種Interceptor鏈式結構也是可以進行中斷的,這種中斷方式是令preHandle的返 回值為false,當preHandle的返回值為false的時候整個請求就結束了。

          eb應用安全管理

          Web應用的安全管理,主要包括兩個方面的內容,一個是用戶身份的認證,即用戶登錄的設計,二是用戶授權,即一個用戶在一個應用系統中能夠執行哪些操作的權限管理。權限管理的設計一般使用角色來管理,即給一個用戶賦予哪些角色,這個用戶就具有哪些權限。

          Spring框架體系中,經典的安全體系框架是Security。關于系統的安全管理及各種設計,Spring Security已經大體上都實現了,只需要一些配置和引用就能夠正常使用。SpringBoot使用Security更加的簡單,因為SpringBoot本身的簡單配置使用加上Security的功能豐富全面,可用快速幫助我們構建完善的登陸認證服務。

          關于Security,SpringBoot本身有spring-boot-starter-security依賴組件,Spring Cloud微服務全家桶中也有spring-cloud-starter-security依賴組件,并且spring-cloud-starter-security中也包含了spring-boot-starter-security,下面的學習中,會先使spring-boot-starter-security,然后再spring-cloud-starter-security學習安全管理的功能,從SpringBoot單體的登陸注冊和權限管理,到Spring Cloud微服務中構建認證和授權服務,都會一一接觸到。

          關于版本的問題,我從SpringBoot1.3.x版的使用到2.1.x的使用,Security的配置也經歷了不小的變化,最準確的配置建議去官網文檔學習。下面的學習中,將使用2.1.5版本,官方文檔地址是: https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/htmlsingle/ 。 Security的源碼非常復雜,因此我們后面再討論深層次的東西,現在來用實例進行入門學習。

          入門例子

          先來看一個入門例子,springboot項目結構我們都很熟悉,先來看依賴:

          依賴很簡單,除了一個web組件和thymeleaf視圖組件,就是一個security。接下來看一下啟動類:

          可以看到啟動類沒有任何特殊的配置。至于配置文件,我們簡單的配置一下端口,其它不做任何配置:

          這樣一個簡單的入門例子就完成了,現在來啟動項目,啟動日志很短,可以看到有一行特殊的日志:

          這是我們加入了security組件的依賴之后,引入了security的默認配置,此時就有了一個簡單的登錄功能,打印出的一行是默認密碼的信息,這個密碼是現在沒有任何代碼和配置的狀態下每次啟動隨機生成的,security不僅會生成一個默認密碼,依賴組件中還有一個默認的登陸鏈接/login,還有一個默認的用戶名 user,而且在springboot2.1.x版本中,這個/login有一個非常不錯的默認登錄頁面,下面進行測試:

          用戶名輸入user,密碼輸入日志中打印出的隨機密碼,登錄成功后,就會跳轉到默認地址,默認成功的地址就是登錄地址去掉/login,

          現在沒有定義任何鏈接匹配這個地址,我們來定義一個簡單的頁面,在resource下面,新建一個templates文件夾,在templates下面新建一個主頁 home.html,內容如下:

          然后定義一個controller跳轉到這個頁面:

          這樣我們登陸成功后,就能自動跳轉到這個頁面:

          這樣,一個最簡單的登錄流程就完成了,我們幾乎沒有做任何配置,只是引入了一個依賴而已。下面我們給security配置一個默認用戶名密碼,這樣就不用每次啟動都用隨機密碼,直接在springboot的默認配置文件中配置:

          這樣等登陸就可以使用 admin/admin登陸了。

          代碼地址 :https://gitee.com/blueses/spring-boot-security 01

          本概念

          權限控制,最常見的基本上有 2 種

          • 基于 ACL 的權限控制
          • 基于 RBAC 的權限控制

          這個兩種到底有什么不同呢?

          我們通過下圖來分析一下

          添加圖片注釋,不超過 140 字(可選)

          ACL 是基于 用戶 -> 權限,直接為每個用戶分配權限 RBAC 基于 用戶 -> 角色 -> 權限,以角色為媒介,來為每個用戶分配權限 這樣做的好處是,某個權限過于敏感時,想要將每個用戶或者部分用戶的權限去掉,就不需要每個用戶的權限都操作一遍,只需要刪除對應角色的權限即可 那在實際的開發中 RBAC 是最常用的權限控制方案,就前端而言,RBAC 主要如何實現的呢? 主要就兩個部分

          • 頁面權限受控
          • 按鈕權限受控

          下面我們就來實現這兩個部分

          • 頁面權限
          • 按鈕權限

          頁面的訪問,我們都是需要配置路由表的,根據配置路由表的路徑來訪問頁面 那么,我們控制了路由表,不就能控制頁面的訪問了嗎? 實現思路

          • 前端根據不同用戶信息,從后端獲取該用戶所擁有權限的路由表
          • 前端動態創建路由表

          基本環境

          創建項目

           npm install -g @vue/cli
           vue --version # @vue/cli 5.0.8
           vue create vue-router-dome

          添加圖片注釋,不超過 140 字(可選)

          打開項目,npm run serve運行一下

          添加圖片注釋,不超過 140 字(可選)

          代碼初始化,刪除不必要的一些文件

          添加圖片注釋,不超過 140 字(可選)

          我們創建幾個新文件夾

          添加圖片注釋,不超過 140 字(可選)

          寫下基本的頁面

          添加圖片注釋,不超過 140 字(可選)

          <!-- home.vue -->
          <template>
            <div>主頁</div>
          </template>


          <!-- menu.vue -->
           <template>
             <div>菜單管理</div>
           </template>


           <!-- user.vue -->
           <template>
             <div>用戶管理</div>
           </template>

          寫下路由配置

          添加圖片注釋,不超過 140 字(可選)

           // remaining.ts
           import Layout from '@/layout/index.vue'
           
           const remainingRouter: AppRouteRecordRaw[]=[
             {
               path: '/remaining',
               component: Layout,
               redirect: 'home',
               children: [
                 {
                   path: '/remaining/home',
                   component: ()=> import('@/views/home.vue'),
                   name: '首頁',
                   meta: {},
                 }
               ],
               name: '主頁管理',
               meta: undefined
             },
           ]
           
           export default remainingRouter

          remaining 主要為了存放一些公共路由,沒有權限頁可以訪問,比如登錄頁、404頁面這些

          因為是用 typescript 編寫的,我們需要加一下聲明文件,定義下 remainingRouter 的類型

          添加圖片注釋,不超過 140 字(可選)

           // router.d.ts
           import type { RouteRecordRaw } from 'vue-router'
           import { defineComponent } from 'vue'
           
           declare module 'vue-router' {
             interface RouteMeta extends Record<string | number | symbol, unknown> {
               hidden?: boolean
               alwaysShow?: boolean
               title?: string
               icon?: string
               noCache?: boolean
               breadcrumb?: boolean
               affix?: boolean
               activeMenu?: string
               noTagsView?: boolean
               followAuth?: string
               canTo?: boolean
             }
           }
           
           type Component<T=any>=| ReturnType<typeof defineComponent>
             | (()=> Promise<typeof import('*.vue')>)
             | (()=> Promise<T>)
           
           declare global {
             interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
               name: string
               meta: RouteMeta
               component?: Component | string
               children?: AppRouteRecordRaw[]
               props?: Recordable
               fullPath?: string
               keepAlive?: boolean
             }
           
             interface AppCustomRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
               icon: any
               name: string
               meta: RouteMeta
               component: string
               componentName?: string
               path: string
               redirect: string
               children?: AppCustomRouteRecordRaw[]
               keepAlive?: boolean
               visible?: boolean
               parentId?: number
               alwaysShow?: boolean
             }
           }

          接下來編寫,創建路由、導出路由

           import type { App } from 'vue'
           import type { RouteRecordRaw } from 'vue-router'
           import { createRouter, createWebHashHistory } from 'vue-router'
           import remainingRouter from './modules/remaining'
           
           // 創建路由實例
           const router=createRouter({
             history: createWebHashHistory(), // createWebHashHistory URL帶#,createWebHistory URL不帶#
             strict: true,
             routes: remainingRouter as RouteRecordRaw[],
             scrollBehavior: ()=> ({ left: 0, top: 0 })
           })
           
           // 導出路由實例
           export const setupRouter=(app: App<Element>)=> {
             app.use(router)
           }
           
           export default router

          main.ts中導入下

           import { createApp } from 'vue'
           import App from './App.vue'
           import { setupRouter } from './router/index' // 路由
           import ElementPlus from 'element-plus'
           import 'element-plus/dist/index.css'
           
           // 創建實例
           const setupAll=async ()=> {
             const app=createApp(App)
             setupRouter(app)
             app.mount('#app')
           }
           
           setupAll()

          接下來寫下 Layout 架構

          我們要實現的效果,是一個后臺管理頁面的側邊欄,點擊菜單右邊就能跳轉到對應路由所在頁面

          添加圖片注釋,不超過 140 字(可選)

          創建

          AppMain.vue 右邊路由跳轉頁

          Sidebar.vue 側邊欄

          index.vue 作為 layout 架構的統一出口

          添加圖片注釋,不超過 140 字(可選)

           <!--
           @description: AppMain
           -->
           
           <template>
             <div>
               <router-view v-slot="{ Component, route }">
                 <transition name="fade-transform" mode="out-in"> <!-- 設置過渡動畫 -->
                   <keep-alive>
                     <component :is="Component" :key="route.fullPath" />
                   </keep-alive>
                 </transition>
               </router-view>
             </div>
           </template>

          上面是一種動態路由的固定寫法,需要與的路由配置進行對應 其中最主要的就是 <component :is="Component" :key="route.fullPath" /> 中的 key,這是為確定路由跳轉對應頁面的標識,沒這個就跳不了 有一個小知識點

          • route.fullPath 拿到的地址是包括 searchhash 在內的完整地址。該字符串是經過百分號編碼的
          • route.path 經過百分號編碼的 URL 中的 pathname
           //路徑:http://127.0.0.1:3000/user?id=1
           console.log(route.path) // 輸出 /user
           console.log(route.fullPath) // 輸出 /user?id=1

          為了實現右邊側邊欄,需要引入element plus來快速搭建

               pnpm install element-plus

          main.ts改造一下,完整引入element-plus

           import { createApp } from 'vue'
           import App from './App.vue'
           import ElementPlus from 'element-plus' // element-plus 組件庫
           import 'element-plus/dist/index.css' // element-plus 組件庫樣式文件
           
           // 創建實例
           const setupAll=async ()=> {
             const app=createApp(App)
             app.use(ElementPlus)
             app.mount('#app')
           }
           
           setupAll() 

          我們來編寫下 側邊欄

           <!--
           @description: Sidebar
           -->
           
           <template>
             <div>
               <el-menu active-text-color="#ffd04b" background-color="#304156" default-active="2" text-color="#fff" router>
                 <el-sub-menu :index="item.path" v-for="item in routers">
                   <template #title>{{ item.name }}</template>
                   <el-menu-item :index="child.path" v-for="child in item.children">{{ child.name }}</el-menu-item>
                 </el-sub-menu>
               </el-menu>
             </div>
           </template>
           
           <script setup lang='ts'>
           import { filterRoutes } from '@/utils/router';
           import { computed } from 'vue';
           import { useRouter } from 'vue-router';
           const router=useRouter()
           // 通過計算屬性,路由發生變化時更新路由信息
           const routers=computed(()=> {
             return filterRoutes(router.getRoutes()) // router.getRoutes() 用于獲取路由信息
           })
           </script>

          統一導出 layout 架構,加一點小樣式

          
           <!--
           @description: layout index
           -->
           
           <template>
             <div class="app-wrapper">
               <Sidebar class="sidebar-container" />
               <App-Main class="main-container" />
             </div>
           </template>
           
           <script setup lang='ts'>
           import { ref, reactive } from 'vue'
           import Sidebar from './components/Sidebar.vue'
           import AppMain from './components/AppMain.vue'
           </script>
           
           <style scoped>
           .app-wrapper {
               display: flex;
           }
           .sidebar-container {
               width: 200px;
               height: 100vh;
               background-color: #304156;
               color: #fff;
           }
           .main-container {
               flex: 1;
               height: 100vh;
               background-color: #f0f2f5;
           }
           </style>

          pnpm run serve運行一下

          添加圖片注釋,不超過 140 字(可選)

          頁面權限管理

          通常我們實現頁面權限管理,比較常見的方案是,有權限的路由信息由后端傳給前端,前端再根據路由信息進行渲染

          我們先安裝下 pinia 模擬下后端傳過來的數據

          pnpm install pinia

          添加圖片注釋,不超過 140 字(可選)

           import { defineStore } from "pinia";
           
           interface AuthStore {
             // 菜單
             menus: any[];
           }
           
           export const useAuthStore=defineStore("authState", {
             state: (): AuthStore=> ({
               menus: [
                 {
                   path: "/routing",
                   component: null,
                   redirect: "user",
                   children: [
                     {
                       path: "/routing/user",
                       component: "/user.vue",
                       name: "用戶管理",
                       meta: {},
                     },
                     {
                       path: "/routing/menu",
                       component: "/menu.vue",
                       name: "菜單管理",
                       meta: {},
                     }
                   ],
                   name: "系統管理",
                   meta: undefined,
                 },
               ]
             }),
             getters: {},
             actions: {},
           });

          好了,我們把模擬的路由數據,加到本地路由中

          添加圖片注釋,不超過 140 字(可選)

          // permission.ts
          import router from './router'
          import type { RouteRecordRaw } from 'vue-router'
          import { formatRoutes } from './utils/router'
          import { useAuthStore } from '@/store';
          import { App } from 'vue';
          
          
          // 路由加載前
          router.beforeEach(async (to, from, next)=> {
            const { menus }=useAuthStore()
            routerList.forEach((route)=> {
              router.addRoute(menus as unknown as RouteRecordRaw) // 動態添加可訪問路由表
            })
            next()
          })
          
          // 路由跳轉之后調用
          router.afterEach((to)=> { })

          添加圖片注釋,不超過 140 字(可選)

          添加圖片注釋,不超過 140 字(可選)

          報錯了,為什么呢?

          對比路由表的數據,原來,組件模塊的數據與公共路由的數據不一致

          添加圖片注釋,不超過 140 字(可選)

          我們需要把模擬后端傳過來的數據處理一下

          添加圖片注釋,不超過 140 字(可選)

           // router.ts
           import Layout from '@/layout/index.vue';
           import type { RouteRecordRaw } from 'vue-router'
           
           /* 處理從后端傳過來的路由數據 */
           export const formatRoutes=(routes: any[])=> {
             const formatedRoutes: RouteRecordRaw[]=[]
             routes.forEach(route=> {
                 formatedRoutes.push(
                   {
                     ...route,
                     component: Layout, // 主要是將這個 null -> 組件
                     children: route.children.map((child: any)=> {
                       return {
                         ...child,
                         component: ()=> import(`@/views${child.component}`), // 根據 本地路徑配置頁面路徑
                       }
                     }),
                   }
                 )
             })
             return formatedRoutes;
           }

          再修改下permission.ts

          
           import router from './router'
           import type { RouteRecordRaw } from 'vue-router'
           import { formatRoutes } from './utils/router'
           import { useAuthStore } from '@/store';
           import { App } from 'vue';
           
           
           // 路由加載前
           router.beforeEach(async (to, from, next)=> {
             const { menus }=useAuthStore()
             const routerList=menus
             routerList.forEach((route)=> {
               router.addRoute(route as unknown as RouteRecordRaw) // 動態添加可訪問路由表
             })
             next()
           })
           
           // 路由跳轉之后調用
           router.afterEach((to)=> { }) 

          main.ts引入一下

           import './permission'

          可以正常訪問了

          添加圖片注釋,不超過 140 字(可選)

          按鈕權限

          除了頁面權限,外我們還有按鈕權限

          可以通過自定義指令來完成,permission.ts 中定義一下

          添加圖片注釋,不超過 140 字(可選)

           /* 按鈕權限 */
           export function hasPermi(app: App<Element>) {
             app.directive('hasPermi', (el, binding)=> {
               const { permissions }=useAuthStore()
               const { value }=binding
               const all_permission='*:*:*'
           
               if (value && value instanceof Array && value.length > 0) {
                 const permissionFlag=value
           
                 const hasPermissions=permissions.some((permission: string)=> {
                   return all_permission===permission || permissionFlag.includes(permission)
                 })
           
                 if (!hasPermissions) {
                   el.parentNode && el.parentNode.removeChild(el)
                 }
               } else {
                 throw new Error('權限不存在')
               }
             })
           }
           
           export const setupAuth=(app: App<Element>)=> {
             hasPermi(app)
           }
          
          

          需要掛載到main.ts

          
          
           import { createApp } from 'vue'
           import App from './App.vue'
           import { setupRouter } from './router/index'
           import ElementPlus from 'element-plus'
           import { createPinia } from 'pinia'
           import { setupAuth } from './permission'
           import 'element-plus/dist/index.css'
           import './permission'
           
           // 創建實例
           const setupAll=async ()=> {
             const app=createApp(App)
             setupRouter(app)
             setupAuth(app)
             app.use(ElementPlus)
             app.use(createPinia())
             app.mount('#app')
           }
           
           setupAll() 

          還是在store那里加一下模擬數據

           export const useAuthStore=defineStore("authState", {
             state: (): AuthStore=> ({
               menus: [
                 {
                   path: "/routing",
                   component: null,
                   redirect: "user",
                   children: [
                     {
                       path: "/routing/user",
                       component: "/user.vue",
                       name: "用戶管理",
                       meta: {},
                     },
                     {
                       path: "/routing/menu",
                       component: "/menu.vue",
                       name: "菜單管理",
                       meta: {},
                     }
                   ],
                   name: "系統管理",
                   meta: undefined,
                 },
               ],
               permissions: [
                 // '*:*:*', // 所有權限
                 'system:user:create',
                 'system:user:update',
                 'system:user:delete',
               ]
             }),
           });

          user.vue加入幾個按鈕,使用自定義指令

           <!-- user.vue -->
           <template>
             <div>
               <el-button type="primary" v-hasPermi="['system:user:create']">創建</el-button>
               <el-button type="primary" v-hasPermi="['system:user:update']">更新</el-button>
               <el-button type="primary" v-hasPermi="['system:user:delete']">刪除</el-button>
               <el-button type="primary" v-hasPermi="['system:user:admin']">沒權限</el-button>
             </div>
           </template>

          system:user:admin這個權限沒有配置,無法顯示

          添加圖片注釋,不超過 140 字(可選)

          加一下權限

          添加圖片注釋,不超過 140 字(可選)

          添加圖片注釋,不超過 140 字(可選)

          擴展

          用戶權限我們使用 v-hasPermi自定義指令,其原理是通過刪除當前元素,來實現隱藏

          如果使用 Element Plus 的標簽頁呢

          我們在 src/views/home.vue 寫一下基本樣式

          <!--
          @description: 主頁
          -->
          <template>
            <div>
              <el-tabs>
                <el-tab-pane label="標簽一" name="first">標簽一</el-tab-pane>
                <el-tab-pane label="標簽二" name="second">標簽二</el-tab-pane>
              </el-tabs>
            </div>
          </template>
          
          <script setup lang='ts'>
          
          </script>

          添加圖片注釋,不超過 140 字(可選)

          我們加下按鈕權限控制

          <template>
            <div>
              <el-tabs v-model="activeName">
                <el-tab-pane label="標簽一" v-hasPermi="['system:tabs:first']" name="first">標簽一</el-tab-pane>
                <el-tab-pane label="標簽二" name="second">標簽二</el-tab-pane>
              </el-tabs>
            </div>
          </template>

          添加圖片注釋,不超過 140 字(可選)

          因為這個權限我們沒有配置,標簽頁內容隱藏了,這沒問題

          但是,標簽沒隱藏啊,通常要是標簽一沒權限,應該是標簽項、和標簽內容都隱藏才對

          為什么會這樣呢?

          我們在 hasPermi 自定義指令中,打印下獲取到的元素

          添加圖片注釋,不超過 140 字(可選)

          添加圖片注釋,不超過 140 字(可選)

          id 為pane-firstpane-second元素對應位置在哪里,我們找一下 需要先把指令去掉,因為元素都被我們刪除的話,我們看不到具體DOM結構

          添加圖片注釋,不超過 140 字(可選)

          添加圖片注釋,不超過 140 字(可選)

          添加圖片注釋,不超過 140 字(可選)

          對比一下,明顯可以看出 hasPermi 自定義指令獲取到只是標簽內容的元素 那怎么辦? 解決辦法一:根據當前元素,一層層找到標簽項,然后刪除,這樣是可以。但是這樣太麻煩了,也只能用于標簽頁,那要是其他組件有這樣的問題咋辦 解決辦法二:我們寫一個函數判斷權限是否存在,再通過 v-if 進行隱藏


          主站蜘蛛池模板: 国模私拍一区二区三区| 一区高清大胆人体| 日本一区二区免费看| 欧洲精品码一区二区三区| 久久一区不卡中文字幕| 亚洲第一区精品观看| 黑巨人与欧美精品一区| 精品黑人一区二区三区| 亚洲av福利无码无一区二区| 中文字幕一区二区视频| 亚洲日本一区二区三区在线不卡 | 国产成人一区二区三区电影网站 | 在线播放偷拍一区精品| 无码人妻精品一区二区三区在线| 国产在线精品一区二区三区不卡| 福利一区二区在线| 国产精品福利区一区二区三区四区| 日韩高清一区二区| 国产精品亚洲综合一区在线观看| 亚洲AV无码一区二区三区久久精品 | 亚洲AV无码国产精品永久一区| 久久久老熟女一区二区三区| 无码人妻品一区二区三区精99| 亚洲av鲁丝一区二区三区| 无码视频一区二区三区在线观看 | 欧美av色香蕉一区二区蜜桃小说 | 精品国产一区二区22| 中文无码精品一区二区三区 | 亚洲欧洲专线一区| 冲田杏梨AV一区二区三区| 夜精品a一区二区三区| 色天使亚洲综合一区二区| 波多野结衣在线观看一区 | 国产成人无码精品一区不卡| 国产在线观看精品一区二区三区91| 在线视频精品一区| 一区二区三区在线看| 久久99热狠狠色精品一区| 奇米精品视频一区二区三区| 亚洲熟女乱色一区二区三区 | 国产无套精品一区二区 |