【Spring MVC】Spring MVC,阻止直接訪問jsp,使用Interceptor登入攔截
問題描述:
在Java Web專案中,使用者可訪問Url一般只有一個,即index或login。而使用者的其他Url請求都會引導到index頁。如何來避免未登入使用者直接訪問Spring的Conroller和jsp檔案?
解決方案:
一、阻止使用者訪問jsp。
Spring的MVC模式是不提倡直接通過URL形式訪問.jsp頁面的,建議通過Controller跳轉至View頁面。
把jsp檔案放在WEB-INF目錄下,js和css等資原始檔放在WEB-INF的同級目錄下,WEB-INF對使用者不可見,可以起到隔離作用。
同時修改mvc配置檔案,配置jsp的路徑。
1 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 2 <property name="prefix" value="/WEB-INF/jsp/" /> 3 <property name="suffix" value=".jsp" /> 4 </bean>
配置資原始檔夾的路徑,以便jsp能訪問到所引用的靜態資原始檔。
<mvc:resources mapping="/**" location="/resources"/>
二、Interceptor登入攔截
Spring攔截器介紹:
攔截器HandlerInterceptor介面有三個回撥方法
1.preHandle方法,顧名思義,該方法將在請求處理之前進行呼叫。SpringMVC 中的Interceptor 是鏈式的呼叫的,在一個應用中或者說是在一個請求中可以同時存在多個Interceptor 。每個Interceptor 的呼叫會依據它的宣告順序依次執行,而且最先執行的都是Interceptor 中的preHandle 方法,所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的返回值是布林值Boolean型別的,當它返回為false 時,表示請求結束,後續的Interceptor 和Controller 都不會再執
2.postHandle方法,由preHandle 方法的解釋我們知道這個方法包括後面要說到的afterCompletion 方法都只能是在當前所屬的Interceptor 的preHandle 方法的返回值為true 時才能被呼叫。postHandle 方法,顧名思義就是在當前請求進行處理之後,也就是Controller 方法呼叫之後執行,但是它會在DispatcherServlet 進行檢視返回渲染之前被呼叫,所以我們可以在這個方法中對Controller 處理之後的ModelAndView 物件進行操作。postHandle 方法被呼叫的方向跟preHandle 是相反的,也就是說先宣告的Interceptor 的postHandle 方法反而會後執行。
3.afterCompletion方法,該方法也是需要當前對應的Interceptor 的preHandle 方法的返回值為true 時才會執行。顧名思義,該方法將在整個請求結束之後,也就是在DispatcherServlet 渲染了對應的檢視之後執行。這個方法的主要作用是用於進行資源清理工作的。
1 package org.springframework.web.servlet; 2 public interface HandlerInterceptor { 3 boolean preHandle( 4 HttpServletRequest request, HttpServletResponse response, 5 Object handler) 6 throws Exception; 7 8 void postHandle( 9 HttpServletRequest request, HttpServletResponse response, 10 Object handler, ModelAndView modelAndView) 11 throws Exception; 12 13 void afterCompletion( 14 HttpServletRequest request, HttpServletResponse response, 15 Object handler, Exception ex) 16 throws Exception; 17 }
寫自己的攔截器:
1 public class LoginInterceptor extends HandlerInterceptorAdapter { 2 private List<String> excludedUrls; 3 public void setExcludeUrls(List<String> excludeUrls) { 4 this.excludedUrls = excludeUrls; 5 } 6 @Override 7 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 8 String requestUri = request.getRequestURI(); 9 for (String url : excludedUrls) { 10 if (requestUri.endsWith(url)) { 11 return true; 12 } 13 } 14 HttpSession session = request.getSession(); 15 if (session.getAttribute("login")==null) { 16 throw new WebAuthException(); 17 } else { 18 return true; 19 } 20 } 21 }
HandlerInterceptorAdapter是介面卡模式的介面,這裡允許我們只實現一個方法即可。excludedUrls中聲明瞭不需要攔截的URL,如果訪問的URL以指定字串結尾,preHandle將返回true,他們將不被攔截而直接訪問到對應的Controller。宣告方式可以在Spring配置檔案中,如下
1 <mvc:interceptors> 2 <mvc:interceptor> 3 <mvc:mapping path="/*"/> 4 <bean id="loginInterceptor" class="LoginInterceptor"> 5 <property name="excludeUrls"> 6 <list> 7 <value>/index</value> 8 9 <value>/</value> 10 </list> 11 </property> 12 </bean> 13 </mvc:interceptor> 14 </mvc:interceptors>
也就是說“http://xxxxxxxxxx/index”,“http://xxxxxxxxxx/login”,“http://xxxxxxxxxx/”是不會被攔截的。
如果URL不以指定字串結尾,那麼將判斷session中是否已經有登入資訊(使用者是否已經登入),如果登入了,正常跳轉到Controller。如果沒有登入則丟擲異常。該異常是自定義的:
1 public class WebAuthException extends Exception { 2 }
然後再配置檔案中指定,丟擲異常時,重定向到登入頁。
1 <bean id="handlerExceptionResolver" 2 class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> 3 <property name="exceptionMappings"> 4 <props> 5 <prop key="com.thisone.apiserver.exception.WebAuthException">redirect:/index</prop> 6 </props> 7 </property> 8 </bean>
注意,如果沒有redirect:,則直接跳轉至名為index.jsp的頁面。有redirect則會由Controller中@RequestMapping(value="/login")的方法來處理它。
1 @Controller 2 public class SystemController { 3 @RequestMapping(value = {"/index","/"}, 4 method = RequestMethod.GET) 5 public String home() { 6 return "login"; //返回到login.jsp登入頁,而不是Controller的方法。 7 } 8 }
1 @RequestMapping(value = "/login", 2 method = RequestMethod.POST) 3 public String login(HttpServletRequest request, 4 @RequestParam String loginname, 5 @RequestParam String password) throws Exception { 6 if (!loginname.equals("xxxx")) { 7 request.getSession().setAttribute("loginReply", "使用者名稱錯誤, 請重新登入"); 8 return "login"; 9 } 10 if (!password.equals("xxxx")) { 11 request.getSession().setAttribute("loginReply", "密碼錯誤, 請重新登入"); 12 return "login"; 13 } 14 request.getSession().setAttribute("login", "yes"); 15 return "welcome"; 16 }
這樣,使用者只能通過/index,/,三個URL字尾來正常訪問到頁面,其他都是會重定向到/index,再有Cntroller方法引導至login.jsp。
參考: