本文已参与「新人创作礼」活动,一起开启掘金创作之路。
目录
一.引入
讨论如何保护资源文件,不让外界可以直接访问资源文件。
0.扩大dispatcher接收请求的范围
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- DispatcherServlet拦截的请求,只接收*.action的请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
dispatcher接收任何请求。
在转发请求时, 如果地址栏访问demo,并且url-pattern是/,那么dispatcher接收请求,并寻找demo资源。如果访问路径是demo.action 那么也会接收请求,并寻找demo.action资源,如果找不到会寻找demo资源。
可见HandlerMapping执行了去掉后缀的功能,当后缀名不是常见的资源后缀名时,才会被去掉。
如果资源名为demo.action 访问资源时路径为demo,那么不会访问到资源。
1.将资源放在WEB-INF下
不可对外直接访问,必须经过控制器请求转发才能访问。
编辑
直接访问myFile.jsp找不到资源
编辑
先重新配置前后缀
<!-- 添加视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 配置前缀--> <property name="prefix" value="/WEB-INF/jsp/"></property> <!-- 配置后缀--> <property name="suffix" value=".jsp"></property> </bean>
@Controller public class Protect { @RequestMapping("/protect") public String protect(){ System.out.println("You find me"); return "myFile"; } }
编辑
2.给控制器添加登录功能
<body> <form action="${pageContext.request.contextPath}/protect"> <input type="text" name="account"><br> <input type="password" name="password"><br> <input type="submit" value="登录"> ${msg} </form> </body>
@Controller public class Protect { @RequestMapping("/login") public String login(){ System.out.println("访问login.jsp"); return "login"; } @RequestMapping("/show") public String file(){ return "myFile"; } @RequestMapping("/protect") public String protect(String account, String password, HttpServletRequest request){ if (account.equals("张三")&&password.equals("123")){ System.out.println("登录成功"); return "myFile"; }else { request.setAttribute("msg","输入错误"); return "login"; } } }
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> secret </body> </html>
但是如果直接访问show的话,会忽略登录功能,直接访问到保护的资源。
所以需要拦截器,如果没有登录的身份就禁止访问,返回登录页面。
二.SpringMVC拦截器
1.介绍
能够针对请求和响应进行额外,在请求和响应的过程中添加预处理,后处理和最终处理。
2.拦截器的执行原理 
编辑
preHandle()
在请求被处理之前进行操作-预处理
postHandle()
在请求被处理之后,但结果还没有渲染前进行操作,可以改变响应结果-后处理
afterCompletion()
所有的请求响应结束后执行善后工作,清理对象,关闭资源-最终处理
3.拦截器实现的两种方式
1.继承HandlerInterceptorAdapter的父类。
2.实现HandlerInterceptor接口,推荐使用实现接口的方式。因为继承是单一继承,最好把继承的位置留给业务相关的处理,而拦截器,过滤器,线程之类的一般实现其接口。
该接口含有三个方法
(1)preHandle()
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; }
该方法在处理器方法执行之前执行,返回值为true,则紧接着执行处理器方法,且会将afterCompletion()方法放入到一个专门的方法栈中等待执行。
第一个方法返回值Boolean,其他的都是void。因为只有当权限验证通过了之后才能跳到控制器执行下面的操作。
请求来的时候做权限验证,如登录验证,若登录了就放行,没有登录打回登陆界面。
(2)postHandle()
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { }
该方法在处理器方法执行之后执行,处理器方法若最终未被执行,则该方法不会执行。由于该方法是在处理器方法执行完执行,且该方法参数中包含ModelAndView,所以该方法可以修改处理器方法的处理结果数据,且可以修改跳转方向。
(3)afterCompletion()
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { }
4.拦截器的实现步骤
(1)代码
登录界面
<body>
<form action="${pageContext.request.contextPath}/protect">
<input type="text" name="account"><br>
<input type="password" name="password"><br>
<input type="submit" value="登录">
${msg}
</form>
</body>
Step1:改造登录方法,在登录之后保存到session作用域当前登录对象,便于后期验证。
@Controller public class Protect { @RequestMapping("/login") public String login(){ System.out.println("访问login.jsp"); return "login"; } @RequestMapping("/show") public String file(){ return "myFile"; } @RequestMapping("/protect") public String protect(String account, String password, HttpServletRequest request, HttpSession session){ if (account.equals("张三")&&password.equals("123")){ session.setAttribute("user","张三"); System.out.println("登录成功"); return "myFile"; }else { request.setAttribute("msg","输入错误"); return "login"; } } }
Step2:开发拦截器功能,实现HandlerInterceptor的接口,重写preHandle()方法
public class Interceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //是否登陆过的判断 if (request.getSession().getAttribute("user") == null){ request.setAttribute("msg","您还没有登录,请先登录"); request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response); //不放行 return false; } return true; } }
Step3:在springmvc.xml文件中注册拦截器
<!--注册拦截器--> <mvc:interceptors> <mvc:interceptor> <!-- 映射要拦截的请求,在这里拦截所有请求--> <mvc:mapping path="/**"/> <!-- 排除的请求,将对登录页面访问的请求排除--> <!-- 登录页面--> <mvc:exclude-mapping path="/protect"/> <!-- 直接跳转到登录页面的controller类--> <mvc:exclude-mapping path="/showlogin"/> <!-- 配置具体的拦截器实现功能的类--> <bean class="controller.Interceptor"></bean> </mvc:interceptor> </mvc:interceptors>
(2)测试功能
由于资源文件都在WEB-INF下,所以现在只能访问控制器。 所以现在拦截器只拦截访问控制器的请求。
1.请求访问show这个controller类,当没有拦截器时,会直接跳转到myFile.jsp,当有了拦截器,会先经过拦截器,拦截器发现访问的不是login.jsp和showlogin,会进行拦截验证,判断session域中的user是否为空,发现为空,请求转发到了login.jsp页面,并且显示未登录的信息。
2.请求访问protect这个controller类,会出现错误,因为不能传递对应参数。
3.正常访问,请求访问showlogin这个controller类,请求访问showlogin,拦截器不拦截该请求,转发到login.jsp页面,输入账号和密码,点击登录提交到protect,protect也不被拦截器拦截,将user用户保存到了session作用域中,并跳转到要访问的资源页面。此后,便登录成功,每当想直接访问资源时,即直接访问show这个路径时,经过拦截器拦截后发现session域有user用户,便可直接访问。
\