1. 程式人生 > >Spring中利用攔截器控制登入及頁面跳轉

Spring中利用攔截器控制登入及頁面跳轉

一、問題簡要描述:

在做web開發的時候往往會遇到這種情況,使用者在沒登入的情況下訪問一些需要身份驗證的頁面,系統會自動幫使用者跳轉到登入頁面,使用者登入成功後,不會返回一個固定的頁面,系統會跳轉到使用者之前訪問的頁面,使用者可以繼續進行剛才的操作。或者是session裡面儲存的使用者資訊過期了,使用者需要重新進行身份驗證(重新登入),使用者登入成功後,頁面還是會回到之前訪問的頁面。

二、問題所涉及的知識和技術:

Shiro,Spring,SpringMVC,攔截器,java,Servlet

三、問題解決

這個問題我現在知道的有兩種解決辦法(本人能力有限),一種是利用SpringMVC裡面的攔截器(實現HandlerInterceptor介面),一種是使用Shiro外掛(不久我會整理一份《Apache Shiro簡介》和《Spring整合Shiro詳解》,具體可檢視我的這兩個博文)

1、實現HandlerInterceptor介面從而實現控制登入及頁面跳轉

HandlerInterceptor介面中定義了三個方法,我們就是通過這三個方法來對使用者的請求進行攔截處理的。

(1 )preHandle(HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顧名思義,該方法將在請求處理之前進行呼叫。SpringMVC 中的Interceptor是鏈式的呼叫的,在一個應用中或者說是在一個請求中可以同時存在多個Interceptor 。每個Interceptor的呼叫會依據它的宣告順序依次執行,而且最先執行的都是Interceptor 中的preHandle方法,所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的返回值是布林值Boolean 型別的,當它返回為false時,表示請求結束,後續的Interceptor 和Controller都不會再執行;當返回值為true 時就會繼續呼叫下一個Interceptor 的preHandle 方法,如果已經是最後一個Interceptor 的時候就會是呼叫當前請求的Controller方法。

   (2 )postHandle (HttpServletRequest request,HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解釋我們知道這個方法包括後面要說到的afterCompletion 方法都只能是在當前所屬的Interceptor的preHandle 方法的返回值為true時才能被呼叫。postHandle 方法,顧名思義就是在當前請求進行處理之後,也就是Controller 方法呼叫之後執行,但是它會在DispatcherServlet進行檢視返回渲染之前被呼叫,所以我們可以在這個方法中對Controller 處理之後的ModelAndView物件進行操作。postHandle 方法被呼叫的方向跟preHandle是相反的,也就是說先宣告的Interceptor 的postHandle方法反而會後執行,這和Struts2 裡面的Interceptor的執行過程有點型別。Struts2 裡面的Interceptor的執行過程也是鏈式的,只是在Struts2 裡面需要手動呼叫ActionInvocation的invoke 方法來觸發對下一個Interceptor或者是Action 的呼叫,然後每一個Interceptor中在invoke 方法呼叫之前的內容都是按照宣告順序執行的,而invoke 方法之後的內容就是反向的。

   (3 )afterCompletion(HttpServletRequest request,HttpServletResponse response, Object handle, Exception ex) 方法,該方法也是需要當前對應的Interceptor 的preHandle 方法的返回值為true 時才會執行。顧名思義,該方法將在整個請求結束之後,也就是在DispatcherServlet 渲染了對應的檢視之後執行。這個方法的主要作用是用於進行資源清理工作的。

下面給出具體的程式碼:(具體思路是這樣的,在preHandle這個方法中,獲得請求的地址(具體讀者可以瞭解專案地址,真實地址,帶參地址)然後把它儲存在session中,最後在你認證登入的時候作一個判斷,取出這個session裡面的地址,進行登入跳轉)

        @Override
	public void afterCompletion(HttpServletRequest arg0,
			HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		
		
	}
	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
			Object arg2, ModelAndView arg3) throws Exception {
		
		
	}
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
			Object arg2) throws Exception {
		
		User user = (User) request.getSession().getAttribute("loginUser");
		if(user == null){
			request.getRequestDispatcher("/login.jsp").forward(request, response);  
			HttpSession session = request.getSession(true); 
	    	String uri = request.getRequestURI();//拿到上一個頁面地址
	    	String path = uri.substring(request.getContextPath().length());//去掉專案地址長度的字元(因為我的預設專案地址是給出的)
	    	String query = request.getQueryString();//得到引數
	    	if(query == null) {
	    		query = "";
	    	}
	    	String realPath = path+"?"+query;
	    	System.out.println(uri+"?"+query);//測試用
	    	System.out.println(realPath);//測試用
	    	session.setAttribute("realPath", realPath);
			return false;
		}
		return true;
	}
}

2、使用Shiro外掛實現

如果你的專案裡面集成了Shiro安全框架,那麼恭喜你,你可以很方便的實現控制登入及頁面跳轉,我也推薦使用這個方法,因為Shiro實在是太強大了,讀者可以閱讀我的另外兩個博文(即將發出):《Apache Shiro簡介》和《Spring整合Shiro詳解》。先來介紹這個,Shiro裡面有一個方法,可以得到跳轉到登入頁面之前的頁面的地址SavedRequest savedRequest =WebUtils.getSavedRequest(request);是不是很方便。

具體程式碼如下(因為牽扯的太多,所以只講述這一塊,如果想深入瞭解,可以閱讀我的其他博文,謝謝):

這段是你的專案裡面的認證控制器

        @SysLog(operation="登入")
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public String login(String username, String password, String remember, String captcha, Model model, HttpSession httpSession,HttpServletRequest request) throws IOException {
		httpSession.setAttribute("username", username);
		User user = userService.getByUsername(username);
		httpSession.setAttribute("loginUser", userService.getByUsername(username));
		
		SavedRequest savedRequest = WebUtils.getSavedRequest(request);
		if(savedRequest == null){
			if(user.getUserType() == UserType.admin) {
				return "redirect:/admin/dashboard";
			}
			return "redirect:/user";
		}else {
			String url = savedRequest.getRequestUrl().substring(request.getContextPath().length());
			return "redirect:"+url;
		}
	}

作者宣告:寫這篇文章的初衷旨在不忘記一些工作中需要的知識點和技能,方便以後查閱,也方便有需要的同行拿去參考。以上文字和圖片都不是本人敲打上去的(除了本段),因為沒那時間和精力,也沒必要。東湊西拼的東西好用就行,也能發揮網際網路共享的理念,我也做一個不辭辛苦的搬運工。以上內容的原文如有,“轉載註明原文出處”,還望見諒沒能實現,早已忘記是從哪裡摘錄。此致!奮鬥在猿類世界的小晨。