SpringMVC(4)-拦截器

123 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

目录

 一.引入

0.扩大dispatcher接收请求的范围

1.将资源放在WEB-INF下

2.给控制器添加登录功能

二.SpringMVC拦截器

1.介绍 

2.拦截器的执行原理​编辑

3.拦截器实现的两种方式

4.拦截器的实现步骤

(1)代码

(2)测试功能 


一.引入

讨论如何保护资源文件,不让外界可以直接访问资源文件。 

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用户,便可直接访问。

\