標籤:spring security login ajax
原創文章,歡迎轉載!轉載時務必保留:作者:jmppok ;出處http://blog.csdn.net/jmppok/article/details/44832641
1.問題
在一個Web項目中一般會有兩部分組成:
1)靜態資源, 如html頁面, js指令碼,圖片等等.
2)API介面。
在進行許可權控制時,需要對這兩部分進行統一管理。
Spring架構本身提供了強大的Security機制,但是在使用過程中還是會出現以下問題:
當使用者訪問一個頁面時,頁面本身經常是既包含靜態資源,又需要調用API介面。
大家都知道,如果Spring Security判斷目前使用者沒有許可權訪問某個資源時,會根據我們的配置自動跳轉到Login頁面或者403頁面。
但實際上這可能並不是我們想要的:因為對於靜態資源來說,瀏覽器一般會進行緩衝,一旦緩衝後就不會再向伺服器請求,也就是說即使沒有登陸或許可權,靜態頁面也有可能被顯示出來;但這時候對服務段的API調用可能是失敗的。如前面所說,API調用失敗時,會自動調轉到會根據我們的配置自動跳轉到Login頁面或者403頁面(注意這裡是一個頁面),而這並不是我們想要的結果。
因為對於API的服務調用來說,應該返回一個Json或者xml的結果,通過結果來判斷調用成功還是失敗以及失敗原因是沒有登陸或沒有許可權。然後根據調用失敗資訊,再判斷是否跳轉到登陸頁面。而不是在調用API時由後台直接來返回一個Login.html或403.jsp.
2.解決辦法
1)配置統一的許可權管理
在applicationContext-security.xml中配置:
<http auto-config="false" entry-point-ref="myAuthenticationProcessingFilterEntryPoint" ><intercept-url pattern="/login**" access="IS_AUTHENTICATED_ANONYMOUSLY" /><intercept-url pattern="/js/**" access="IS_AUTHENTICATED_ANONYMOUSLY" /><intercept-url pattern="/images/**" access="IS_AUTHENTICATED_ANONYMOUSLY" /><intercept-url pattern="/**" access="ROLE_USER" /> <form-login login-page="/login.html" authentication-failure-url ="/loginfailure" default-target-url="/loginsuccess"/><logout invalidate-session="true" logout-success-url="/login.html" logout-url="/j_spring_security_logout"/><access-denied-handler ref="myAuthenticationFailureHandler"/></http><beans:bean id="myAuthenticationProcessingFilterEntryPoint" class="com.lenovo.MyAuthenticationProcessingFilterEntryPoint" ><beans:property name="loginFormUrl" value="/login.html"></beans:property></beans:bean><pre name="code" class="html"><beans:bean id="myAuthenticationFailureHandler" class="com.lenovo.MyAuthenticationFailureHandler" /><!-- 認證管理器。使用者名稱密碼都整合在設定檔中 --><authentication-manager ><authentication-provider><user-service><user name="admin" password="admin" authorities="ROLE_USER" /></user-service></authentication-provider></authentication-manager>
主要包括以下兩點:
1)自訂EntryPoint,在Spring自動跳轉到登陸節目時,進行自己的處理
entry-point-ref="myAuthenticationProcessingFilterEntryPoint"
<beans:bean id="myAuthenticationProcessingFilterEntryPoint" class="com.lenovo.MyAuthenticationProcessingFilterEntryPoint" ><beans:property name="loginFormUrl" value="/login.html"></beans:property></beans:bean>
MyAuthenticationProcessingFilterEntryPoint 繼承 LoginUrlAuthenticationEntryPoint 並重寫其commence方法。
在commence中對請求進行判斷, 比如判斷是否ajax請求(代碼中的視線)。 也可以判斷是否某個URL,如/api/**等。
對於合格直接返回一個Json資料串,表示沒有登陸,訪問失敗。
其他的請求調用super.commence()按Spring原來的方式進行處理。
public class MyAuthenticationProcessingFilterEntryPoint extends LoginUrlAuthenticationEntryPoint { /** * @author ligh4 2015年4月1日下午4:38:04 */ @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; if ("XMLHttpRequest".equals(httpRequest.getHeader("X-Requested-With"))) { Map<String, Object> error = new HashMap<String, Object>(); error.put("success", false); error.put("result", "loginfailure"); error.put("data", "loginfailure"); // 相容extjs form loading //RenderUtils.renderJSON((HttpServletResponse) response, error); response.setContentType("json"); String json = new Gson().toJson(error); response.getWriter().write(json); response.getWriter().flush(); } else { super.commence(request, response, authException); } }}
2) 添加拒絕訪問AccessDeniedHandler處理
<access-denied-handler ref="myAuthenticationFailureHandler"/>
<pre name="code" class="html"><beans:bean id="myAuthenticationFailureHandler" class="com.lenovo.MyAuthenticationFailureHandler" />
需要注意的是,AccessDeniedHandler只有在已Login而沒有許可權的情況下才被觸發。 如果是session逾時或者沒有登陸,Spring則會直接跳轉到登陸頁面。
public class MyAuthenticationFailureHandler implements AccessDeniedHandler { /** * @author ligh4 2015年3月31日下午4:15:59 */ @Override public void handle(HttpServletRequest arg0, HttpServletResponse arg1, AccessDeniedException arg2) throws IOException, ServletException { LogHelper.debug(this, "handler AccessDeniedException..."); HttpServletRequest httpRequest = arg0; // is ajax request? if ("XMLHttpRequest".equals(httpRequest.getHeader("X-Requested-With"))) { String msg = "{\"success\" : false, \"message\" : \"authentication-failure\"}"; arg1.setContentType("json"); OutputStream outputStream = arg1.getOutputStream(); outputStream.write(msg.getBytes()); outputStream.flush(); } }}
AccessDeniedHandler的處理同上面的MyAuthenticationProcessingFilterEntryPoint類似。
判斷是否ajax或者api調用請求,如果是直接返回json資料。否則按Spring原來方式進行處理。
Spring security 3.x 普通login與ajax login筆記