驗證過濾器進行權限驗證的原理。
Filter過濾器:可以把對資源的請求攔截下來,從而實現一些特殊的功能。一般完成登錄校驗、統一編碼處理、敏感字符處理等通用操作。
定義:實現Filter接口
配置:@WebFilter(urlPatterns="/*")
? ? ? ? ? ?@ServletComponentScan
執行流程:放行后訪問對應資源,資源訪問完成后回到Filter中,執行放行后邏輯
過濾器鏈:一個web應用中可以配置多個過濾器,這多個過濾器就形成了一個過濾器鏈
LoginFilter
package edu.wust.filter;import com.alibaba.fastjson.JSONObject;
import edu.wust.pojo.Result;
import edu.wust.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;@Slf4j
@WebFilter(urlPatterns = "/*") //攔截所有請求
public class LoginFilter implements Filter {//路徑匹配器,支持通配符public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();private boolean isStaticResource(String requestURI){//靜態資源目錄列表List<String > staticResourcePatterns = Arrays.asList("/image/","/css/","/js/","/fonts/","/img/","/favicon.ico"//可以根據需要添加更多靜態資源目錄);// 遍歷靜態資源目錄列表,檢查請求URI是否以這些目錄開頭for (String pattern : staticResourcePatterns) {if (requestURI.startsWith(pattern)) {return true; // 如果找到匹配的目錄,則返回true}}// 如果沒有找到匹配的目錄,則返回falsereturn false;}@Overridepublic void init(FilterConfig filterConfig) throws ServletException { //初始化,web服務器啟動創建Filter時調用,只調用一次System.out.println("init 初始化方法執行了");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//攔截到請求時,調用該方法,可調用多次//強轉HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;//1.獲取請求url并定義不需要處理的請求路徑String url = request.getRequestURI();// /backend/index.html
// String url = request.getRequestURL().toString();log.info("請求的url:{}",url);String[] urls = new String[]{"/login","/login.html","/index.html","/order_show.html","/index2","/error.html","/listOrder/*"};//2.判斷請求url是否需要處理,如果不需要處理,直接放行。
// if (url.contains("login")){
// log.info("登錄操作,放行...");
// filterChain.doFilter(servletRequest,servletResponse);
// return; //無需向下運行
// }// 檢查請求URI是否為靜態資源if (isStaticResource(url)) {filterChain.doFilter(request, response); // 靜態資源,跳過驗證return;}//判斷本次請求是否需要處理boolean check = check(urls,url);//如果不需要處理,則直接放行if(check){log.info("本次請求{}不需要處理",url);filterChain.doFilter(request,response);return;}//3.獲取請求頭中的令牌(token)。String jwt = request.getHeader("Authorization");log.info("從請求頭中獲取的令牌:{}",jwt);//4.判斷令牌是否存在,如果不存在,返回錯誤信息。if (!StringUtils.hasLength(jwt)){log.info("請求頭token為空,返回未登錄的信息");Result error = Result.error("Not_LOGIN");//手動將對象轉為jsonString notLogin = JSONObject.toJSONString(error);response.getWriter().write(notLogin);return;}//5.解析token,如果解析失敗,返回錯誤信息。try {JwtUtils.parseJWT(jwt); //校驗jwt令牌} catch (Exception e){ //jwt令牌解析失敗e.printStackTrace();log.info("解析令牌失敗,返回未登錄的信息");Result error = Result.error("令牌解析失敗");//手動將對象轉為jsonString notLogin = JSONObject.toJSONString(error);response.getWriter().write(notLogin);return;}//6.放行。log.info("令牌合法,放行");filterChain.doFilter(request,response);// System.out.println("攔截到了請求...放行前邏輯");
//
// //放行
// filterChain.doFilter(servletRequest,servletResponse);
//
// System.out.println("攔截到了請求...放行后邏輯");}/*** 路徑匹配,檢查本次請求是否需要放行* @param urls* @param requestURI* @return*/public boolean check(String[] urls,String requestURI){for (String url : urls) {boolean match = PATH_MATCHER.match(url, requestURI);if(match){return true;}}return false;}@Overridepublic void destroy() { //銷毀方法,服務器關閉時調用,只調用一次System.out.println("destroy 銷毀方法執行了");}
}
LoginController
package edu.wust.controller;import edu.wust.pojo.Manager;
import edu.wust.pojo.PageBean;
import edu.wust.pojo.Result;
import edu.wust.service.LoginService;
import edu.wust.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;@Slf4j
@RestController
public class LoginController {@Autowiredprivate LoginService loginService;// 基礎登錄功能
// @RequestMapping("/login")
// public Result login(@RequestBody Manager manager){
// log.info("管理員登錄:{}",manager);
// Manager m = loginService.login(manager);
// return m!=null ?Result.success():Result.error("用戶名或密碼錯誤");
// }//JWT令牌登錄驗證@RequestMapping("/login")public Result login(@RequestBody Manager manager){log.info("管理員登錄:{}",manager);Manager m = loginService.login(manager);//登錄成功,生成令牌,下發令牌if(m != null){Map<String, Object> claims = new HashMap<>();claims.put("id",m.getId()); //把管理員id信息存入claims中claims.put("username",m.getUsername());claims.put("password",m.getPassword());//生成jwt令牌String jwt = JwtUtils.generateJwt(claims); //jwt中包含了當前登錄的員工信息System.out.println(jwt);Map<String,String> response = new HashMap<>();response.put("token",jwt);return Result.success(jwt);}return Result.error("用戶名或密碼錯誤");}@RequestMapping("/index2")public Result findAll1(){PageBean orderList = loginService.list(1,20);return Result.success(orderList);}
Login.html
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><!--引入組件庫--><script src="./js/jquery.min.js"></script><script src="./js/vue.js"></script><script src="./js/element.js"></script><script src="./js/axios-0.18.0.js"></script><link rel="stylesheet" href="./js/element.css"><title>登錄頁面</title>
</head><body>
<div id="app"><div class="Login_container"><div class="Login_box"><div class="avatar_box"><img src="./image/head.jpg" alt=""></div><!--登錄表單區--><el-form :model="form" :rules="rules" ref="loginForm" label-width="80px" class="login_form"><el-form-item label="用戶" prop="username"><el-input v-model="form.username" ></el-input></el-form-item><el-form-item label="密碼" prop="password"><el-input type="password" v-model="form.password"></el-input></el-form-item><div class="btns"><el-button type="primary" :plain="true" @click="submitForm('loginForm')">登錄</el-button><el-button type="primary" @click="resetForm('loginForm')">重置</el-button></div></el-form></div></div>
</div>
</body><script>new Vue({el:"#app",data(){return{//登錄表單的數據綁定對象form:{username:'',password:''},// tableData:[],//表單的驗證規則對象rules:{//用戶名合法username: [{ required: true, message: '請輸入登錄名稱', trigger: 'blur' },{ min: 3, max: 10, message: '長度在 3 到 10 個字符', trigger: 'blur' }],//密碼合法password:[{ required: true, message: '請輸入密碼', trigger: 'blur' },{ min: 6, max: 15, message: '長度在 6 到 15 個字符', trigger: 'blur' }]},token: localStorage.getItem("token")||''};},methods:{//登錄submitForm(formName) {console.log(this.form.username);console.log(this.form.password);this.$refs[formName].validate((valid) => {//表單驗證if (valid) {axios.post('/login',{'username':this.form.username,'password':this.form.password}).then(res=>{//從服務器響應中提取一個令牌,并將其存儲到瀏覽器的LocalStorage中const token = res.data.data;localStorage.setItem("token",token);if(res.data.code) {// 假設服務器返回了正確的登錄信息// this.tableData = res.data;// console.log(this.res.data);location.href='order_show.html';alert('登錄成功')} else {alert('登錄失敗');}}).catch(error => {console.error(error);})axios.get('/login', {headers: {'Authorization': `Bearer ${this.token}` // 注意這里是'Authorization'并使用Bearer前綴}}).then(res => {// ... 其他邏輯}).catch(error => {console.error(error);});}else {console.log('error submit!!');return false;}});},//重置resetForm(formName) {this.$refs[formName].resetFields();}}})
</script><style>#app{height:100%;}.Login_container{background-image: url("./image/login.png");background-size: cover;background-position: center;background-repeat: no-repeat;height: 100%;/* 如果容器是 body 或其他可能不完全占據視口的元素,你可能需要額外的樣式來確保它占據整個視口 */width: 100%;}.Login_box{width:450px;height: 300px;background-color:rgba(255,255,255,0.3);border-radius: 3px;position: absolute;left: 36%;top:55%;transform: translate(-50%,-50%);}.avatar_box{height: 130px;width:130px;border: 1px solid #eee;border-radius: 50%;padding: 8px;box-shadow: 0 0 10px #ddd;position: absolute;left: 50%;top:4%;transform: translate(-50%,-50%);background-color:#fff;}img{width:120px;border-radius: 50%;background-color: #eee;}.login_form{position:absolute;bottom:25px;left:30px;}.btns{display:flex;justify-content:flex-end;}
</style></html>
order_show.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><!--引入組件庫--><script src="./js/jquery.min.js"></script><script src="./js/vue.js"></script><script src="./js/element.js"></script><script src="./js/axios-0.18.0.js"></script><link rel="stylesheet" href="./js/element.css"><title>訂單管理</title>
</head>
<body>
<div id="app"><!--Session登錄驗證--><!--v-if="resData.code==1"--><div><el-container style="height: 100vh; display: flex; flex-direction: row;"><!--側邊欄--><el-aside width="200px" ><el-menu background-color="#FEF5E4" text-color="#F09466" active-text-color="#ffd04b" style="position: absolute;top: 100px;"><el-menu-item index="1"><i class="el-icon-menu"></i><span slot="title">用戶管理</span></el-menu-item><el-menu-item index="2"><i class="el-icon-document"></i><span slot="title">電影信息管理</span></el-menu-item><el-menu-item index="3"><i class="el-icon-document"></i><span slot="title">場次管理</span></el-menu-item><el-menu-item index="4"><i class="el-icon-document"></i><span slot="title">訂單管理</span></el-menu-item></el-menu></el-aside><el-container><!--頭部--><el-header style="height:80px; position: sticky; top: 0;z-index: 10" ><div><img src="./image/head.jpg" alt="海綿寶寶">影院管理系統</div><el-button type="info" @click="back" style="height:40px; background-color: #D7A93A">退出</el-button></el-header><!--查詢--><h1 style="text-align: center">用戶信息</h1><div align="center"><el-form :inline="true" :model="formInline" class="demo-form-inline"><el-form-item label="用戶ID"><el-input v-model="formInline.uid" placeholder="用戶ID"></el-input></el-form-item><el-form-item label="電影ID"><el-input v-model="formInline.mid" placeholder="電影ID"></el-input></el-form-item><el-form-item label="廳號"><el-input v-model="formInline.hid" placeholder="廳號"></el-input></el-form-item><el-form-item><el-button type="warning" plain @click="onSubmit">查詢</el-button></el-form-item><!--新增信息彈框--><el-dialog title="新增訂單信息" :visible.sync="insertRule.insertDialog" width="50%" center><el-form :model="order" label-width="100px"><el-row :gutter="20"><el-col :span="20"><el-form-item label="用戶ID" prop="uid"><el-row :gutter="4"><el-col :span="12" class="checkRule_list"><el-input v-model="order.uid" placeholder="請輸入用戶ID"></el-input></el-col></el-row></el-form-item></el-col></el-row><el-row :gutter="20"><el-col :span="20"><el-form-item label="電影ID" prop="mid"><el-row :gutter="4"><el-col :span="12" class="checkRule_list"><el-input v-model="order.mid" placeholder="請輸入電影ID"></el-input></el-col></el-row></el-form-item></el-col></el-row><el-row :gutter="20"><el-col :span="20"><el-form-item label="廳號" prop="hid"><el-row :gutter="4"><el-col :span="12" class="checkRule_list"><el-input v-model="order.hid" placeholder="請輸入廳號"></el-input></el-col></el-row></el-form-item></el-col></el-row><el-row :gutter="20"><el-col :span="20"><el-form-item label="觀影時間" prop="time"><el-date-pickerv-model="order.time"type="datetime"placeholder="選擇觀影時間"format="yyyy-MM-dd HH:mm:ss"value-format="yyyy-MM-dd HH:mm:ss"></el-date-picker></el-form-item></el-col></el-row><el-row :gutter="20"><el-col :span="20"><el-form-item label="座位號" prop="snum"><el-row :gutter="4"><el-col :span="12" class="checkRule_list"><el-input v-model="order.snum" placeholder="請輸入座位號"></el-input></el-col></el-row></el-form-item></el-col></el-row><el-row :gutter="20"><el-col :span="20"><el-form-item label="價格" prop="price"><el-row :gutter="4"><el-col :span="12" class="checkRule_list"><el-input v-model="order.price" placeholder="請輸入價格"></el-input></el-col></el-row></el-form-item></el-col></el-row></el-form><span slot="footer" class="checkRule_footer"><el-button @click="cancelIn">取 消</el-button><el-button type="primary" @click="insert">確 定</el-button></span></el-dialog><el-form-item><el-button type="warning" plain @click="handleInsert">添加</el-button></el-form-item></el-form></div><!--主要內容區--><el-main style="flex: 1; overflow: auto;"><img class="moving-image"src="./image/1.gif" alt="動態圖像" style="position: absolute;right: 10px"><el-card><!--用戶信息表--><el-table :data="tableData.filter(data => !search || data.oid.toString().toLowerCase().includes(search.toLowerCase()))" style="width: 100%"><el-table-column prop="oid" label="訂單編號" width="180"></el-table-column><el-table-column prop="uid" label="用戶ID" width="180"></el-table-column><el-table-column prop="mid" label="電影ID"></el-table-column><el-table-column prop="hid" label="廳號"></el-table-column><el-table-column prop="time" label="觀影時間"></el-table-column><el-table-column prop="snum" label="座位號"></el-table-column><el-table-column prop="price" label="價格"></el-table-column><el-table-column prop="createtime" label="訂單創建時間"></el-table-column><el-table-column align="right"><template slot="header" slot-scope="scope"><el-input v-model="search" size="mini" placeholder="輸入訂單編號搜索"></el-input></template><template slot-scope="scope"><!--編輯信息彈框--><el-dialog title="更新訂單信息" :visible.sync="editRule.editDialog" width="50%" center><el-form :model="editRule.ruleForm" label-width="100px"><el-row :gutter="20"><el-col :span="20"><el-form-item label="用戶ID" prop="uid"><el-row :gutter="4"><el-col :span="12" class="checkRule_list"><el-input v-model="order.uid" placeholder="請輸入用戶ID"></el-input></el-col></el-row></el-form-item></el-col></el-row><el-row :gutter="20"><el-col :span="20"><el-form-item label="電影ID" prop="mid"><el-row :gutter="4"><el-col :span="12" class="checkRule_list"><el-input v-model="order.mid" placeholder="請輸入電影ID"></el-input></el-col></el-row></el-form-item></el-col></el-row><el-row :gutter="20"><el-col :span="20"><el-form-item label="廳號" prop="hid"><el-row :gutter="4"><el-col :span="12" class="checkRule_list"><el-input v-model="order.hid" placeholder="請輸入廳號"></el-input></el-col></el-row></el-form-item></el-col></el-row><el-row :gutter="20"><el-col :span="20"><el-form-item label="觀影時間" prop="time"><el-date-pickerv-model="order.time"type="datetime"placeholder="選擇觀影時間"format="yyyy-MM-dd HH:mm:ss"value-format="yyyy-MM-dd HH:mm:ss"></el-date-picker></el-form-item></el-col></el-row><el-row :gutter="20"><el-col :span="20"><el-form-item label="座位號" prop="snum"><el-row :gutter="4"><el-col :span="12" class="checkRule_list"><el-input v-model="order.snum" placeholder="請輸入座位號"></el-input></el-col></el-row></el-form-item></el-col></el-row><el-row :gutter="20"><el-col :span="20"><el-form-item label="價格" prop="price"><el-row :gutter="4"><el-col :span="12" class="checkRule_list"><el-input v-model="order.price" placeholder="請輸入價格"></el-input></el-col></el-row></el-form-item></el-col></el-row></el-form><span slot="footer" class="checkRule_footer"><el-button @click="cancel">取 消</el-button><el-button type="primary" @click="update">確 定</el-button></span></el-dialog><el-button size="mini" @click="handleEdit(scope.$index, scope.row)">編輯</el-button><el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">刪除</el-button></template></el-table-column></el-table><!--分頁--><el-paginationlayout="total, sizes, prev, pager, next, jumper"@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="currentPage4":page-sizes="[20, 30, 40, 50]":page-size="pageSize":total="total"></el-pagination></el-card></el-main></el-container></el-container></div>
<!--Session登錄驗證-->
<!-- <div v-else-if="resData.code==0" style="position: absolute;top: 30%;left: 20%;font-size: 100px">-->
<!--<!– {{resData.code}}沒有權限訪問!–>-->
<!-- 沒有權限訪問!-->
<!-- </div>-->
</div></body><script>new Vue({el:"#app",data(){return{tableData:[],search: '',currentPage4: 1,pageSize: 20,total:null,//編輯規則editRule: {editDialog: false, //彈窗},insertRule: {insertDialog: false, //彈窗},order:{time:''},//查詢formInline:{uid:'',mid:'',hid:''},//驗證是否登錄resData:{code:'',msg:'',data:''},token: localStorage.getItem("token")||''}},methods:{//返回back(){var URL='/logout';axios.get(URL).then(res=>{location.href='login.html'})},//新增handleInsert(){this.insertRule.insertDialog=true;},cancelIn(){this.insertRule.insertDialog=false;},insert(){var url = '/insertOrder';axios.put(url,this.order).then(res=>{if(res.data.code){this.insertRule.insertDialog=false;location.href='order_show.html';}else{alert(res.data.message);}}).catch(error=>{})},//編輯handleEdit(index, row) {this.editRule.editDialog=true;var URL=`getByOid/${row.oid}`axios.get(URL).then(response=>{if(response.data.code){this.order=response.data.data;}}).catch(error=>{})console.log(index, row);},//取消彈框cancel(){this.editRule.editDialog=false;},//彈框編輯信息并確認update:function(){var URL='updateOrder';//put請求用于更新資源.url 是你想要更新的資源的地址;this.poet是包含更新后數據的對象,這個對象將被發送到服務器以更新資源。axios.put(URL,this.order).then(res=>{if(res.data.code){this.editRule.editDialog=false;location.href='order_show.html';}else{alert(res.data.message);}}).catch(error=>{})},//刪除handleDelete(index, row) {if (window.confirm("確定要刪除該記錄嗎?")) {axios.post(`/deleteOrder?oid=${row.oid}`).then(ans => {alert("刪除成功");this.findAll();}).catch(function (error) {console.log(error);})}console.log(index, row);},//分頁findAll() {var URL = `/listOrder/${this.currentPage4}/${this.pageSize}`// var URL = `/index2`axios.get(URL).then(res => {if (res.data.code) {this.tableData = res.data.data.rows;this.total = res.data.data.total;}}).catch(error => {console.error(error);})},handleSizeChange(val) {this.pageSize = val;this.findAll();console.log(`每頁 ${val} 條`);},handleCurrentChange(val) {this.currentPage4 = val;this.findAll();console.log(`當前頁: ${val}`);},//查詢onSubmit() {var url = `/listOrderWithConditions/${this.currentPage4}/${this.pageSize}?uid=${encodeURIComponent(this.formInline.uid)}&mid=${encodeURIComponent(this.formInline.mid)}&hid=${encodeURIComponent(this.formInline.hid)}`console.log(this.formInline.uid)console.log(this.formInline.mid)console.log(this.formInline.hid)axios.get(url).then(res =>{this.tableData = res.data.data.rows;this.total=res.data.data.total;console.log(this.tableData);console.log(this.total);}).catch(error=>{console.error(error);})},},// mounted(){// axios.get('/listOrder').then(res=>{// if(res.data.code){// this.tableData = res.data.data;// }// });// },// Session登錄驗證// mounted() {// var url = `/index1`// axios.get(url)// .then(response => {// this.resData = response.data;// console.log(this.resData);// })// .catch(error=>{// console.error(error);// })// },// jwt令牌登錄驗證mounted() {var url = `/index2`axios.get(url,{headers:{"token":this.token}}).then(res => {if (res.data.code){console.log("已登錄")this.tableData = res.data.data.rows;this.total = res.data.data.total;} else {location.href = "error.html"}})},created() {this.findAll();}})
</script><style>@keyframes verticalMove {0% {transform: translateY(0);}50% {transform: translateY(400px);}100% {transform: translateY(0);}}.moving-image {animation: verticalMove 50s ease-in-out infinite; /* 2秒動畫,緩動函數,無限循環 */}a {color: white;text-decoration: none;}.el-header {background-color: #FEF5E4;display: flex;justify-content: space-between;align-items: center;color: #F09466;font-size: 40px;}> div {display: flex;align-items: center;}img {width: 55px;border-radius: 50%;background-color: #eee;}.el-aside {background-color:#FEF5E4;height: 100%;}body>.el-container {margin-bottom: 40px;}.el-container:nth-child(5) .el-aside,.el-container:nth-child(6) .el-aside {line-height: 260px;}.el-container:nth-child(7) .el-aside {line-height: 280px;}
</style>
</html>
登陸成功