前几天在完成一个小作业的时候,作业要求里面又一个要求是需要我们配置一个简单的登陆验证过滤器,实现的效果是:配置该LoginFilter后,1)只要通过该服务器登陆,都需要进行身份验证;2)、如果你没有登陆,就跳转到登陆页面。
我之前还没有认真地了解过关于过滤器的东西,不懂原理也不懂运行方式以及机制,所以浪费了好一些时间去了解一下,而且在参考别人的代码后写出来的过滤器竟然还有坑,最后在经过实验和思考后终于解决了这个小问题,完成了这个简单的登陆验证过滤器。
那就总结一下吧,虽然自己了解得还不够深入,但是总结一下,避免下次再次踩坑。
一、Filter基本工作原理
- 1、 首先Filter也是一个实现了特殊接口的Java类,与自己编写的其他servlet程序类似, Filter也是经由servlet容器进行调用和执行的。
- 2、当你在web.xml配置了一个Filter来进行拦截处理时,servlet将不再调用servlet本身的service方法,而是让请求先经过Filter进行处理。
- 3、此时servlet容器不再直接调用编写的Servlet的service方法,而是通过你编写的Filter过滤器中doFilter中的规则进行处理,然后调用FilterChain.doFilter方法来激活目标Servlet的方法。
好了,我们现在粗略了解了Filter是如何工作的,把里面一些比较重要的关键字拿出来:
web.xml,在服务器中初始化配置信息,你的Filter或许也需要在里面配置(当然也也可以不在里面配)
service( ),调用你写的servlet时需要的方法
doFilter( ),你制定的规则都在这个doFilter方法里面了
FilterChain.doFilter( ),字面意思理解就是过滤器链,请求会在里面经过一个个Filter的规则处理最后成为你想要的样子,当这个过滤器是其中最后一个的时候,那么就会把请求提交给Servlet程序进行处理。
二、登陆过滤器LoginFilter
实现效果:进去浏览器,只要不是通过登陆界面登陆进入,都重定向到登陆页面。
初始思路:
1、只要url是登陆页面的地址都可以通过;
2、在session中检测有没有成功登录的user存在,有就通过,没有就重定向。
先贴一下出错时的LoginFilter.java代码:
LoginFilter.java
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author OkaMe1o
*/
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//获取页面的session
HttpSession session = request.getSession();
//把url转化为字符给后面判断
String url = request.getRequestURL().toString();
//如果url是登陆页面的地址就放行
if (url.startsWith("http://localhost:8080/login.jsp") || url.startsWith("http://localhost:8080/loginServlet") ) {
//在这里把请求放行给servlet处理
filterChain.doFilter(request, response);
return;
}
//获取session中成功登陆的qUser对象,如果存在就可以获取,不存在就获取不到
User qUser = (User) request.getSession().getAttribute("qUser");
//检测成功登陆的去qUser的存在,如果没获取到说明还没登陆。
if (qUser == null) {
//本来想弹个警告框但是弹不出来,还没解决
PrintWriter out = response.getWriter();
out.print("<script>alert('请先登录');top.location.href='login.jsp'</script>");
//进行重定向操作,回到登陆页面
response.sendRedirect(request.getContextPath()+"/login.jsp");
return;
}
//如果上面的
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
LoginServlet.java
这里是登陆的servlet代码,里面的核心就是在登陆成功后把成功登陆的用户放入session中,在整个登陆期间都存在里面。
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @ClassName LoginServlet
* @Description TODO
* @Author OkaMe1o
* @Date 2019/8/13,17:32
* @Version 1.0
*/
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
loginIn(req, resp);
}
private void loginIn(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
User user = new User();
PrintWriter out = resp.getWriter();
String errMsg = "";
try {
if (!"".equals(req.getParameter("username"))){
user.setUsername(req.getParameter("username"));
}
else {
throw new RuntimeException("用户名不可为空!");
}
if (!"".equals(req.getParameter("password"))){
user.setPassword(req.getParameter("password"));
}
else {
throw new RuntimeException("密码不可为空!");
}
} catch (RuntimeException e) {
e.printStackTrace();
errMsg =e.getMessage();
out.println("<script type='text/javascript'>alert('登陆失败!" + errMsg + "');history.back();</script>");
}
MatchUser mu = new MatchUser();
//数据库查询出来的user
User qUser = mu.matchUser(user);
if (!user.getUsername().equals(qUser.getUsername())){
out.println("<script type='text/javascript'>alert('用户不存在!');history.back();</script>");
//思考加入询问用户需不需要注册
}
//进行密码判断
else {
if (!user.getPassword().equals(qUser.getPassword())){
out.println("<script type='text/javascript'>alert('密码错误!');history.back();</script>");
}
//登陆页面并且把对象加入到session中,然后跳转到下个页面
else{
HttpSession session = req.getSession();
session.setAttribute("qUser",qUser);
out.println("<script type='text/javascript'>alert('成功登陆!!!');location.href='showAllGoods.jsp'</script>");
}
}
}
}
web.xml文件中Filter的配置
这是Filter在web.xml中的配置,拦截目标是 /*,即面对所有的页面请求都生效。
<!--过滤器的servlet-->
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.servlet.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
三、错误效果
运行起来的效果如图:
在这个界面就是我写的登陆页面,填写正确的用户名和密码后就可以登陆,在输入错误或者是没有填写框内信息点击 ‘登陆‘ 按钮时都有对应的错误提示框,点击 ’注册‘按钮会跳转到登陆页面。
但是现在的情况是无论点击哪个按钮,什么情况,都会在这个登录页面一直停留。
四、排查情况
在初步查资料了解Filter的基本原理之后,大概明白过滤器怎么运行之后,我就开始思考请求的提交路径。
login.jsp --> LoginFilter.java --> LoginServlet.java --> 其他操作
怎么知道执行到哪里了呢?对于像我这样的初学者来说,最简单的就是观察地址栏中的url的变化,所以在过滤器的规则中,这个判断条件:
//把url转化为字符给后面判断
String url = request.getRequestURL().toString();
//如果url是登陆页面的地址就放行
if (url.startsWith("http://localhost:8080/login.jsp") || url.startsWith("http://localhost:8080/loginServlet") ) {
//在这里把请求放行给servlet处理
filterChain.doFilter(request, response);
return;
}
这个规则中,放行的页面就只有login.jsp的页面以及和login的servlet,对于登陆来说应该足够了。但是在我检查自己的代码结构后发现,我的代码结构中是存在一个分发的servlet,即判断各个页面提交的opr动作,再转发到对应的servlet进行处理。
下面是我servlet的组成截图:
这里面的逻辑是通过LoginServlet或者SignServlet登陆的行为要通过Divide的判断,再把请求转发到对应的Servlet执行请求。所以在地址栏跑过的url地址至少有这几个servlet以及对应的.jsp文件
所以在规则中只放行LoginServlet是不足够的,还需要放行相关的servlet和页面。故在拦截规则中加入,修改后的代码如下:
String url = request.getRequestURL().toString();
//如果url是登陆页面的地址就放行
if (url.startsWith("http://localhost:8080/login.jsp") || url.startsWith("http://localhost:8080/loginServlet") || url.startsWith("http://localhost:8080/signUp.jsp") || url.startsWith("http://localhost:8080/signServlet") || url.startsWith("http://localhost:8080/divide")||url.substring(url.lastIndexOf(".")+1).equals("css")||url.substring(url.lastIndexOf(".")+1).equals("js")) {
//在这里把请求放行给servlet处理
filterChain.doFilter(request, response);
return;
}
修改后的代码,让登陆过程中经过的文件都放行,那么就能顺利完成登陆效果。
五、总结
整个过滤器处理请求的重点就在于FilterChain中的规则处理,加入放行名单的那些文件和页面,应该是你达到目标前(如登陆过滤器就是拦截全部未登录的用户,放行已登陆的用户,目标是登陆后进入系统)所要经过的路径中执行的servlet和页面。
因为了解得并不深入,所以文章可能存在错误以及说的不清楚的地方,如果有人看到错误,以及有哪些地方需要更正,请务必告诉我,我才能从中学到更多,谢谢
—19/08/16