21 3月 2011

Spring Security (3) Basic Configuration fo <authentication-provider>

<authentication-manager>共本上提供了兩種Authentication Provider,一個是authentication-provider,另一個是ldap-authentication-provider

預設的authentication-provide就是採用DaoAuthenticationProvider,有三種基本的帳號密碼檢核與提供人員資料方式

  • user-service:建立一個in-memory的UserDetailService,帳密可以自properties file載入或在xml以<user>建立
  • jdbc-user-service:Spring Security提供了SQL Schema,所以相關的帳密可以自DB載入,只要給jdbc-user-service一個DataSource就可以使用,或是現有欄位定稱與Spring Security不同,那也可以透過提供users-by-username-query、authorities-by-username-query等SQL來取得資料。
  • ldap-user-service:再明顯不過了吧,透過ldap取得帳密資料,在預設的情形下是沒意義的....

通常DB裡存的密碼不會是明碼(如果真的是明碼....),所以Spring Security也提供了基本的編碼方式,像sha、md5都有支援,我們可以透過<password-encoder hash="type">來設定這個authentication provider要用哪種編碼;如果我們的編碼方式不在Spring Security的支援範圍之內,也可以自行提供一個PasswordEncoder的實作。

如果可以完全接受SpringSecurity提供的Schema作為系統的帳密設定,那當然是很不錯的一件事,但我想這種事不太容易發生...所以修改的方式通常有二,一是自已提供UserDetailsService的實作將user service換掉,二是用自已的Authentication Provider實作,直接把authentication provider換掉囉。不過換Authentication Provider的原因通常不是因為帳密的DB Schema不同,通常是因為檢核的方式不同而更換,看看Spring Security提供的其他Authentication Provider名稱就知道,像JaasAuthenticationProvider、OpenIDAuthenticationProvider、CasAuthenticaionProvider等就知道需要更換Authentication Provider大約是在什麼情形才需要更動。

 

 

20 3月 2011

Spring Security (2) Basic Configuration of <http>

看過先前的說明應該會有些困惑,我們應該先稍為拆解一下<http>
<http auto-config='true'> 代表了三個預設定設定

  • <form-login>
  • <http-basic>
  • <logout>

<form-login>代表要使用基本的Form-based authentication,但由於沒有指定登入的頁面,所以SpringSecurity會直接採用內建的Servlet產生登入的頁面,如果只是展示一下系統,當然沒問題,但真實在使用的系統應該沒辦法接受,所以直接加上<form-login login-page="/your_login.jsp" />這樣的設定就可以改用目前系統使用的登入頁面,但由於其他設定都沒動,所以form的欄位及action url仍有一定的要求,action仍必需是“/j_spring_security_check“,account的欄位名稱必需是“j_username“,password欄位名稱必需是“j_password“。
form-login還可以設定其他的參數,像login-processing-url代表action要送出的url,default-target-url代表登入成功後要轉的url,authentication-failure-url代表登入失敗後要轉入的url,always-use-default-target設定為true則代表登入成功或失敗都要轉入default-target-url。
比較有趣的是authentication-success-handler-ref與 authentication-failure-handler-ref,這兩個參數不應該與default-target-url、authentication-failure-url共用,這兩個handler-rul的設定代表啟用AuthenticationSuccessHandler與AuthenticationFailureHandler,如果想實現轉入登入前的那一頁用這個設定會很容易達成。

<http-basic>則代表使用HTTP basic authentication header,主要是將帳密用冒號(:)組合,再以base64編碼後送出,這個設定讓系統可以很容易地支援REST的程式。

<logout>代表起用Logout Filter,就SpringSecurity認為,每個系統都應該要有一個Logout Filter才對,比較form-login,當然也可以設定logout-url、logout-success-url及success-handler-rel,另一項是invalidate-session,若設定為true,在登出的同時就會令session失效。

 

18 3月 2011

Spring Security (1) Basic Configuration

一個對外的系統通常有權限設定,主要的需求通常就是兩個

  • 判斷目前的操作人員是誰
  • 人員是否可以進行這個操作

Spring Security提供了一個快速而有彈性的方法可以處理上述兩個需求,當然,有彈性通常也代表較多的設定與較複雜的設計....

