使用SecurityContextHolder來偷窺登入帳號密碼,手段還真是不夠文雅。Spring-Security3是有提供取得登入資訊塞到Session的實踐,不過寫起來很煩,很煩也大概不易被破解^^。Google這方面的資訊,不是缺漏,就是講述古早的版本,還有中文網站,資訊雖新,卻常出現文章一大抄的謬誤,我目前是用3.0.2版,和3.0.1、3.0.0差異何在也不知,不過至少我這方面有踹通,實踐方式和Google內容還是有些關鍵性的差異:

  第一個應該是security-context.xml的設定了:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="
http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
    <http use-expressions="true" auto-config="true">
        <intercept-url pattern="/*.do" access="hasRole('ROLE_USER')"/>
        <intercept-url pattern="/login.jsp" access="isAnonymous()"/>
        <intercept-url pattern="/**" access="permitAll"/>
        <form-login login-processing-url="/j_spring_security_check"
         login-page="/login.jsp" default-target-url="/echo.do"
         authentication-failure-url="/login.jsp?error=1"/>
        <logout logout-url="/j_spring_security_logout" logout-success-url="/login.jsp"/>
        <custom-filter before="FORM_LOGIN_FILTER" ref="authenticationProcessingFilter"/>
        <session-management invalid-session-url="/timeout.html">
            <concurrency-control max-sessions="1" />
        </session-management>
    </http>
    <authentication-manager alias="authenticationManager">
        <authentication-provider user-service-ref="securityManager"/>
    </authentication-manager>
    <beans:bean id="securityManager"
        class="com.foo.dao.impl.UserDetailsServiceImpl"/>
   
<beans:bean id="authenticationProcessingFilter"
        class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
        <beans:property name="authenticationManager" ref="authenticationManager"/>
        <beans:property name="authenticationSuccessHandler">
            <beans:bean class="com.foo.security.MyAuthenticationSuccessHandler">
                <beans:property name="defaultTargetUrl" value="/echo.do"/>
            </beans:bean>
        </beans:property>
        <beans:property name="filterProcessesUrl" value="/j_spring_security_check"/>       
    </beans:bean>
</beans:beans>

  紅字是之前SecurityContextHolder飯粒多出來的設定,真正是靠腰的多。<http>標籤多出<custom-filter>指向authenticationProcessingFilter這個bean,而Google的before後所接的定字幾乎都錯,都是這麼寫:

<custom-filter before="AUTHENTICATION_PROCESSING_FILTER" ref="authenticationProcessingFilter"/>

  可是Filter的列舉(枚舉)型態裡,根本沒這有AUTHENTICATION_PROCESSING_FILTER這號人物,其它的參考下列表,我原本用CONCURRENT_SESSION_FILTER過不了關,後來改成FORM_LOGIN_FILTER才Pass,Spring-Seucrity 3這部份很像Struts2的Interceptor Stack,從下表來看,應該是有順序,LOGOUT_FILTER排在FORM_LOGIN_FILTER之前…嗯~~也蠻合理的。

Enumerated Values :
    - FIRST
    - CHANNEL_FILTER
    - CONCURRENT_SESSION_FILTER
    - SECURITY_CONTEXT_FILTER
    - LOGOUT_FILTER
    - X509_FILTER
    - PRE_AUTH_FILTER
    - CAS_FILTER
    - FORM_LOGIN_FILTER
    - OPENID_FILTER
    - BASIC_AUTH_FILTER
    - SERVLET_API_SUPPORT_FILTER
    - REMEMBER_ME_FILTER
    - ANONYMOUS_FILTER
    - EXCEPTION_TRANSLATION_FILTER
    - SESSION_MANAGEMENT_FILTER
    - FILTER_SECURITY_INTERCEPTOR
    - SWITCH_USER_FILTER
    - LAST

  <custom-filter>選擇FORM_LOGIN_FILTER時機委給Bean-authenticationProcessingFilter使用,這一動作出現頻道蓋台,<form-login>標籤應該失效,委給authenticationProcessingFilter處理。authenticationProcessingFilter顯然是指定org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter來處理的,此處在Google上又會得到錯誤類別名,正名是UsernamePasswordAuthenticationFilter,而Google的類別名是UsernamePasswordAuthenticationProcessingFilter,多出了Processing,所以我才猜是不是前版的遺留,至少在3.0.2版沒這個類別。

  authenticationProcessingFilter要指定三類properties,第一個是指定Authentication Manager。若非這次的需求,SecurityContextHolder會找到<authentication-manager>這個標籤作為預設值。但既然委託給Spring Bean處理,所以要有的bean reference的name,所以後來才在<authentication-manager>加個alias屬性,這樣<authentication-manager>才能被authenticationProcessingFilter找到。

  authenticationProcessingFilter要指定的第二類properties是本篇文章目的主程式所在,儲存username至session。最多有四種bean,最常用的是認證成功(AuthenticationSuccessHandler)和認證失敗(AuthenticationFailureHandler),尚有未認證的訪問及已認證訪問受保護的URL。而儲存至session的動作在第一種,是故AuthenticationSuccessHandler屬性指向MyAuthenticationSuccessHandler這個類別,其實作如下:

public class MyAuthenticationSuccessHandler extends
        SavedRequestAwareAuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
            HttpServletResponse response, Authentication authentication)
            throws ServletException, IOException {
        HttpSession session = request.getSession();
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        session.setAttribute("username", userDetails.getUsername());
        super.onAuthenticationSuccess(request, response, authentication);
    }
}

  和前一篇SecurityContextHolder飯粒原理是一樣,只是比較文雅,而MyAuthenticationSuccessHandler下設了一個defaultTargetUrl屬性,其實也恰恰覆蓋<form-login>的default-target-url屬性。先前有言,<customer-filter>若設置為CONCURRENT_SESSION_FILTER過不了關,是session值在onAuthenticationSuccess的設定無法帶給defaultTargetUrl,所以改成FORM_LOGIN_FILTER就可以work。

  第三類是filterProcessesUrl屬性,指authenticationProcessingFilter會對哪個URL Pattern產生作用,本例是指/j_spring_security_check,就因為custom-filter設置before="FORM_LOGIN_FILTER"搶在<form-login>攔截,致使<form-login>失效。

arrow
arrow
    全站熱搜

    Jemmy 發表在 痞客邦 留言(0) 人氣()