前面學習過過濾器, 可是過濾器是針對servlet的, 用在springmvc和spring boot裏面, 功能上, 感受並非很好用.javascript
那這裏來學習一下攔截器.html
一. 攔截器的執行順序java
1. 目錄jquery
2. 攔截器web
攔截器裏面, 我加了三個(First,Two,Third), 可是內容都差很少.ajax
package org.elvin.boot.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class FirstInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { System.out.println("FirstInterceptor preHandle"); return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { System.out.println("FirstInterceptor postHandle"); } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { System.out.println("FirstInterceptor afterCompletion"); } }
preHandle 返回true, 纔會繼續下面的執行.spring
攔截器註冊:瀏覽器
package org.elvin.boot.interceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @Configuration public class RegisterInterceptor extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new FirstInterceptor()); registry.addInterceptor(new TwoInterceptor()); registry.addInterceptor(new ThirdInterceptor()); super.addInterceptors(registry); } }
爲了驗證執行順序, 這裏使用了 thymeleaf, 而後在前臺訪問了我後臺傳過去的屬性, 在訪問的時候, 就會打印信息到控制檯session
package org.elvin.boot.pojo; public class Book { private String name ; public String getName() { System.out.println("view : Book'name is " + name); return name; } public void setName(String name) { this.name = name; } }
Controller:mvc
package org.elvin.boot.Controller; import org.elvin.boot.pojo.Book; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("first") public class FirstController { private String controllerPath = "first/"; @GetMapping("index") public String index(Model model){ System.out.println("controller : FirstController index doing..."); Book book = new Book(); book.setName("spring boot"); model.addAttribute("book", book); return controllerPath + "index"; } }
View:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Title</title> </head> <body> <h1 th:text="${book.name}"></h1> </body> </html>
在訪問 localhost:8080/first/index 的時候, 就會在控制檯輸出響應的信息.
這樣, 就能看出單個攔截器的執行順序.
1. 在控制器方法執行以前, 執行的 preHandle 方法
2. 執行控制器的action方法
3. 執行完action, 解析view以前(若是有的話), 執行攔截器的 posthandle 方法
4. 解析view
5. 解析完以後, 執行 afterCompletion 方法
當註冊多個攔截器的時候, 執行順序, 如圖上所示了.
二. 攔截器實現權限驗證
一樣的, 先加入權限攔截器
package org.elvin.boot.interceptor; import org.elvin.boot.annotation.NoLogin; import org.springframework.util.StringUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handle) throws Exception { HandlerMethod method = (HandlerMethod ) handle; Class<?> controllerType = method.getBeanType(); if(method.getMethodAnnotation(NoLogin.class) != null || controllerType.getAnnotation(NoLogin.class) != null){ return true; } HttpSession session = request.getSession(); String token = (String)session.getAttribute("token"); if(!StringUtils.isEmpty(token)){ return true; } response.sendRedirect("/login/index"); return false; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
而後註冊權限攔截器
package org.elvin.boot.interceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @Configuration public class RegisterInterceptor extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()); super.addInterceptors(registry); } }
在控制器中加入登陸控制器, 提供登陸頁面和註銷方法
package org.elvin.boot.Controller; import org.elvin.boot.annotation.NoLogin; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @NoLogin @Controller @RequestMapping("login") public class LoginController { @Autowired private HttpServletRequest request; @Autowired private HttpServletResponse response; private String controllerPath = "login/"; //@NoLogin @GetMapping("index") public String index(){ HttpSession session = request.getSession(); session.setAttribute("token", "token"); return controllerPath + "index"; } //@NoLogin @PostMapping("checkOut") @ResponseBody public String checkOut(){ HttpSession session = request.getSession(); session.setAttribute("token", null); return "ok"; } }
這裏我作了一個免登陸註解, 能夠加在Controller上, 也能夠加在 action 上.
package org.elvin.boot.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface NoLogin { }
註解裏面, 並不須要任何內容.
登陸頁面(這裏登陸頁面只是爲了註銷用的, 因此訪問過這個頁面以後, 就表示登陸成功了).
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> <div class="container"> <input type="button" value="註銷" id="checkOut"/> </div> <script th:src="@{/js/jquery-1.11.1.js}"></script> <script th:inline="javascript"> $(function () { $(".container").delegate("#checkOut", "click", function () { $.ajax({ url: [[@{/login/checkOut}]], type: 'post', data: {}, success: function (res) { if (res == "ok") { alert("註銷成功"); } } }); }); }); </script> </body> </html>
結果演示方式:
在瀏覽器中, 先打開 http://localhost:8080/login/index 頁面, 而後在新標籤中訪問 http://localhost:8080/first/index 頁面.
你會發現訪問 first/index 的時候, 是能夠訪問的.
此時, 在login/index頁面中, 點擊註銷按鈕以後, 再刷新 first/index 頁面, 就會直接跳去登陸頁面.