PicketLink その2

前回に続いて、PicketLinkの紹介をします。
今回は、picketlink-authentication-formを取り上げます。
この例では、JSFと組み合わせた場合のコードとなっています。
コードはここにあります。

実行環境
PicketLink 2.7.0.Final
WildFly 8.2.0.Final

前提条件
CDI
JSF 2.0

著作権表示

JBoss, Home of Professional Open Source
Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual
contributors by the @authors tag. See the copyright.txt in the 
distribution for a full listing of individual contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,  
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

picketlink-authentication-form.war
warファイルの内容は次の通りです。
warFile

/WEB-INF/beans.xml

<!-- Marker file indicating CDI should be enabled -->
<beans xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee 
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

beans.xmlは、前回と同様です。

/WEB-INF/faces-config.xml

<?xml version="1.0"?>
<!-- Marker file indicating JSF should be enabled -->
<faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xi="http://www.w3.org/2001/XInclude"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">

</faces-config>

JSFの設定ファイルです。このサンプルでは、何も設定していません。

SecurityInitializer.java

package org.jboss.as.quickstarts.picketlink.authentication.form;

import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.credential.Password;
import org.picketlink.idm.model.basic.User;

import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;

/**
 ** This startup bean creates a default user account when the application is started. Since we are not
 ** providing an IDM configuration in this example, PicketLink will default to using a file-based identity
 ** store to persist user and other identity state.
 **
 **
 ** @author Shane Bryzak
 **/
@Singleton
@Startup
public class SecurityInitializer {

    @Inject
    private PartitionManager partitionManager;

    @PostConstruct
    public void create() {
        IdentityManager identityManager = this.partitionManager.createIdentityManager();

        User user = new User("jane");

        user.setEmail("jane@doe.com");
        user.setFirstName("Jane");
        user.setLastName("Doe");

        identityManager.add(user);
        identityManager.updateCredential(user, new Password("abcd1234"));
    }
}

ここでは、ユーザjaneとパスワードabcd1234を設定しています。

HttpSecurityConfiguration.java

package org.jboss.as.quickstarts.picketlink.authentication.form;

import org.picketlink.config.SecurityConfigurationBuilder;
import org.picketlink.event.SecurityConfigurationEvent;

import javax.enterprise.event.Observes;

/**
 * <p>A simple CDI observer for the {@link org.picketlink.event.SecurityConfigurationEvent}.</p>
 *
 * <p>The event is fired during application startup and allows you to provide any configuration to PicketLink
 * before it is initialized.</p>
 *
 * <p>All the configuration related with Http Security is provided from this bean.</p>
 *
 * @author Pedro Igor
 */
public class HttpSecurityConfiguration {

    public void onInit(@Observes SecurityConfigurationEvent event) {
        SecurityConfigurationBuilder builder = event.getBuilder();

        builder
            .http()
                .allPaths()
                    .authenticateWith()
                        .form()
                            .loginPage("/login.xhtml")
                            .errorPage("/error.xhtml")
                .forPath("/logout")
                    .logout()
                    .redirectTo("/index.html");
    }

}

allPathsメソッドで、すべてのパスを以下の設定の対象にしています。
authenticateWithメソッドは、認証を設定するために用います。
formメソッドは、FORM認証の設定に用います。
loginPageメソッドは、ログインページを設定します。認証されていないユーザがアクセスした場合、ここで設定したページへリダイレクトします。ここでは、/login.xhtmlが設定されています。
errorPageメソッドは、ログインに失敗した場合のリダイレクト先を設定します。ここでは、/error.xhtmlが設定されています。
forPathメソッドは、パスに関する設定を行います。ここでは、/logoutについての設定を行っています。
logoutメソッドは、ログアウトの機能を与えています。ここでは、/logoutがリクエストされた場合、ログアウトが実行されるようになります。
redirectToメソッドは、処理完了後のリダイレクト先を設定します。ここでは、ログアウト後に/index.htmlにリダイレクトされるように設定しています。

/index.html

<html>
<head>
    <meta http-equiv="Refresh" content="0; URL=home.jsf"/>
</head>
</html>

最初に/index.htmlにアクセスした場合、/index.htmlも保護されているため、/login.xhtmlが呼び出されます。

/login.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<body>
<form method="POST" action="j_security_check">
 <input type="text" name="j_username"/>
 <input type="password" name="j_password"/>
 <input type="submit" name="login" value="Login"/>
</form>
<p>
Tip: you can login with a username/password of jane/abcd1234.
</p>

</body>
</html>

login
ここでは、ログイン名にjane、パスワードにabcd1234を入力します。
ログインに成功すると、デフォルトではコンテキストパス(/)へ遷移します。
この例の場合、/index.htmlへ遷移後、<meta http-equiv=”Refresh” content=”0; URL=home.jsf”/>があるので、/home.xhtmlが呼び出されます。
ログインに失敗した場合、/error.xhtmlへ遷移します。

/home.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<body>
    <ui:fragment rendered="#{identity.loggedIn}">
        <meta http-equiv="Refresh" content="0; URL=protected/private.jsf"/>
    </ui:fragment>

    <ui:fragment rendered="#{not identity.loggedIn}">
        <h:form>
            <p>
                This is a public resource.
            </p>
            <p>
                Click <h:outputLink value="protected/private.jsf">here</h:outputLink> here to access the protected resources.
            </p>

            Tip: you can login with a username/password of jane/abcd1234.
        </h:form>
    </ui:fragment>
</body>
</html>

ログイン後、5行目の#{identity.loggedIn}はtrueとなり、/protected/private.xhtmlへ遷移します。

/protected/private.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<body>
    <p>Hi <b>#{identity.account.loginName}</b>, this resource is protected. If you reach this page is because you're authenticated.</p>
    <p>Click here to <a href="#{request.contextPath}/logout">Logout</a></p>
</body>
</html>

private
5行目の#{identity.account.loginName}には、ログイン名janeが入ります。
6行目のリンクをクリックすると、HttpSecurityConfiguration.javaでの設定に基づいて、ログアウトと/index.htmlへのリダイレクトが実行されます。
この例の場合、/index.htmlも保護されているため、ログアウト後に/index.htmlへリダイレクトした結果、/login.xhtmlへ遷移します。

/error.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<body>
    Sorry, there was an error!
</body>
</html>

error
ログインに失敗した場合、このページが表示されます。