上篇博文我在博客中講到如何使用spring mvc框架來實現文件的上傳和下載,今天小錢給大家再來分享和介紹spring mvc框架中相當重要的一塊功能——攔截器。
關于攔截器的概念我在這里就不多說了,大家可以上網百度或者看別人寫的具體博客,我今天要說的是攔截器在實際開發中它有什么作用,怎樣用spring mvc攔截器來實現可拔插方式管理各種功能。interceptor攔截器,它的主要作用就是攔截用戶的請求并進行相應的處理。什么意思呢?比如說:通過攔截器來進行用戶的權限驗證,或者是用來判斷用戶是否已經登錄等。
spring mvc攔截器是可拔插式的設計。如果需要某個攔截器,只需要在配置文件中應用該攔截器即可,如果不需要使用攔截器,只需要在配置文件中取消其應用攔截器。不管是否應用某個攔截器,對spring mvc框架不會有任何的影響。本文將介紹handlerinterceptor接口和演示“spring mvc攔截器實現用戶權限驗證”項目案例來學習、掌握攔截器。
本項目源碼下載:spring mvc攔截器實現用戶權限驗證
handlerinterceptor接口
spring mvc中的interceptor攔截器攔截的請求是通過實現handlerinterceptor接口來完成的。在spring mvc中定義一個interceptor攔截器非常簡單,通過在要定義的interceptor攔截器類中實現spring的handlerinterceptor接口,或是繼承抽象類handlerinterceptoradapter。
handlerinterceptor接口定義了三個方法,spring mvc就是通過這三個方法來對用戶的請求進行攔截處理的,下面對這三個方法進行具體的詳解:
(1)boolean prehandle(httpservletrequest request,httpservletresponse response,object handle)。顧名思義,這個方法將在請求處理之前被調用。spring mvc中的interceptor實行的是鏈式調用,即在一個應用中或者說在一個請求中可以同時存在多個interceptor。每個interceptor的調用會依據它的聲明順序依次執行,而且最先執行的是interceptor中的prehandle方法,所以可以在這個方法中進行一些前置的初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的返回值是boolean類型的,當返回值為false時,表示請求結束,后續的interceptor和controller都不會再執行;當返回值為true時就會繼續調用下一個interceptor的prehandle方法;如果已經是最后一個interceptor,就會調用當前請求的controller方法。
(2)void posthandle(httpservletrequest request,httpservletresponse response,object handler,modelandview mv)。該方法和之后的aftercompletion方法都只能在當前所屬的interceptor的prehandle方法的返回值為true時才能被調用。posthandle方法,顧名思義,就是在當前請求被處理之后,也就是controller方法被調用之后執行,但是它會在dispatcherservlet進行視圖返回渲染之前被調用,所以我們可以在這個方法中對controller處理之后的modelandview對象進行操作。posthandle方法被調用的方向跟prehandle是相反的,也就是說先聲明的interceptor的posthandle方法反而會后執行,這和struts2里面的interceptor的執行過程類似。
(3)void aftercompletion(httpservletrequest request,httpservletresponse response,object handler,exception exception)。 該方法也是在當前所屬的interceptor的prehandle方法的返回值為true時才會執行。顧名思義,該方法將在這個請求結束之后,也就是在dispatcherservlet渲染了對應的視圖之后執行。這個方法的主要作用就是進行資源整理。
攔截器實現用戶權限驗證
本文將通過spring mvc攔截器完成一個用戶權限驗證的功能。即用戶必須登錄之后才可以訪問這個web網站的首頁,如果沒有登錄就直接訪問網站首頁,則攔截器會攔截請求,并將請求重新轉發到登錄頁面,同時提示用戶“需要先登錄再訪問網站”,由于是演示案例,所以成功登錄之后的網站頁面我們直接是拼的一個網頁顯示給用戶。本項目作為測試案例,我就不創建maven項目了,直接創建的是一個dynamic web project(動態的web項目),項目名稱為:interceptor,本項目采用tomcat 8作為web服務器,我們需要在項目中引入以下jar包,jar包我就截圖演示了,附件源碼中lib文件夾下會有,這里直接給一個項目的目錄結構,如下圖:
首先呢,我們要做一個網頁,這個網頁就是用來提示用戶的登錄信息,提示輸入用戶名和密碼,在此我們在項目的webcontent/web-inf/content文件夾中創建一個loginform.jsp,這是一個jsp文件,具體代碼如下:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<%@ page language= "java" contenttype= "text/html; charset=utf-8" pageencoding= "utf-8" %> <!doctype html public "-//w3c//dtd html 4.01 transitional//en" " http://www.w3.org/tr/html4/loose.dtd " > <html> <head> <meta http-equiv= "content-type" content= "text/html; charset=utf-8" > <title>登錄頁面</title> </head> <body> <h2>用于演示攔截器登錄頁面</h2> <form action= "login" method= "post" > <!-- 提示信息 --> <font color= "red" >${requestscope.message }</font> <table> <tr> <td><label>登錄名: </label></td> <td><input type= "text" id= "loginname" name= "loginname" ></td> </tr> <tr> <td><label>密碼: </label></td> <td><input type= "password" id= "password" name= "password" ></td> </tr> <tr> <td><input type= "submit" value= "登錄" ></td> </tr> </table> </form> </body> </html> |
這時我們需要進行處理/login的請求,我們需要寫請求的功能代碼,在src目錄下創建“cn.edu.jit.controller”包,在創建一個usercontroller類,用來處理用戶請求。具體代碼如下:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
package cn.edu.jit.controller; import javax.servlet.http.httpsession; import cn.edu.jit.domain.user; import org.springframework.stereotype.controller; import org.springframework.web.bind.annotation.requestmapping; import org.springframework.web.servlet.modelandview; /** * 處理用戶請求控制器 */ @controller public class usercontroller { /** * 處理/login請求 */ @requestmapping (value= "/login" ) public modelandview login(string loginname,string password,modelandview mv,httpsession session) { //模擬數據庫根據登錄名和密碼查找用戶,判斷用戶登錄 if (loginname != null && loginname.equals( "錢春華" ) && password != null && password.equals( "123456" )) { //模擬創建用戶 user user = new user(); user.setloginname(loginname); user.setpassword(password); user.setusername( "錢春華" ); //登錄成功,將user對象設置到httpsession作用范圍域 session.setattribute( "user" , user); //轉發到main請求 mv.setviewname( "redirect:main" ); } else { //登錄失敗,設置失敗提示信息,并跳轉到登錄頁面 mv.addobject( "message" , "登錄名或密碼錯誤,請重新輸入!" ); mv.setviewname( "loginform" ); } return mv; } } |
usercontrolle類的login方法用來處理登錄的請求,本項目沒有使用數據庫存儲數據,只是簡單的模擬了用戶的登錄,只要用戶輸入登錄名是“錢春華”,密碼是“123456”,則驗證通過,并創建一個user對象保存到httpsession當中,同時將請求使用客戶端跳轉到main請求:如果登錄失敗提示信息到modelandview對象,同時將請求使用客戶端跳轉到loginfrom請求,即登錄頁面。
同樣地,我們剛剛寫了一個用戶的登錄請求,我們接下來要處理/main請求,用于實現登錄之后給用戶顯示書的詳細定價以及出版信息,我們還在“cn.edu.jit.controller”包中創建一個名為bookcontroller類,用于處理圖書請求。具體代碼如下:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package cn.edu.jit.controller; import java.util.arraylist; import java.util.list; import cn.edu.jit.domain.book; import org.springframework.stereotype.controller; import org.springframework.ui.model; import org.springframework.web.bind.annotation.requestmapping; /** * 處理圖書請求控制器 * @author 錢春華 */ @controller public class bookcontroller { /** * 處理/main請求 */ @requestmapping (value= "/main" ) public string main(model model) { //模擬數據庫獲得所有圖書集合 list<book> book_list = new arraylist<book>(); book_list.add( new book( "java.jpg" , "java核心技術1" , "周立新 編著" , 90.00 )); book_list.add( new book( "ee.jpg" , "android第一行代碼" , "郭霖 編著" , 72.50 )); book_list.add( new book( "android.jpg" , "spring+mybatis企業應用實戰" , "李剛 編著" , 58.00 )); book_list.add( new book( "ajax.jpg" , "springmvc實戰" , "alex bretet 編著" , 99.00 )); //將圖書集合添加到model當中 model.addattribute( "book_list" , book_list); //跳轉到main頁面 return "main" ; } } |
bookcontroller類中的main方法用來處理網站首頁的請求,該方法獲得所有圖書的信息,并將它們設置到model當中,然后傳遞到main頁面。由于本案例沒有使用數據庫存儲數據,只是簡單的創建了一個集合模擬從數據庫獲取圖書信息。
下面是登錄成功后訪問網頁的main.jsp代碼,該jsp代碼位于webcontent/web-inf/content文件夾中。如下具體代碼所示:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<%@ page language= "java" contenttype= "text/html; charset=utf-8" pageencoding= "utf-8" %> <%@ taglib uri= " http://java.sun.com/jsp/jstl/core " prefix= "c" %> <!doctype html public "-//w3c//dtd html 4.01 transitional//en" " http://www.w3.org/tr/html4/loose.dtd " > <html> <head> <meta http-equiv= "content-type" content= "text/html; charset=utf-8" > <title>首頁</title> <style type= "text/css" > table{border-collapse:collapse;border-spacing: 0 ;border-left:1px solid # 888 ;border-top:1px solid # 888 ;background:#efefef;} th,td{border-right:1px solid # 888 ;border-bottom:1px solid # 888 ;padding:5px 15px;} th{font-weight:bold;background:#ccc;} </style> </head> <body> <h2 align= "center" >歡迎[${sessionscope.user.username }]訪問</h2> <br> <table border= "1" align= "center" > <tr> <th>封面</th><th>圖書名稱</th><th>作者</th><th>出版價格</th> </tr> <c:foreach items= "${requestscope.book_list }" var= "book" > <tr> <td><img src= "images/${book.image }" height= "60" ></td> <td>${book.name }</td> <td>${book.author }</td> <td>${book.price }</td> </tr> </c:foreach> </table> </body> </html> |
接下來,我將設計攔截器驗證用戶是否登錄,如果用戶沒有登錄,不可以訪問除登錄頁面和登錄請求的所有controller。我們在src文件下創建名為“cn.edu.jit.interceptor”這個包,在這個包下創建“authorizationinterceptor”類,用于演示攔截器驗證用戶是否登錄。具體代碼如下:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
package cn.edu.jit.interceptor; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; import cn.edu.jit.domain.user; import org.springframework.web.servlet.handlerinterceptor; import org.springframework.web.servlet.modelandview; /** * 攔截器必須實現handlerinterceptor接口 */ public class authorizationinterceptor implements handlerinterceptor { //不攔截"/loginform"和"/login"請求 private static final string[] ignore_uri = { "/loginform" , "/login" }; /** * 該方法將在整個請求完成之后執行, 主要作用是用于清理資源的, * 該方法也只能在當前interceptor的prehandle方法的返回值為true時才會執行。 */ @override public void aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception exception) throws exception { system.out.println( "authorizationinterceptor aftercompletion --> " ); } /** * 該方法將在controller的方法調用之后執行, 方法中可以對modelandview進行操作 , * 該方法也只能在當前interceptor的prehandle方法的返回值為true時才會執行。 */ @override public void posthandle(httpservletrequest request, httpservletresponse response, object handler, modelandview mv) throws exception { system.out.println( "authorizationinterceptor posthandle --> " ); } /** * prehandle方法是進行處理器攔截用的,該方法將在controller處理之前進行調用, * 該方法的返回值為true攔截器才會繼續往下執行,該方法的返回值為false的時候整個請求就結束了。 */ @override public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception { system.out.println( "authorizationinterceptor prehandle --> " ); //flag變量用于判斷用戶是否登錄,默認為false boolean flag = false ; //獲取請求的路徑進行判斷 string servletpath = request.getservletpath(); //判斷請求是否需要攔截 for (string s : ignore_uri) { if (servletpath.contains(s)) { flag = true ; break ; } } //攔截請求 if (!flag) { //1.獲取session中的用戶 user user = (user) request.getsession().getattribute( "user" ); //2.判斷用戶是否已經登錄 if (user == null ) { //如果用戶沒有登錄,則設置提示信息,跳轉到登錄頁面 system.out.println( "authorizationinterceptor攔截請求:" ); request.setattribute( "message" , "請先登錄再訪問網站" ); request.getrequestdispatcher( "loginform" ).forward(request, response); } else { //如果用戶已經登錄,則驗證通過,放行 system.out.println( "authorizationinterceptor放行請求:" ); flag = true ; } } return flag; } } |
我們需要在springmvc-config.xml文件中配置攔截器,配置代碼具體如下:
1
|
2
3
4
5
6
7
|
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path= "/*" /> <!-- 使用bean定義一個interceptor,直接定義在mvc:interceptors根下面的interceptor將攔截所有的請求 --> <bean class = "cn.edu.jit.interceptor.authorizationinterceptor" /> </mvc:interceptor> </mvc:interceptors> |
我們在web.xml中配置代碼如下:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
<?xml version= "1.0" encoding= "utf-8" ?> <web-app xmlns:xsi= " http://www.w3.org/2001/xmlschema-instance " xmlns= " http://xmlns.jcp.org/xml/ns/javaee " xsi:schemalocation= " http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd " id= "webapp_id" version= "3.1" > <display-name>multipartfiletest</display-name> <!-- 定義spring mvc的前端控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet- class > org.springframework.web.servlet.dispatcherservlet </servlet- class > <init-param> <param-name>contextconfiglocation</param-name> <param-value>/web-inf/springmvc-config.xml</param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet> <!-- 讓spring mvc的前端控制器攔截所有請求 --> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 編碼過濾器 --> <filter> <filter-name>characterencodingfilter</filter-name> <filter- class >org.springframework.web.filter.characterencodingfilter</filter- class > <init-param> <param-name>encoding</param-name> <param-value>utf- 8 </param-value> </init-param> </filter> <filter-mapping> <filter-name>characterencodingfilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> |
我們來部署interceptor這個web應用,在瀏覽器中輸入如下url來測試應用:
http://localhost:8088/interceptor
按enter(回車),出現以下頁面,如果沒有登錄,直接訪問main請求,攔截器會攔截請求,驗證用戶是否登錄,此時用戶若沒有登錄,則跳轉到登錄頁面。如下圖:
此時我若輸入用戶名:“錢春華”,密碼(假設輸入錯誤的密碼)為:“123123”,則攔截器會攔截請求,并將請求重新轉發到登錄頁面,同時提示用戶“需要先登錄再訪問網站”。如下圖所示:
輸入正確的用戶名“錢春華”,密碼為“123456”后,顯示用戶登錄成功,而后跳轉到網頁的首頁。如下圖所示:
本文中的一些功能案例代碼和配置文件不是很完整,下面附上完整代碼:
formcontroller類完整的代碼如下:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package cn.edu.jit.controller; import org.springframework.stereotype.controller; import org.springframework.web.bind.annotation.pathvariable; import org.springframework.web.bind.annotation.requestmapping; /** * 動態頁面跳轉控制器 */ @controller public class formcontroller { @requestmapping (value= "/{formname}" ) public string loginform( @pathvariable string formname){ //動態跳轉頁面 return formname; } } |
在src文件下創建“cn.edu.jit.domain”包,用于存放圖書和用戶的兩個實例,具體代碼如下:
book類代碼:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package cn.edu.jit.domain; import java.io.serializable; /** * * @author 錢春華 * */ public class book implements serializable{ private integer id; //id private string name; //書名 private string author; //作者 private double price; //價格 private string image; //封面圖片 public book() { super (); } public book( string image,string name, string author, double price) { super (); this .image = image; this .name = name; this .author = author; this .price = price; } public integer getid() { return id; } public void setid(integer id) { this .id = id; } public string getname() { return name; } public void setname(string name) { this .name = name; } public string getauthor() { return author; } public void setauthor(string author) { this .author = author; } public double getprice() { return price; } public void setprice( double price) { this .price = price; } public string getimage() { return image; } public void setimage(string image) { this .image = image; } @override public string tostring() { return "book [id=" + id + ", name=" + name + ", author=" + author + ", price=" + price + ", image=" + image + "]" ; } } |
user類代碼:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package cn.edu.jit.domain; import java.io.serializable; public class user implements serializable { private integer id; //id private string loginname; //登錄名 private string password; //密碼 private string username; //用戶名 public user() { super (); } public integer getid() { return id; } public void setid(integer id) { this .id = id; } public string getloginname() { return loginname; } public void setloginname(string loginname) { this .loginname = loginname; } public string getpassword() { return password; } public void setpassword(string password) { this .password = password; } public string getusername() { return username; } public void setusername(string username) { this .username = username; } @override public string tostring() { return "user [id=" + id + ", loginname=" + loginname + ", password=" + password + ", username=" + username + "]" ; } } |
springmvc-config.xml配置文件具體配置信息如下:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
<?xml version= "1.0" encoding= "utf-8" ?> <beans xmlns= " http://www.springframework.org/schema/beans " xmlns:xsi= " http://www.w3.org/2001/xmlschema-instance " xmlns:p= " http://www.springframework.org/schema/p " xmlns:mvc= " http://www.springframework.org/schema/mvc " xmlns:context= " http://www.springframework.org/schema/context " xsi:schemalocation=" http: //www.springframework.org/schema/beans http: //www.springframework.org/schema/beans/spring-beans-4.2.xsd http: //www.springframework.org/schema/mvc http: //www.springframework.org/schema/mvc/spring-mvc-4.2.xsd http: //www.springframework.org/schema/context http: //www.springframework.org/schema/context/spring-context-4.2.xsd"> <!-- spring可以自動去掃描base-pack下面的包或者子包下面的java文件, 如果掃描到有spring的相關注解的類,則把這些類注冊為spring的bean --> <context:component-scan base- package = "cn.edu.jit.controller" /> <!-- 設置默認配置方案 --> <mvc:annotation-driven/> <!-- 使用默認的servlet來響應靜態文件 --> <mvc: default -servlet-handler/> <!-- 視圖解析器 --> <bean id= "viewresolver" class = "org.springframework.web.servlet.view.internalresourceviewresolver" > <!-- 前綴 --> <property name= "prefix" > <value>/web-inf/content/</value> </property> <!-- 后綴 --> <property name= "suffix" > <value>.jsp</value> </property> </bean> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path= "/*" /> <!-- 使用bean定義一個interceptor,直接定義在mvc:interceptors根下面的interceptor將攔截所有的請求 --> <bean class = "cn.edu.jit.interceptor.authorizationinterceptor" /> </mvc:interceptor> </mvc:interceptors> </beans> |
web.xml配置文件具體配置信息如下:
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
<?xml version= "1.0" encoding= "utf-8" ?> <web-app xmlns:xsi= " http://www.w3.org/2001/xmlschema-instance " xmlns= " http://xmlns.jcp.org/xml/ns/javaee " xsi:schemalocation= " http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd " id= "webapp_id" version= "3.1" > <display-name>multipartfiletest</display-name> <!-- 定義spring mvc的前端控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet- class > org.springframework.web.servlet.dispatcherservlet </servlet- class > <init-param> <param-name>contextconfiglocation</param-name> <param-value>/web-inf/springmvc-config.xml</param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet> <!-- 讓spring mvc的前端控制器攔截所有請求 --> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 編碼過濾器 --> <filter> <filter-name>characterencodingfilter</filter-name> <filter- class >org.springframework.web.filter.characterencodingfilter</filter- class > <init-param> <param-name>encoding</param-name> <param-value>utf- 8 </param-value> </init-param> </filter> <filter-mapping> <filter-name>characterencodingfilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> |
總結:今天主要就介紹spring mvc攔截器,通過引用攔截器機制,spring mvc框架可以使用可插拔方式管理各種功能。以上就是完整的使用spring mvc攔截器實現了用戶的權限驗證,本文所有的案例都是本人親自測試,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://blog.csdn.net/qian_ch/article/details/71697626