我們先假設一個基本的網頁系統權限需求,除index.jsp外,其他的頁面存取都必需要是登入後才能看到,但/admin.jsp則必需是具有admin角色的人員才能看到。

web.xml

第一步當然是載入Spring的設定檔....
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
再來就是利用Spring的filter來檢查url及使用者,設定welcome file主要是讓url為"/"的request直接送到index.jsp
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

接下來就是spring security的設定了,先用最基本的方式,由Spring提供登入的頁面,我們只要提供哪些url需要被檢查,登入的帳號密碼資料即可。

<http auto-config='true'>
    <intercept-url pattern="/" filters="none" />
    <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
    <intercept-url pattern="/**" access="ROLE_USER" />
</http>
<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN" />
            <user name="user" password="user" authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
</authentication-manager>
<http>的<intercept-url>指出哪些url需要被檢查,像"/admin.jsp"就要是具有"ROLE_ADMIN"角色的人才能進人,而除了“/“之外的資源全部需要登入後才能使用。
<user-service>則是提供了帳密及角色的資料。

接下來在browser上試著要進入系統,你會發現除了"/"之外,都會跳出一個登入頁面 Spring Login
可以試著用admin或user的帳號進入。  

這就是最基本的Spring Security 體驗....

17 3月 2011

Maven 同時使用Emma 與Surefire會執行兩次Unit test的解決方式

emma-maven-plugin的開發人員認為,被instrumented過的test class不等同於原來的test class,可能會造成原來test不會通過,但在emma處理後而可以通過,或是反過來的情形發生。所以在pom中如果同時加上emma-maven-plugin與maven-surefire-plugin,就會跑兩次的unit test。

如果可以接受被emma instrumented過的test class可以代表原來的test class,那解決的方式就還蠻容易的,簡單說, 就是讓surefire執行被instrumented過的test class,但是要求emma在test或site之類的phase不做事。
設定如下



	org.sonatype.maven.plugin
	emma-maven-plugin
	1.2
	
		
			instrument
			process-classes
			
				instrument
			
		
		
			test
			test
			
				true
			
		
		
			site
			site
			
				true
			
		
	



	org.apache.maven.plugins
	maven-surefire-plugin
	2.8
	true
	
	
		once
		xml
		${project.build.directory}/generated-classes/emma/classes
	


Report的部份不用特別修改


	
		
		
			org.sonatype.maven.plugin
			emma-maven-plugin
			1.2
		

		
			org.apache.maven.plugins
			maven-surefire-report-plugin
			2.8
		

14 3月 2011

偶有所感

方才看了 綿羊的譯心譯意 的新博文“大師出手,高下立見“,見到下面這段文字

“I am not bound to win, but I am bound to be true. I am not bound to succeed but I am bound to live up to what light I have. I must stand with anybody that stands right, stand with him while he is right and part with him when he goes wrong.“

我覺得失敗也要敗得有格調,但我現在配合的人似乎不在意格調的問題....
突然有點感慨我現在做的工作,又要找下一步了嗎?

 

07 3月 2011

全型 <->半型

嗯,非常無聊,寫了還是記錄一下

字串裡的全型字元與半型字元轉換

public abstract class StringUtils {
	/**
	 * 全型轉半型
	 * @param source
	 * @return
	 */
	public static String convertToHalfWidth(final String source) {
		if (null == source) {
			return null;
		}
		
		char[] charArray = source.toCharArray();
		for (int i = 0; i < charArray.length; i++) {
			int ic = (int) charArray[i];
			
			if (ic >= 65281 && ic <= 65374) {
				charArray[i] = (char) (ic - 65248);
			} else if (ic == 12288) {
				charArray[i] = (char) 32;
			}
			
		}
		return new String(charArray);
	}
	
	/**
	 * 半型轉全型
	 * @param source
	 * @return
	 */
	public static String convertToFullWidth(final String source) {
		if (null == source) {
			return null;
		}
		
		char[] charArray = source.toCharArray();
		for (int i = 0; i < charArray.length; i++) {
			int ic = (int) charArray[i];
			
			if (ic >= 33 && ic <= 126) {
				charArray[i] = (char) (ic + 65248);
			} else if (ic == 32) {
				charArray[i] = (char) 12288;
			}
			
		}
		return new String(charArray);
	}
}