2010年11月28日日曜日

Spring SecurityでDB接続してユーザ認証してみる

Springの認証機構Spring SecurityでのDBと繋いだログイン処理。

・環境
Spring 3.0.5
Roo 1.1.0

基本となる各種設定をSpring Rooでセットアップします。

roo> security setup

Managed ROOT\pom.xml [Adding property 'spring-security.version' to '3.0.2.RELEASE']
Managed ROOT\pom.xml [Added dependency org.springframework.security:spring-security-core:${spring-security.version}]
Managed ROOT\pom.xml [Added dependency org.springframework.security:spring-security-config:${spring-security.version}]
Managed ROOT\pom.xml [Added dependency org.springframework.security:spring-security-web:${spring-security.version}]
Managed ROOT\pom.xml [Added dependency org.springframework.security:spring-security-taglibs:${spring-security.version}]
Created SRC_MAIN_RESOURCES\META-INF\spring\applicationContext-security.xml
Created SRC_MAIN_WEBAPP\WEB-INF\views\login.jspx
Managed SRC_MAIN_WEBAPP\WEB-INF\views\views.xml
Managed SRC_MAIN_WEBAPP\WEB-INF\web.xml
Managed SRC_MAIN_WEBAPP\WEB-INF\spring\webmvc-config.xml

Spring RooによってSpring Securityアドオンがインストールされ、web.xmlにプロキシが登録されたり、各種viewが作成されます。
(実行時のSpring Securityバージョンは3.0.2)

認証機構の定義ファイルとして
SRC_MAIN_RESOURCES\META-INF\spring\applicationContext-security.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 security configurations -->
    <http auto-config="true" use-expressions="true">
     <form-login login-processing-url="/resources/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t"/>
        <logout logout-url="/resources/j_spring_security_logout"/>
        
        <!-- Configure these elements to secure URIs in your application -->
        <intercept-url pattern="/choices/**" access="hasRole('ROLE_ADMIN')"/>
        <intercept-url pattern="/member/**" access="isAuthenticated()" />
        <intercept-url pattern="/resources/**" access="permitAll" />
        <intercept-url pattern="/**" access="permitAll" />
    </http>

 <!-- Configure Authentication mechanism -->
    <authentication-manager alias="authenticationManager">
     <!-- SHA-256 values can be produced using 'echo -n your_desired_password | sha256sum' (using normal *nix environments) -->
     <authentication-provider>
      <password-encoder hash="sha-256"/>
         <user-service>
             <user name="admin" password="8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918" authorities="ROLE_ADMIN"/>
          <user name="user" password="04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb" authorities="ROLE_USER"/>
      </user-service>
     </authentication-provider>
 </authentication-manager>

</beans:beans>

デフォルトのこの設定ファイルで、
"/choices/**"のURLは'ROLE_ADMIN'なロールを持っていること、
"/member/**"のURLは認証されていること、
その他のアクセスは無制限に許可、
のインターセプトがかかります。

また、User/Passがadmin/adminの場合はROLE_ADMIN、user/userの場合はROLE_USERな権限設定がXMLに直接記述されています。

これをDBに問い合わせ、ユーザ認証を行うように変更します。

<?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 security configurations -->
    <http auto-config="true" use-expressions="true">
     <form-login login-processing-url="/resources/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t"/>
        <logout logout-url="/resources/j_spring_security_logout"/>
        
        <!-- Configure these elements to secure URIs in your application -->
        <!-- cssやimgなどWEBリソースは無制限 -->
        <intercept-url pattern="/resources/**" access="permitAll" />
        <!-- ログイン処理は無制限 -->
        <intercept-url pattern="/login/**" access="permitAll" />
        <!-- それ以外はadmin権限 -->
        <intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
    </http>

 <!-- Configure Authentication mechanism -->
    <authentication-manager alias="authenticationManager">
     <!-- SHA-256 values can be produced using 'echo -n your_desired_password | sha256sum' (using normal *nix environments) -->
     <authentication-provider>
      <!-- sha-256でパスワードをエンコーディングする -->
      <!-- "admin"文字列のハッシュ => "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918" -->
      <!-- "user"文字列のハッシュ => "04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb" -->
      <password-encoder hash="sha-256"/>
      <!-- データソース指定。SQL指定。 -->
      <jdbc-user-service data-source-ref="dataSource"
      users-by-username-query="select users_id as username, password as password, true as enabled from users where users_id=?"
      authorities-by-username-query="select users_id as username, 'ROLE_ADMIN' as authority from users where users_id=?"/>
     </authentication-provider>
 </authentication-manager>
</beans:beans>

以下で記述されているスキーマを定義することが好ましいですが、上記の設定ではusersテーブル一つで処理を行っています。
http://static.springsource.org/spring-security/site/docs/2.0.x/reference/appendix-schema.html

usersテーブルにはusers_id、passwordの2つのフィールドが定義されているものとします。

users-by-username-query設定で以下のクエリを発行し、認証を許可するユーザを取得します。

select
    users_id as username,
    password as password,
    true     as enabled
from
    users
where
    users_id=?

authorities-by-username-query設定で実際の認証設定を取得する以下のクエリを発行します。
ユーザが存在し、users-by-username-queryでのユーザ取得が行えれば全てのリソースに接続可能とするため、'ROLE_ADMIN'な固定値をauthorityとして返却します。

select
    users_id     as username,
    'ROLE_ADMIN' as authority
from
    users
where
    users_id=?

usersテーブルに以下のデータを登録すればhoge/adminでログイン可能となります。
users_id:hoge
password:8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918

