验证过滤器进行权限验证的原理。
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>
登陆成功