目录
概述
解决需求:在用户未登录的情况下访问未登录不可访问的页面时,请求将被过滤到,将用户退回登录页面。
技术选型
前端:Vue + ElementUI
后端:SpringBoot + MyBatisPlus
说明
1.本篇博客不真正连接后台数据库,以及仅做粗糙的前端页面做演示,重点说明的是登录请求过滤器使用,以及实现当未登录用户访问非登录页面时将返回到登录页面的功能完善。
2.如果仅想了解过滤器的书写形式和用法,可以在目录点击"过滤器的编写"查看,不必看完全部篇章的实验细节。
前端编写
页面展示
说明:页面仅做两个简易的页面作为演示,一个是登录页面,一个是管理页面(未登录访问将退回登录页面)。
登录页面
当点击"登录按钮时,会向后台登录接口发送一个请求",验证账号和密码是否匹配成功,当匹配成功后将session添加上,代表为登录状态。
代码片段:
<script>
//获取DOM节点
let loginBtn = document.querySelector("#loginBtn"); // 登录按钮
let idInput = document.querySelector("#id"); // 账号输入框
let passwordInput = document.querySelector("#password"); // 密码输入框
loginBtn.addEventListener("click",function (){
//获取输入框id和password
let id = idInput.value;
let password = passwordInput.value;
// 发送请求
axios.post("/logins?id="+id+"&password="+password).then(function (res){
//判断后端响应信息登录状态提示是否为:“登录成功”
if (res.data === "登录成功"){
// 登录成功,页面跳转到数据管理页面
window.location = "/manage.html"
}else {
alert("账号密码不匹配")
}
})
})
</script>
说明:代码中的接口下面会有代码编写片段,已经提前准备好。
管理页面
访问此页面,当vue数据加载完毕后,会自动执行钩子函数mount(),在其中向后台接口发送请求,获取分页数据,当前页面的"王*虎"是虚拟数据,并不是真实的从后台获取的数据,分页接口就是模拟向后台发送请求获取真实的数据。并且获取返回值提示,判断是否获取到真实数据。
代码片段
后端编写
实验中后台编写就是需要编写接口和过滤器,接口是为了向前端提供请求路径,给过滤器过滤这些请求,用以判断用户在访问管理页面时是否登录过。
编写接口
需求分析
1.登录接口:我们需要提供当用户点击登录后访问登录接口,将session添加上,代表用户登录了。
2.获取分页数据接口,当访问到管理页面时,后台钩子函数会自动向分页接口发送请求获取数据在页面进行展示,此接口作用:做为拦截的请求。
说明:登录接口中不向后台数据库真实验证账号密码,提供虚拟数据进行验证作为演示,获取分页数据接口中不向数据库真实获取分页数据,仅向后台发送请求作为演示,作为可以被过滤到的请求,过滤到后如果未登录就退回登录页面。
登录接口
@RestController
@ResponseBody
@RequestMapping("/logins")
@Slf4j
public class LoginController {
/**
* 判断ID和密码是否匹配,匹配成功登录成功
* @param id 账号
* @param password 密码
* @param httpServletRequest 用户操作session
* @return 返回登录提示
*/
@PostMapping
public String loginCheck(int id, String password, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){
log.info("id:{}<--->password{}",id,password);
//设置虚拟ID和密码(在此模拟数据库中的登录数据)
int myId = 1;
String myPassword = "aa";
//判断传入id和password是否和虚拟登录数据匹配(仅作演示,真实情况需要访问数据库进行匹配验证)
if (id == myId && password.equals(myPassword)){
//登录数据匹配,登录成功
//将session添加上(登录成功的标志)
httpServletRequest.getSession().setAttribute("login","ok");
return "登录成功";
}
return "登录失败";
}
}
获取分页数据接口
@RestController
@RequestMapping("/users")
@Slf4j
public class UserController {
/**
* 模拟获取分页数据
* @return 返回提示信息
*/
@GetMapping
public String returnPageData(){
log.info("分页数据请求...");
return "我是模拟数据";
}
}
过滤器的编写
经过上面的实验演示,当我们在登录页面输入正确的账号密码登录成功后,后台会为用户添加上session,以作为登录过的标志,并且自动跳转到数据管理页面,但是如果用户未登录,直接通过url访问数据管理页面,这时也是可以访问成功的。这并不符合我们的预期。
过滤器编写步骤
1.启动类添加注解@servletComponentScan
2.创建自定义过滤器类并完善过滤逻辑
过滤逻辑
1.当用户访问登录页面时,或登录检验等类似请求时,我们选择直接放行。
2.当用户访问的不是直接放行的请求,那么我们检查用户的session,判断用户使用登录过,如果有session,那么我们放行,否则退回登录页面。
代码实现一:添加注解
@SpringBootApplication
// 程序组件扫描,用于扫描到我们的自定义过滤器
@ServletComponentScan
public class BlogLoginFilterApplication {
public static void main(String[] args) {
SpringApplication.run(BlogLoginFilterApplication.class, args);
}
}
代码实现二:编写自定义过滤器
package com.mh.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Date:2023/1/25
* author:zmh
* description: 自定义登录过滤器
**/
// 定义过滤器名称和定义过滤路径
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter { // 实现javax.servlet下的Filter
//1.定义路径匹配器--用于下方的checkPath()方法匹配请求路径和放行路径是否存在匹配(固定写法)。
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
// 重写其中的doFilter方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//2.将参数servlet转为http形式,因为下面要操作session
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//3.获取当前访问的URL,用以判断是否直接放行
String requestURI = request.getRequestURI();
log.info("当前的URI是:{}",requestURI);
//4.定义直接放行的资源请求(根据自己实际情况定义放行的资源)
String[] urls = new String[]{
//登录页面的请求
"/logins",
//下方访问静态页面可以放行,但是在静态页面发送的分页数据请求是会过滤的
//例如:当一访问manege页面就会请求获取分页数据,就会向/users发送
//请求,然会就会进行登录检查
"/element-ui/**",
"/html/**",
"/JS/**",
"/favicon.ico"
};
//步骤5在最下面(定义方法)
//6.检查当前请求是否需要进行过滤(true代表匹配成功,直接放行)
if(checkPath(urls,requestURI)){
//如果匹配成功,则放行
filterChain.doFilter(request,response);
log.info("资源直接放行...");
return;
}
//7.捕获到需要过滤的请求,检查是否有登录后赋予的session(检查是否已登录)
if(request.getSession().getAttribute("login") != null){ //“login”是登录接口定义的session名
filterChain.doFilter(request,response);
log.info("用户已登录...");
return;
}
//8.如果无session,登录失败,可以给予前端一些提示
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("请先登录");
log.info("请求过滤成功,登录失败...");
}
//5.定义路径匹配方法,传入放行请求路径数组和当前请求路径进行匹配。
/**
* 路径匹配方法
* @param urls 需要放行的请求路径数组
* @param targetUrl 需要检查的请求路径
* @return 返回是否匹配成功
*/
private Boolean checkPath(String[] urls,String targetUrl){
for (String url : urls) {
if (PATH_MATCHER.match(url, targetUrl)){
return true;
}
}
return false;
}
}
过滤器功能实验
我们知道,当用户在未登录的情况下直接访问"数据管理页面",我们是不允许访问此页面的以及此页面中的"真实数据"的(王大虎,王小虎这些属于虚拟数据并不是从后台获取的数据)。
如果我们在登录后访问"据管理页面",那么我们就会看到真实从后台返回的数据,但是因为在此做的是模拟操作,只要在访问"数据管理页面"后就会向接口/users发送请求获取分页数据,如果未登录(无session)那么向接口/users的请求就不会被过滤器放行,后台的日志就输出提示并且前端页面退回登录页面,反之登录后,有了session代表已经登录,那么请求会被放行,后台会有相应提示,相当于是模拟了从后台获取数据给到了前端。
但在此并不向后台获取真实数据,仅做模拟,因此不向前端表格中做渲染了。
未登录时直接访问“数据管理页面”
会看到请求"/users"被过滤器过滤了,请求"/users"并没有进入方法体,因此不会返回"我是模拟数据"字符串,导致前端页面自动跳转到登录页面,因为如果进入了控制台会有相应提示,再看看接口中的定义:
我们输入正确账号密码登录,自动跳转到数据管理页面
可以看到图中红框,登陆后请求被放行,确实是从后台获取到了数据。
在登录的情况下直接访问数据管理页面
可以看到,因为登录过了存在session,所以请求也会被过滤器放行,获取到了分页数据。