参考資料)

・Spring Securityの機能について
http://ameblo.jp/spring-beginner/entry-10234230466.html

・実際に認証と認可をWEBにつけるには?
http://ameblo.jp/spring-beginner/entry-10236817063.html

・Spring by Example - Simple Spring Security Webapp
http://www.springbyexample.org/examples/simple-spring-security-webapp-spring-config.html

・5 Minute Guide to Spring Security
http://www.mularien.com/blog/2008/07/07/5-minute-guide-to-spring-security/

・Using Spring-Security Database in Spring-Roo
http://roosbertl.blogspot.com/2010/06/using-spring-security-database-in.html

・Security Database Schema
http://static.springsource.org/spring-security/site/docs/2.0.x/reference/appendix-schema.html

・Security Namespace Configuration
http://static.springsource.org/spring-security/site/docs/3.1.x/reference/ns-config.html#ns-auth-manager

・概説 Springプロダクト(3) - Spring Securityでユーザ認証/アクセス制御
http://journal.mycom.co.jp/articles/2010/03/25/spring3/index.html

・Spring Roo日本語リファレンス - 2.10 セキュアなアプリケーション
http://www.spring-roo.com/home/210

2010年11月10日水曜日

EclipseプラグインでJetty連携

開発用のAPコンテナをJettyにしよう作業してたところ、EclipseからJettyを起動させるプラグインを発見。

http://docs.codehaus.org/display/JETTY/Web+Tooling+Support


これはいいや、と書かれている通りに構築していたのですが、以下のエラーが出て起動できず。

java.lang.NoClassDefFoundError: org/mortbay/start/Main
Caused by: java.lang.ClassNotFoundException: org.mortbay.start.Main
 at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
 at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
Exception in thread "main" 

別途アプリをデプロイすればいいだけなのですが、開発資材を毎度毎度操作するのは面倒過ぎる。。

何だかんだで5時間近く色々と試してみたところ、当該プラグインがJetty7には対応していない、というオチ。

こことか、ここにその旨ありました。

Jetty6系だとちゃんと動きます。

2010年11月9日火曜日

Eclipseで比較エディタが開けなくなった。

Eclipse Version: 3.6.1

Updateかけたら比較エディタが開けなくなった。

こんなエラーが出る。

Could not open the editor: An unexpected exception was thrown.

java.lang.NullPointerException
 at org.eclipse.php.internal.core.format.FormatPreferencesSupport.verifyValidity(FormatPreferencesSupport.java:100)
 at org.eclipse.php.internal.core.format.FormatPreferencesSupport.getIndentationChar(FormatPreferencesSupport.java:89)
 at org.eclipse.php.internal.ui.editor.configuration.PHPStructuredTextViewerConfiguration.getIndentPrefixes(PHPStructuredTextViewerConfiguration.java:448)
 at org.eclipse.wst.sse.ui.internal.StructuredTextViewer.configure(StructuredTextViewer.java:311)
 at org.eclipse.php.internal.ui.compare.MergeSourceViewer.configure(MergeSourceViewer.java:542)
 at org.eclipse.php.internal.ui.compare.TextMergeViewer$ContributorInfo.internalSetDocument(TextMergeViewer.java:546)
 at org.eclipse.php.internal.ui.compare.TextMergeViewer$ContributorInfo.setDocument(TextMergeViewer.java:434)
 at org.eclipse.php.internal.ui.compare.TextMergeViewer.updateContent(TextMergeViewer.java:2569)
 at org.eclipse.php.internal.ui.compare.ContentMergeViewer.internalRefresh(ContentMergeViewer.java:814)
 at org.eclipse.php.internal.ui.compare.ContentMergeViewer.inputChanged(ContentMergeViewer.java:704)
 at org.eclipse.jface.viewers.ContentViewer.setInput(ContentViewer.java:274)
 at org.eclipse.compare.CompareViewerSwitchingPane.setInput(CompareViewerSwitchingPane.java:276)
 at org.eclipse.compare.internal.CompareContentViewerSwitchingPane.setInput(CompareContentViewerSwitchingPane.java:158)
 at org.eclipse.compare.CompareEditorInput.internalSetContentPaneInput(CompareEditorInput.java:844)
 at org.eclipse.compare.CompareEditorInput.access$8(CompareEditorInput.java:842)
 at org.eclipse.compare.CompareEditorInput$11.run(CompareEditorInput.java:778)
 at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
 at org.eclipse.compare.CompareEditorInput.feed1(CompareEditorInput.java:772)
 at org.eclipse.compare.CompareEditorInput.feedInput(CompareEditorInput.java:750)
 at org.eclipse.compare.CompareEditorInput.createContents(CompareEditorInput.java:554)
 at org.eclipse.compare.internal.CompareEditor.createCompareControl(CompareEditor.java:456)
 at org.eclipse.compare.internal.CompareEditor.createPartControl(CompareEditor.java:414)
 at org.eclipse.ui.internal.EditorReference.createPartHelper(EditorReference.java:670)
 at org.eclipse.ui.internal.EditorReference.createPart(EditorReference.java:465)

数日無視してたんですが、、地味に困るぜ、、ということで調べてみた。

何でも知ってるGoogle先生に聞けばすぐに判明。
https://bugs.eclipse.org/bugs/show_bug.cgi?id=326227
PDTのバグらしい。

Updateサイトを登録してHelp>Check for Updates
http://download.eclipse.org/tools/pdt/updates/2.2/milestones

PDT2.2が落ちてくるので更新すればOK。