Spring Boot + Vue先後端分離(五)登陸攔截器

 

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

 

上篇文章說到前端路由,同時也簡單說了一下 先後端合併啓動項目,Spring Boot + Vue先後端分離(四)前端路由本篇文章接着上篇內容繼續爲你們介紹 登陸攔截器。php

 

本文是Spring Boot + Vue先後端分離 系列的第五篇,瞭解前面的文章有助於更好的理解本文:html


1.Spring Boot + Vue先後端分離(一)前端Vue環境搭建
2.Spring Boot + Vue先後端分離(二)前端Vue啓動流程
3.
Spring Boot + Vue先後端分離(三)實現登陸功能
4.
Spring Boot + Vue先後端分離(四)前端路由前端


 

目錄vue

(一).後端攔截器java

(二).前端攔截器webpack

 

前言ios

上一篇你們都學習到了 前端路由web

(1)hash模式spring

(2)history模式。vue-router

和先後端合併啓動,本文主要說一下 攔截器,主要限制在未登陸狀態下 對核心功能的訪問權限。

 

(一).後端攔截器

 

就像上面說的,添加攔截器主要是限制在未登陸狀態的權限控制,好比:咱們通常系統會要求只有在登陸狀態下才能進入系統,其餘路徑的訪問都須要重定向到登陸頁面,首先咱們先講解後端攔截器的開發。(注意:後端攔截器須要把先後端項目整合起來,如沒有合併(先後端分離部署)是沒有辦法使用這種方式

攔截器的邏輯以下:

1.用戶訪問 URL,檢測是否爲登陸頁面,若是是登陸頁面則不攔截

2.若是用戶訪問的不是登陸頁面,檢測用戶是否已登陸,若是未登陸則跳轉到登陸頁面,若是已登陸不作攔截。

 

後端攔截器從以下幾個點作處理:

 

(1) LoginController 登陸控制器

  •  
package com.cxzc.mycxzc.demo.controller;import com.cxzc.mycxzc.demo.bean.User;import com.cxzc.mycxzc.demo.response.Result;import com.cxzc.mycxzc.demo.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import org.springframework.web.util.HtmlUtils;import javax.servlet.http.HttpSession;import java.util.Objects;@Controllerpublic class LoginController { @Autowired UserService userService; @CrossOrigin @PostMapping(value = "req/login") @ResponseBody public Result login(@RequestBody User requestUser, HttpSession session) { String username = requestUser.getUsername(); username = HtmlUtils.htmlEscape(username); User user = userService.get(username, requestUser.getPassword()); if (null == user) { return new Result(400); } else { //用戶對象User添加到session中 session.setAttribute("userinfo", user); return new Result(200); } }}package com.cxzc.mycxzc.demo.controller;import com.cxzc.mycxzc.demo.bean.User;import com.cxzc.mycxzc.demo.response.Result;import com.cxzc.mycxzc.demo.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import org.springframework.web.util.HtmlUtils;import javax.servlet.http.HttpSession;import java.util.Objects;@Controllerpublic class LoginController {@AutowiredUserService userService;@CrossOrigin@PostMapping(value = "req/login")@ResponseBodypublic Result login(@RequestBody User requestUser, HttpSession session) {String username = requestUser.getUsername();username = HtmlUtils.htmlEscape(username);User user = userService.get(username, requestUser.getPassword());if (null == user) {return new Result(400);} else {//用戶對象User添加到session中session.setAttribute("userinfo", user);return new Result(200);}}}

解釋:

不難發現,這個登陸控制器咱們比以前的多了一行代碼:session.setAttribute("userinfo", user); 這行代碼的做用:把用戶信息存在 Session 對象中(當用戶在應用程序的 Web 頁之間跳轉時,存儲在 Session 對象中的變量不會丟失),這樣在訪問別的頁面時,能夠經過判斷是否存在用戶變量來判斷用戶是否登陸。這是一種比較簡單的方式,但讓還有其餘方式,這裏不作多說,能夠自行查找。

 


(2) 
LoginInterceptor 登陸攔截器

 

新建 package 名爲 interceptor,新建類 LoginInterceptor

  •  
package com.cxzc.mycxzc.demo.interceptor;import com.cxzc.mycxzc.demo.bean.User;import org.springframework.web.servlet.HandlerInterceptor;import org.apache.commons.lang.StringUtils;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;/** * 判斷 session 中是否存在 user 屬性,若是存在就經過,若是不存在就跳轉到 login 頁面 */public class LoginInterceptor implements HandlerInterceptor{ @Override public boolean preHandle (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { HttpSession session = httpServletRequest.getSession(); String contextPath=session.getServletContext().getContextPath(); String[] requireAuthPages = new String[]{ "index", }; String uri = httpServletRequest.getRequestURI(); uri = StringUtils.remove(uri, contextPath+"/"); String page = uri; if(begingWith(page, requireAuthPages)){ User user = (User) session.getAttribute("userinfo"); if(user==null) { httpServletResponse.sendRedirect("login"); return false; } } return true; } private boolean begingWith(String page, String[] requiredAuthPages) { boolean result = false; for (String requiredAuthPage : requiredAuthPages) { if(StringUtils.startsWith(page, requiredAuthPage)) { result = true; break; } } return result; }}package com.cxzc.mycxzc.demo.interceptor;import com.cxzc.mycxzc.demo.bean.User;import org.springframework.web.servlet.HandlerInterceptor;import org.apache.commons.lang.StringUtils;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;/*** 判斷 session 中是否存在 user 屬性,若是存在就經過,若是不存在就跳轉到 login 頁面*/public class LoginInterceptor implements HandlerInterceptor{@Overridepublic boolean preHandle (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {HttpSession session = httpServletRequest.getSession();String contextPath=session.getServletContext().getContextPath();String[] requireAuthPages = new String[]{"index",};String uri = httpServletRequest.getRequestURI();uri = StringUtils.remove(uri, contextPath+"/");String page = uri;if(begingWith(page, requireAuthPages)){User user = (User) session.getAttribute("userinfo");if(user==null) {httpServletResponse.sendRedirect("login");return false;}}return true;}private boolean begingWith(String page, String[] requiredAuthPages) {boolean result = false;for (String requiredAuthPage : requiredAuthPages) {if(StringUtils.startsWith(page, requiredAuthPage)) {result = true;break;}}return result;}}

解釋:

在 Springboot 中能夠直接繼承攔截器的接口,而後實現 preHandle 方法。preHandle 方法裏的代碼會在訪問須要攔截的頁面時執行。

1,判斷 session 中是否存在 user 屬性,若是存在就放行,若是不存在就跳轉到 login 頁面。

2,

  •  
 String[] requireAuthPages = new String[]{ "index", };String[] requireAuthPages = new String[]{"index",};

,能夠在裏面寫下須要攔截的路徑,目前咱們只有一個頁面 因此只攔截index,後面添加了新頁面能夠在這裏新增攔截。

 

(3)WebConfigurer 配置攔截器

 

攔截器咱們添加好了,可是這樣是不生效果的,怎麼纔能有效呢,須要咱們配置攔截器。

新建 package 名爲 config,新建類 WebConfigurer

 

  •  
package com.cxzc.mycxzc.demo.config;import com.cxzc.mycxzc.demo.interceptor.LoginInterceptor;import org.springframework.boot.SpringBootConfiguration;import org.springframework.context.annotation.Bean;import org.springframework.web.servlet.config.annotation.*;@SpringBootConfigurationpublic class WebConfigurer implements WebMvcConfigurer { @Bean public LoginInterceptor getLoginIntercepter() { return new LoginInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry){ registry.addInterceptor(getLoginIntercepter()).addPathPatterns("/**").excludePathPatterns("/index.html"); }}package com.cxzc.mycxzc.demo.config;import com.cxzc.mycxzc.demo.interceptor.LoginInterceptor;import org.springframework.boot.SpringBootConfiguration;import org.springframework.context.annotation.Bean;import org.springframework.web.servlet.config.annotation.*;@SpringBootConfigurationpublic class WebConfigurer implements WebMvcConfigurer {@Beanpublic LoginInterceptor getLoginIntercepter() {return new LoginInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistry registry){registry.addInterceptor(getLoginIntercepter()).addPathPatterns("/**").excludePathPatterns("/index.html");}}

 

在這個配置類中,添加了寫好的攔截器。

registry.addInterceptor(getLoginIntercepter()).addPathPatterns("/**")

.excludePathPatterns("/index.html");

做用是對全部路徑應用攔截器,除了 /index.html

 

以前咱們在攔截器 LoginInterceptor 中配置的路徑,即 index,觸發的時機是在攔截器生效以後。也就是說,咱們訪問一個 URL,會首先經過 Configurer 判斷是否須要攔截,若是須要,纔會觸發攔截器 LoginInterceptor,根據咱們自定義的規則進行再次判斷。

 

配置完攔截器 咱們就能夠驗證一下了:驗證步驟

 

1,運行後端項目,

2,訪問 http://localhost:8082/index ,發現頁面自動跳轉到了 http://localhost:8082/login ,輸入用戶名和密碼登陸,跳轉http://localhost:8082/index 

3,把瀏覽器標籤關掉,再在一個新標籤頁輸入 http://localhost:8082/index ,這時候不會在進入登陸頁面,直接進入步驟2的登陸成功後的頁面,說明不會被攔截。

 

(二).前端攔截器

 

上面說了使用了後端攔截器作登陸的攔截操做,這種攔截器只有在將先後端項目整合在一塊兒時才能生效,可是咱們作先後端分離確定是要分開的,因此咱們還須要熟知 前端攔截器的使用。

 

前端登陸攔截器,須要在前端判斷用戶的登陸狀態。這裏咱們須要引入一個很好的工具——Vuex,它是專門爲 Vue 開發的狀態管理,咱們能夠把須要在各個組件中傳遞使用的變量、方法定義在這裏。能夠像以前那樣在組件的 data 中設置一個狀態標誌,但登陸狀態應該被視爲一個全局屬性,而不該該只寫在某一組件中。

 

(1)引入Vuex

 

引入Vuex 須要兩步操做:
1,執行命令行 npm install vuex --save  添加組件

2,在src目錄下添加文件目錄store,在裏面新建一個index.js文件,改文件添加以下內容:

  •  
import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({ state: { user: { username: window.localStorage.getItem('userinfo' || '[]') == null ? '' : JSON.parse(window.localStorage.getItem('userinfo' || '[]')).username } }, mutations: { login (state, user) { state.user = user window.localStorage.setItem('userinfo', JSON.stringify(user)) } }})import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {user: {username: window.localStorage.getItem('userinfo' || '[]') == null ? '' : JSON.parse(window.localStorage.getItem('userinfo' || '[]')).username}},mutations: {login (state, user) {state.user = userwindow.localStorage.setItem('userinfo', JSON.stringify(user))}}})

 

解釋:
1,index.js 裏設置須要的狀態變量和方法。爲了實現登陸攔截器,這裏須要一個記錄用戶信息的變量。咱們使用一個用戶對象而不是僅僅使用一個布爾變量。同時,設置一個方法,觸發這個方法時能夠爲咱們的用戶對象賦值。

 

2,咱們這裏用到了這個函數 localStorage,即本地存儲,在項目打開的時候會判斷本地存儲中是否有 userinfo 這個對象存在,若是存在就取出來並得到 username 的值,不然則把 username 設置爲空。

 

3,緩存這些數據,只要不清除緩存,登陸的狀態就會一直保存。

 

 

(2)調整路由配置

 

這裏咱們須要區分頁面是否須要攔截,修改一下路由 src\router\index.js,把須要攔截的路由中設置一個 requireAuth 字段

 

  •  
import Vue from 'vue'import Router from 'vue-router'// 導入剛纔編寫的組件import HomePage from '@/components/home/HomePage'import Login from '@/components/Login'Vue.use(Router)export default new Router({ mode: 'history', routes: [ // 下面都是固定的寫法 { path: '/login', name: 'Login', component: Login }, { path: '/index', name: 'HomePage', component: HomePage, meta: { requireAuth: true } } ]})import Vue from 'vue'import Router from 'vue-router'// 導入剛纔編寫的組件import HomePage from '@/components/home/HomePage'import Login from '@/components/Login'Vue.use(Router)export default new Router({mode: 'history',routes: [// 下面都是固定的寫法{path: '/login',name: 'Login',component: Login},{path: '/index',name: 'HomePage',component: HomePage,meta: {requireAuth: true}}]})

解釋:

1,

  •  
meta: { requireAuth: true }meta: {requireAuth: true}

添加是否攔截狀態

咱們目前只有一個頁面 因此只添加了一個,登陸沒有添加。

 

(3)攔截的判斷(main.js

 

  •  
// The Vue build version to load with the `import` command// (runtime-only or standalone) has been set in webpack.base.conf with an alias.import Vue from 'vue'import App from './App'import router from './router'import store from './store'// 設置反向代理,前端請求默認發送到 http://localhost:8082/var axios = require('axios')axios.defaults.baseURL = 'http://localhost:8082/req'// 全局註冊,以後可在其餘組件中經過 this.$axios 發送數據Vue.prototype.$axios = axiosVue.config.productionTip = falserouter.beforeEach((to, from, next) => { if (to.meta.requireAuth) { if (store.state.user.username) { next() } else { next({ path: 'login', query: {redirect: to.fullPath} }) } } else { next() }})/* eslint-disable no-new */new Vue({ el: '#app', render: h => h(App), router, store, components: { App }, template: '<App/>'})// The Vue build version to load with the `import` command// (runtime-only or standalone) has been set in webpack.base.conf with an alias.import Vue from 'vue'import App from './App'import router from './router'import store from './store'// 設置反向代理,前端請求默認發送到 http://localhost:8082/var axios = require('axios')axios.defaults.baseURL = 'http://localhost:8082/req'// 全局註冊,以後可在其餘組件中經過 this.$axios 發送數據Vue.prototype.$axios = axiosVue.config.productionTip = falserouter.beforeEach((to, from, next) => {if (to.meta.requireAuth) {if (store.state.user.username) {next()} else {next({path: 'login',query: {redirect: to.fullPath}})}} else {next()}})/* eslint-disable no-new */new Vue({el: '#app',render: h => h(App),router,store,components: { App },template: '<App/>'})

解釋:

1,使用 router.beforeEach()函數,意思是在訪問每個路由前調用

2,import store from'./store' 引入

3,邏輯操做:判斷訪問的路徑是否須要登陸,若是須要,判斷 store 裏有沒有存儲 userinfo 的信息,若是存在,則進入,不然跳轉到登陸頁面,並存儲訪問的頁面路徑(以便在登陸後跳轉到訪問頁)

 

(4)登陸頁面修改

 

  •  
<template><div> 帳號: <input type="text" v-model="loginForm.username" placeholder="請輸入帳號"/><br><br> 密碼: <input type="password" v-model="loginForm.password" placeholder="請輸入密碼"/><br><br><button v-on:click="login">登陸帳號</button></div></template><script>export default {name: 'Login', data () {return {loginForm: {username: '',password: '' },responseResult: [] } },methods: { login () {var _this = thisthis.$axios .post('/login', {username: this.loginForm.username,password: this.loginForm.password }) .then(succe***esponse => {if (succe***esponse.data.code === 200) {// var data = this.loginForm_this.$store.commit('login', _this.loginForm)var path = this.$route.query.redirectthis.$router.replace({path: path === '/' || path === undefined ? '/index' : path}) } else { alert('登陸失敗!') } }) .catch(failResponse => { }) } }}</script><template><div>帳號: <input type="text" v-model="loginForm.username" placeholder="請輸入帳號"/><br><br>密碼: <input type="password" v-model="loginForm.password" placeholder="請輸入密碼"/><br><br><button v-on:click="login">登陸帳號</button></div></template><script>export default {name: 'Login',data () {return {loginForm: {username: '',password: ''},responseResult: []}},methods: {login () {var _this = thisthis.$axios.post('/login', {username: this.loginForm.username,password: this.loginForm.password}).then(succe***esponse => {if (succe***esponse.data.code === 200) {// var data = this.loginForm_this.$store.commit('login', _this.loginForm)var path = this.$route.query.redirectthis.$router.replace({path: path === '/' || path === undefined ? '/index' : path})} else {alert('登陸失敗!')}}).catch(failResponse => {})}}}</script>

解釋:

修改了登陸成功後的重定向攔截。

1.點擊登陸按鈕,向後端發送數據

2.受到後端返回的成功代碼時,觸發 store 中的 login() 方法,把 loginForm 對象傳遞給 store 中的 userinfo 對象

3.獲取登陸前頁面的路徑並跳轉,若是該路徑不存在,則跳轉到首頁

  •  
_this.$store.commit('login', _this.loginForm)var path = this.$route.query.redirectthis.$router.replace({path: path === '/' || path === undefined ? '/index' : path})_this.$store.commit('login', _this.loginForm)var path = this.$route.query.redirectthis.$router.replace({path: path === '/' || path === undefined ? '/index' : path})

 

功能添加完成,咱們運行試試吧。

 

步驟和後端攔截器同樣:

 

1,同時運行先後端項目,訪問 http://localhost:8080/index ,發現頁面直接跳轉到了 http://localhost:8080/login?redirect=%2Findex

2,輸入帳號密碼後登陸,成功跳轉到 http://localhost:8080/index ,

3,未清理緩存 狀況下  再次訪問則無需登陸(除非清除緩存)。

 

 

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

 

 

 

 

相關文章
相關標籤/搜索