スポンサーサイト

  • 2008.08.09 Saturday
  • -
  • -
  • -
  • -
  • by スポンサードリンク
この広告は60日以上更新がないブログに表示されております。
新しい記事を書くことで広告を消すことができます。

Jasypt 1.5 を試してみる

Javaの暗号化ライブラリ「Jasypt」の 1.5 を試してみました。

Jasypt: Java simplified encryption
http://www.jasypt.org/

Hibernate3 に Jasypt を組合わせる事により、カラムの暗号化/復号化を簡単に行う事が出来ます。

サンプルではEclipse3.4、JDK1.6.0_01、MySQL5.0.45、Hibernate3.2.6GAを使用しました。

Eclipseのプロジェクト構成は以下のようになります。

Jasypt Package Explorer

このサンプルで使用するテーブルのDDLは以下になります。


DROP TABLE IF EXISTS example;
CREATE TABLE example (
  ID int(11) NOT NULL auto_increment,
  NAME varchar(50) NOT NULL,
  CONTENTS varchar(255) NOT NULL,
  INSERT_DATE datetime NOT NULL,
  UPDATE_DATE datetime default NULL,
  DELETE_DATE datetime default NULL,
  PRIMARY KEY  (ID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8


Entityクラス・hbm.xmlは以下のように作成しました。

Entityクラス

package example.hibernate.entity;

import java.sql.Timestamp;

public class Example {
    private int id;
    private String name;
    private String contents;
    private Timestamp insertDate;
    private Timestamp updateDate;
    private Timestamp deleteDate;
    public String getContents() {
        return contents;
    }
    public void setContents(String contents) {
        this.contents = contents;
    }
    public Timestamp getDeleteDate() {
        return deleteDate;
    }
    public void setDeleteDate(Timestamp deleteDate) {
        this.deleteDate = deleteDate;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public Timestamp getInsertDate() {
        return insertDate;
    }
    public void setInsertDate(Timestamp insertDate) {
        this.insertDate = insertDate;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Timestamp getUpdateDate() {
        return updateDate;
    }
    public void setUpdateDate(Timestamp updateDate) {
        this.updateDate = updateDate;
    }
    
    @Override
    public String toString() {
        return String.format("ID[%s] Name[%s] Contents[%s] InsertDate[%s] UpdateDate[%s] DeleteDate[%s]",
                id, 
                name, 
                contents, 
                insertDate, 
                updateDate, 
                deleteDate);
    }
}

hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="example.hibernate.entity">
    
    <typedef name="encryptedString" class="org.jasypt.hibernate.type.EncryptedStringType">
      <param name="algorithm">PBEWithMD5AndDES</param>
      <param name="password">jasypt</param>
      <param name="keyObtentionIterations">1000</param>
    </typedef>
    
    <class name="Example">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" column="NAME" type="encryptedString" />
        <property name="contents" column="CONTENTS" type="encryptedString"/>
        <property name="insertDate" column="INSERT_DATE"/>
        <property name="updateDate" column="UPDATE_DATE"/>
        <property name="deleteDate" column="DELETE_DATE"/>
    </class>
</hibernate-mapping>

hbm.xml 内に typedef 要素を追加し、Jasypt で提供されている「org.jasypt.hibernate.type.EncryptedStringType」クラスを「encryptedString」という名前で定義します。

暗号化を行いたいEntityの property 要素の type 属性に、先ほど定義した「encryptedString」を指定します。

このEntityを使用するサンプルコードは以下のようになります。


package example.hibernate;

import java.sql.Timestamp;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

import example.hibernate.entity.Example;

public class JasyptExample {

    private static final Log logger = LogFactory.getLog(SaveExample.class);
    
    public static void main(String[] args) {
        JasyptExample jasyptExample = new JasyptExample();
        jasyptExample.save();
    }
    
    public void save() {

        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
        
        Example example = new Example();
        example.setName("あっきー");
        example.setContents("暗号化テスト");
        example.setInsertDate(new Timestamp(System.currentTimeMillis()));
        
        // 保存
        Session session = sessionFactory.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            session.save(example);
            tx.commit();
        } catch (final Exception e) {
            if (tx != null) tx.rollback();
            logger.debug(e);
        } finally {
            session.close();
        }
        
        // 再取得
        session = sessionFactory.openSession();
        try {
            Example savedExample = (Example)session.load(Example.class, example.getId());
            
            logger.debug(savedExample);

        } catch (final Exception e) {
            logger.debug(e);
        } finally {
            session.close();
        }
    }
}


上記のように、Hibernateのコードは通常の場合となんら変わりません。
このサンプルを実行した際のログは以下のようになります。


Hibernate: insert into Example (NAME, CONTENTS, INSERT_DATE, UPDATE_DATE, DELETE_DATE) values (?, ?, ?, ?, ?)
Hibernate: select example0_.id as id0_0_, example0_.NAME as NAME0_0_, example0_.CONTENTS as CONTENTS0_0_, example0_.INSERT_DATE as INSERT4_0_0_, example0_.UPDATE_DATE as UPDATE5_0_0_, example0_.DELETE_DATE as DELETE6_0_0_ from Example example0_ where example0_.id=?
2008-08-09 19:29:40,269 DEBUG [main] hibernate.SaveExample2 (SaveExample2.java:51) - ID[21] Name[あっきー] Contents[暗号化テスト] InsertDate[2008-08-09 19:29:40.0] UpdateDate[null] DeleteDate[null]

MySQLには以下のような感じで、暗号化されたデータが入っています。

mysql> select * from example;
+----+----------------------------------+----------------------------------------------+---------------------+-------------+-------------+
| ID | NAME | CONTENTS | INSERT_DATE | UPDATE_DATE | DELETE_DATE |
+----+----------------------------------+----------------------------------------------+---------------------+-------------+-------------+
| 15 | e2JG19TOFi465Q7zNqWuljsHLcg60nug | BwQBIN9z+gsLeoFzaZmqpUTQZ1yrKnseRckgcDo8hO0= | 2008-08-09 18:32:48 | NULL | NULL |
| 16 | McEhKZFbCg4meBIiN/Cl3E7j0kxo1KCu | 2ZyV3uIRworXd5M8eVi9iqFboPJ/7EboANa2sWJWqwA= | 2008-08-09 18:56:53 | NULL | NULL |
| 17 | 0QnfK9yGPGQPH6WSNE3NwF6EIbp87Z1M | 5ccy+zzAbxNVe5lXzohDpnh3+Q4LBAAMSZDhpWmdO5o= | 2008-08-09 19:01:20 | NULL | NULL |
| 18 | wLGWaqj7CnQFQ1nGqZ2Q9Dm+SR+cNoZ0 | OdhF5r4rsYG3OBKhbKRWzcYMeYC5LBMR | 2008-08-09 19:02:07 | NULL | NULL |
| 19 | 4eNdp9YoJN4XX7Xc4wDua41Jdpn2mFwD | LHG37qr8OY3v0oa18aKPjUgrDW96a2JS | 2008-08-09 19:02:28 | NULL | NULL |
| 20 | vKEaSHMvSLObV4mkvLLxJ2kxxATmxuu3 | cfcfSy0F5v+nTRQoekTKhr9Y8FiZs6tmGY3DlnwnPBE= | 2008-08-09 19:29:25 | NULL | NULL |
| 21 | rsObiSc/YFjA+wqggFn0OTRQTGKfQ9lQ | RjUyU53DuTHeZ3KlxtOhkhUduZ04AQtMPJRR7QIK0/M= | 2008-08-09 19:29:40 | NULL | NULL |
+----+----------------------------------+----------------------------------------------+---------------------+-------------+-------------+
7 rows in set (0.00 sec)

このように、Jasyptを使用する事で、今までHibernateを使用していたアプリケーションに簡単にカラムの暗号化を追加する事ができます。

暗号化された文字列長は平文に比べて長くなるため、カラム長は考慮しておく必要があるかも知れませんが・・・。

JasyptはSpring Securityとも組み合わせる事が出来るらしいので、そのうちまた触ってみたいと思います。

Spring Security 2.0.0 を試してみる その3

リファレンスにあったSession Fixation Attack Protectionの挙動を試してみました。

2.3.5. Session Fixation Attack Protection
http://static.springframework.org/spring-security/site/reference/html/namespace-config.html#ns-session-fixation

この設定は、httpタグのsession-fixation-protection属性で指定します。
属性に指定可能な値は以下の通り。

・migrateSession
    新しいSessionを作成し、既存のSessionに設定されている値をコピーします。デフォルトはこの設定になります。
・none
    session-fixationに対しては何もせず、最初に生成されたSessionを使用し続けます。
・newSession
    新しいSessionを作成します。既存のSessionの値はコピーしません。

新しいSessionを生成するとJSESSIONIDの値が変わるので、それにより確認できます。
この辺りの挙動の確認には以下のツールを使用しました。

ieHTTPHeaders
http://www.blunck.se/iehttpheaders/download.html

デフォルトの設定(migrateSession)では、ログイン前とログイン後でヘッダに書かれているJSESSIONIDの値が変わっています。

ログイン前


GET /spring-security/login.jsp HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*
Referer: http://localhost:8080/spring-security/index.jsp
Accept-Language: ja
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)
Host: localhost:8080
Connection: Keep-Alive
Cookie: JSESSIONID=4C1B17313FB0F92D07FDB341D4579F0F


ログイン後


GET /spring-security/secure/index.jsp HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*
Referer: http://localhost:8080/spring-security/login.jsp
Accept-Language: ja
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)
Host: localhost:8080
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=017BCBC528C79592393096E0CC921151


ヘッダに書かれているJSESSIONIDの値が変わっている事が分かります。
Sessionの値がコピーされているかどうかは、ログイン画面(/login.jsp)に以下の行を追加し、


<% session.setAttribute("session-fixation", "値のコピーのテスト"); %>


ログイン後の画面(/secure/index.jsp)に以下の行を追加しました。


<%=session.getAttribute("session-fixation")%>


ログインに成功すると"値のコピーのテスト"という文字列が画面に表示されたので、旧Sessionの値がちゃんと引き継がれている事が分かります。

次に、設定ファイルで「session-fixation-protection="none"」を指定してみました。
当然ですが今度はログインに成功してもJSESSIONIDの値は変わらず、当然同じSessionが維持されているので"値のコピーのテスト"という文字列も維持されています。

最後は「session-fixation-protection="newSession"」を指定してみます。
ログインに成功するとJSESSIONIDの値は変わるのですが、今度はSessionの値はコピーされないため、"値のコピーのテスト"という文字列は表示されません。

デフォルトの設定が安全且つ便利なので、特に理由がなければこの属性を明示的に指定する必要は無さそうです。

Spring Security 2.0.0 を試してみる その2

前回のサンプルにちょっと手を加えて、データベースを使用してユーザの認証と権限の取得を行うように変更してみました。

データベースにはたまたま手元にインストールされていたMySQL 5.0.45を使用し、以下のテーブルとデータを用意しました。


-- 管理者テーブル
CREATE TABLE IF NOT EXISTS ADMIN (
    ADMIN_ID INT NOT NULL AUTO_INCREMENT,
    LOGIN_ID VARCHAR(255),
    PASSWORD VARCHAR(32),
    INSERT_DATE DATETIME,
    UPDATE_DATE DATETIME,
    DELETE_DATE DATETIME,
    CONSTRAINT PK_ADMIN PRIMARY KEY(ADMIN_ID)
)ENGINE=INNODB;

INSERT INTO ADMIN VALUES(null,"guest","guest",current_timestamp,null,null);
INSERT INTO ADMIN VALUES(null,"admin","admin",current_timestamp,null,null);
INSERT INTO ADMIN VALUES(null,"super","super",current_timestamp,null,null);

-- 権限テーブル
CREATE TABLE IF NOT EXISTS ROLE (
    ROLE_ID INT NOT NULL AUTO_INCREMENT,
    ROLE_NAME VARCHAR(32),
    INSERT_DATE DATETIME,
    UPDATE_DATE DATETIME,
    DELETE_DATE DATETIME,
    CONSTRAINT PK_ROLE PRIMARY KEY(ROLE_ID)
)ENGINE=INNODB;

INSERT INTO ROLE VALUES(null,"ROLE_GUEST",current_timestamp,null,null);
INSERT INTO ROLE VALUES(null,"ROLE_ADMIN",current_timestamp,null,null);
INSERT INTO ROLE VALUES(null,"ROLE_SUPER",current_timestamp,null,null);

-- 管理者と権限の関連テーブル
CREATE TABLE IF NOT EXISTS ADMIN_ROLE (
    ADMIN_ROLE_ID INT NOT NULL AUTO_INCREMENT,
    ADMIN_ID INT,
    ROLE_ID INT,
    INSERT_DATE DATETIME,
    UPDATE_DATE DATETIME,
    DELETE_DATE DATETIME,
    CONSTRAINT PK_ADMIN_ROLE
        PRIMARY KEY (ADMIN_ROLE_ID),
    CONSTRAINT FK_ADMIN_ROLE_1
        FOREIGN KEY (ADMIN_ID)
        REFERENCES ADMIN (ADMIN_ID),
    CONSTRAINT FK_ADMIN_ROLE_2
        FOREIGN KEY (ROLE_ID)
        REFERENCES ROLE (ROLE_ID)
)ENGINE=INNODB;

INSERT INTO ADMIN_ROLE VALUES(null,1,1,current_timestamp,null,null);
INSERT INTO ADMIN_ROLE VALUES(null,2,2,current_timestamp,null,null);
INSERT INTO ADMIN_ROLE VALUES(null,3,2,current_timestamp,null,null);
INSERT INTO ADMIN_ROLE VALUES(null,3,3,current_timestamp,null,null);


Spring Securityの方は「applicationContext-security-ns.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-2.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">

    <http auto-config="true" access-denied-page="/permissionDeny.jsp">
        <intercept-url pattern="/secure/extreme/**" access="ROLE_SUPER"/>
        <intercept-url pattern="/secure/**" access="ROLE_ADMIN" />
        <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <form-login login-page="/login.jsp"
                    login-processing-url="/login" 
                    authentication-failure-url="/authenticationFailure.jsp" />
        <logout logout-url="/logout" logout-success-url="/index.jsp" invalidate-session="true"/>
    </http>

    <authentication-provider>
        <jdbc-user-service data-source-ref="dataSource" 
                           users-by-username-query="
                            SELECT LOGIN_ID, PASSWORD, 'true'
                              FROM ADMIN
                             WHERE DELETE_DATE IS NULL AND LOGIN_ID = ?"
                           authorities-by-username-query="
                            SELECT LOGIN_ID, ROLE_NAME
                              FROM ADMIN AD,
                                   ADMIN_ROLE AR,
                                   ROLE RO
                             WHERE AD.ADMIN_ID = AR.ADMIN_ID
                               AND AR.ROLE_ID = RO.ROLE_ID
                               AND AD.DELETE_DATE IS NULL AND AD.LOGIN_ID = ?" />
    </authentication-provider>

    <beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <beans:property name="driverClassName">
          <beans:value>com.mysql.jdbc.Driver</beans:value>
        </beans:property>
        <beans:property name="url">
          <beans:value>jdbc:mysql://localhost:3306/spring</beans:value>
        </beans:property>
        <beans:property name="username"><beans:value>spring</beans:value></beans:property>
        <beans:property name="password"><beans:value>spring</beans:value></beans:property>
    </beans:bean>

</beans:beans>

以前のバージョンと異なり、JdbcDaoImplのBean定義はauthentication-providerタグの子として指定可能なjdbc-user-serviceタグを使用することができるようになっています。
設定する内容はほぼ同じなのですが、定義するBeanの数が減っただけでも設定ファイルが読み易くなっているように感じました。

また、前回サンプルよりその他の変更として、認証エラー時と権限エラー時のページをそれぞれ設定するようにしています。
認証エラー時のページはform-loginタグの「authentication-failure-url」属性で、権限エラー時の属性はhttpタグの「access-denied-page」属性で指定できるようになっています。

他、今回は試していませんが、一応調べた事として書いておくと、特殊な要件などで独自のFilterを作成・設定したい場合の方法については、公式リファレンスの「2.3.4. Adding in Your Own Filters」に記載されています。

http://static.springframework.org/spring-security/site/reference/html/namespace-config.html#ns-custom-filters

以前のようなカンマ区切りの文字列でFilterのBeanIDを指定する方法ではなく、Spring Securityで予め決められているFilterの呼び出し順序の中のどの位置に自分のFilterを差し込むかを指定するようになったようです。


    <beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter">
        <custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>
    </beans:bean>


position属性で差し替えたいFilterの位置を指定すればいいようです。
また、差し替えではなく割り込ませたい場合にはbefore属性、after属性でFilter名を指定すればいいみたいです。
Filter名はSpring Securityの方で予め決められている名前を指定するようです。
指定可能なFilter名についてはリファレンスに一覧がありました。

他にもリファレンスやXMLの定義を見ていると、Session Fixation対策などの機能もあるようなので(以前からあった?)、暇を見て調べてみたいと思います。

Spring Security 2.0.0 を試してみる

Acegi Securityの新しいバージョンがSpring Security 2.0.0として公開されたので、少しずつ調べてみようと思います。

Spring Security
http://static.springframework.org/spring-security/site/index.html

まずは付属のサンプルをベースとして、もうちょっと簡単にしたサンプルを書いて実際に動かしてみました。
JSPのみの簡単なサンプルになります。

プロジェクトの構成は以下のようにしました。
動作環境としては、Java6 + Eclipse3.3 + WTP + Tomcat 5.5.20を使用しています。

ディレクトリ構成

applicationContext-security-ns.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-2.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">

    <http auto-config="true">
        <intercept-url pattern="/secure/extreme/**" access="ROLE_SUPERVISOR"/>
        <intercept-url pattern="/secure/**" access="IS_AUTHENTICATED_REMEMBERED" />
        <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <form-login login-page="/login.jsp" />
        <logout logout-url="/logout" logout-success-url="/index.jsp" invalidate-session="true"/>
    </http>

    <authentication-provider>
        <password-encoder hash="md5"/>
        <user-service>
            <user name="manager" password="1d0258c2440a8d19e716292b231e3190" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
            <user name="admin" password="21232f297a57a5a743894a0e4a801fc3" authorities="ROLE_USER,ROLE_TELLER" />
            <user name="user" password="ee11cbb19052e40b07aac0ca060c23ee" authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
</beans:beans>


web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  version="2.4">

    <display-name>Spring Security Example</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/applicationContext-security-ns.xml
        </param-value>
    </context-param>
    
    <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>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

</web-app>


index.jsp

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Spring Securityサンプル</h1>
<p>
Spring Securityのサンプルページです。<br />
</p>
<p>
現在ログイン中のユーザ権限情報....: <%= request.getUserPrincipal() %>
</p>
<p><a href="secure/index.jsp">セキュアなページ</a></p>
<p><a href="secure/extreme/index.jsp">とってもセキュアなページ</a></p>
<p><a href="logout">ログアウト</a></p>
</body>
</html>


login.jsp

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core' %>
<%@ page import="org.springframework.security.ui.AbstractProcessingFilter" %>
<%@ page import="org.springframework.security.ui.webapp.AuthenticationProcessingFilter" %>
<%@ page import="org.springframework.security.AuthenticationException" %>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>ログイン</title>
  </head>
  <body onload="document.f.j_username.focus();">
    <h1>ログインページ</h1>
    <P>以下がこのサンプルで使用可能なユーザです。:
    <P>
    <P>username <b>manager</b>, password <b>manager</b>
    <br>username <b>admin</b>, password <b>admin</b>
    <br>username <b>user</b>, password <b>user</b>
    <p>
    <% if (session.getAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY) != null) { %>
      <font color="red">
        ログインに失敗しました。<BR><BR>
        理由: <%= ((AuthenticationException) session.getAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %>
      </font>
    <% } %>
    <form name="f" action="<c:url value='j_spring_security_check'/>" method="POST">
      <table>
        <tr>
            <%
                String username = "";
                if (session.getAttribute(AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY) != null) {
                    username = (String)session.getAttribute(AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY);
                }
            %>
            <td>ユーザ:</td>
            <td><input type='text' name='j_username' value='<%=username%>' /></td>
        </tr>
        <tr>
            <td>パスワード:</td>
            <td><input type='password' name='j_password' /></td>
        </tr>
        <tr>
            <td><input type="checkbox" name="_spring_security_remember_me"></td>
            <td>ログイン情報を保存する(2週間)</td>
        </tr>
      </table>
      <input name="submit" type="submit" value="送信" />
      <input name="reset" type="reset" value="クリア" />
    </form>
  </body>
</html>


secure/index.jsp

 <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
 <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 </head>
 <body>
 <h1>Secure Page</h1>
 <p>
 セキュアなページ。
 </p>
 <p><a href="../index.jsp">戻る</a></p>
 </body>
 </html>


secure/extreme/index.jsp

 <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
 <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 </head>
 <body>
 <h1>Extremely Secure Page</h1>
 <p>
 とってもセキュアなページ。
 </p>
 <p><a href="../../index.jsp">戻る</a></p>
 </body>
 </html>


動作としては、index.jspより「セキュアなページ」または「とってもセキュアなページ」のリンクをクリックしてアクセスするとlogin.jspが表示され、そこでユーザを選んでログインするとそれぞれのページが表示される、といった単純なものになります。
ちなみに、managerとそれ以外のユーザではアクセス可能なページが異なります。

applicationContext-security-ns.xmlの中身を見ると、タグは見慣れないものばかりですが、Acegi Security 1.xの頃に比べると随分記述量が減っていていい感じですね。
どうも以前と違い、かなりの部分でデフォルト設定の場合には記述しなくていいようになったみたいです。

細かい設定の変更方法など、まだまだ分からない事も多いですが、これからこのサンプルをベースとしてリファレンスと格闘しながら勉強して行きたいと思います。

Mapのvalueからkeyの値を取得したい

JavaでMapを使用していると時々、valueからkeyの値を取得したい場合があるので、ちょっと調べた時のメモです。

Commons Collections 3.xに入っているBidiMapを使うとまさにそのままのgetKey(value)というメソッドが使用できるので便利でした。
また、keyとvalueを反転させたMapを取得したい場合にはinverseBidiMap()メソッドで取得する事ができます。


// Mapの生成
Map<String, String> map = new HashMap<String, String>();
map.put("apple", "りんご");
map.put("orange", "オレンジ");
map.put("banana", "バナナ");
map.put("strawberry", "いちご");

// BidiMapの生成
BidiMap bidiMap = new DualHashBidiMap(map);

// valueからkeyを取得
System.out.println(bidiMap.getKey("りんご"));
System.out.println(bidiMap.getKey("オレンジ"));
System.out.println(bidiMap.getKey("バナナ"));
System.out.println(bidiMap.getKey("いちご"));

// keyとvalueを反転させたMapを取得
for (Object entry : bidiMap.inverseBidiMap().entrySet()) {
    System.out.printf("key[%s] value[%s]¥n", ((Map.Entry)entry).getKey(), ((Map.Entry)entry).getValue());
}

このコードを実行すると、以下のように出力されます。

apple
orange
banana
strawberry
key[いちご] value[strawberry]
key[オレンジ] value[orange]
key[バナナ] value[banana]
key[りんご] value[apple]


BidiMapインターフェースを実装したクラスとしては、DualHashBidiMap、
DualTreeBidiMap、TreeBidiMapなどが提供されています。

keyとvalueを反転させたMapを取得したいだけであれば、同じくCommons Collections 3.xにあるMapUtils#invertMap()が便利です。


Map<String, String> map = new LinkedHashMap<String, String>();
map.put("apple", "りんご");
map.put("orange", "オレンジ");
map.put("banana", "バナナ");
map.put("strawberry", "いちご");

// keyとvalueを反転させたMapを取得
for (Map.Entry<String, String> entry : ((Map<String, String>)MapUtils.invertMap(map)).entrySet()) {
    System.out.printf("key[%s] value[%s]¥n", entry.getKey(), entry.getValue());
}


出力はこんな感じです。

key[オレンジ] value[orange]
key[いちご] value[strawberry]
key[バナナ] value[banana]
key[りんご] value[apple]


Commons Collections 2.xの場合は、DoubleOrderedMapが同じ用途で使えます。

Commons Collectionsは未だにGenerics対応版がリリースされないのが難と言えば難なのですが、以下のようなプロダクトもあるみたいですね。
まだ使ってみたわけではありませんが、機会を見て試してみたいですね。

SourceForge.net: Commons-Collections with Generics
http://sourceforge.net/projects/collections

VelocityのStringResourceLoaderを使う

Apache Velocity でテンプレートをファイルではなく文字列として作成したい場合に、Velocity 1.5から追加された「org.apache.velocity.runtime.resource.loader.StringResourceLoader」というResourceLoaderを使えばいいようです。
あまりサンプルコードなどが載っていないようなので、一応。

// 初期化
VelocityEngine engine = new VelocityEngine();
engine.addProperty(VelocityEngine.RESOURCE_LOADER, "STRING");
engine.addProperty("STRING.resource.loader.class",
"org.apache.velocity.runtime.resource.loader.StringResourceLoader");
engine.init();

StringResourceRepository repository = StringResourceLoader.getRepository();
repository.putStringResource("template1", "こんにちは、$name さん");
repository.putStringResource("template2", "こんばんは、$name さん");

// テンプレートの使用
Template template = engine.getTemplate("template1", "UTF-8");
VelocityContext context = new VelocityContext();
context.put("name", "あっきー");

StringWriter writer = new StringWriter();
template.merge(context, writer);
writer.close();
System.out.println(writer.toString());


上記のコードを実行すると「こんにちは、あっきー さん」という文字列が出力されます。

単に文字列をテンプレートとして使用したいだけであればVelocityEngine#evaluate()メソッドで十分なのですが、DBにテンプレート文字列を格納している場合など、数が多い場合にはStringResourceLoaderの方が便利かも知れません。

やっとブログ開始しました

重すぎる腰を上げてやっとブログを開始しました。

書けるような事は特になんもないへっぽこエンジニアですが、
ハマったこととか、勉強したこととか、一所懸命がんばって書いてこーと思います。

よろしくお願いします。

calendar
  12345
6789101112
13141516171819
20212223242526
2728293031  
<< May 2012 >>
profile
selected entries
categories
archives
recent trackback
links
search this site.
others
mobile
qrcode
recommend
現場で使えるデバッグ & トラブルシュート Java編
現場で使えるデバッグ & トラブルシュート Java編 (JUGEMレビュー »)
小堀 一雄,茂呂 範,佐藤 聖規,石垣 一,飯山 教史
recommend
recommend
recommend
recommend
recommend
recommend
recommend
リファクタリング―プログラムの体質改善テクニック (Object Technology Series)
リファクタリング―プログラムの体質改善テクニック (Object Technology Series) (JUGEMレビュー »)
マーチン ファウラー,Martin Fowler,児玉 公信,平澤 章,友野 晶夫,梅沢 真史
recommend
パターン指向リファクタリング入門~ソフトウエア設計を改善する27の作法
パターン指向リファクタリング入門~ソフトウエア設計を改善する27の作法 (JUGEMレビュー »)
ジョシュア・ケリーエブスキー,小黒 直樹,村上 歴,高橋 一成,越智 典子
sponsored links
powered
無料ブログ作成サービス JUGEM