90-3.アクセス認可設定を自動リロードするサンプル
(Security3.x系)

概要

Spring Security 3.1系のサンプルです。

自作したフィルタを使いたいときにどうすればよいか?

そのサンプルと思っていください。

サンプル全体の説明もお読みください。

目標

まずはゴールを示します。

内容は2.x系の記事と同じです。そちらを参照ください。

また、各クラスの説明も2.x系の記事と同じなので省略しています。

実際のサンプル

自作クラス(/src/com/my/security/PuppetDefinitionSource.java)

package com.my.security;

import java.util.Collection;

import org.springframework.security.access.ConfigAttribute;

import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

/**

* 1つのリクエストの途中で情報が変わらないように、スレッドローカルで認可の情報を管理するクラス。

* このクラスを介して、FilterSecurityInterceptorの認可定義を変更することを目的としたクラス。

*/

public class PuppetDefinitionSource implements FilterInvocationSecurityMetadataSource {

static private ThreadLocal<FilterInvocationSecurityMetadataSource> localDef = new ThreadLocal<FilterInvocationSecurityMetadataSource>();

@Override

public Collection<ConfigAttribute> getAllConfigAttributes() {

return localDef.get().getAllConfigAttributes();

}

@Override

public Collection<ConfigAttribute> getAttributes(Object securedObject)

throws IllegalArgumentException {

return localDef.get().getAttributes(securedObject);

}

@Override

public boolean supports(Class<?> c) {

return localDef.get().supports(c);

}

public void setDefinitionSource(FilterInvocationSecurityMetadataSource def){

localDef.set(def);

}

}

Spring Security2.x系との違いは、importしているクラスのパッケージ名と、Class<?>のようにGenericになったところくらいです。

実装を変える必要はありませんでした。

自作クラス(/src/com/my/security/DefinitionSourceRefreshFilter.java)

package com.my.security;

import java.io.File;

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.BeanFactory;

import org.springframework.beans.factory.BeanFactoryAware;

import org.springframework.beans.factory.InitializingBean;

import org.springframework.context.support.FileSystemXmlApplicationContext;

import org.springframework.core.Ordered;

import org.springframework.core.io.Resource;

import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

import org.springframework.util.Assert;

/**

* リクエストのたびにFilterSecurityInterceptorに認可定義currentDefSourceを設定するだけのフィルタ。

* 初めてのdoFilter時に強引にFilterSecurityInterceptorの認可定義(FilterInvocationDefinitionSource)を

* localDefinitionSourceに変更している。

* FilterSecurityInterceptorフィルタの前に設定すること。

*/

public class DefinitionSourceRefreshFilter

implements Filter, Ordered, InitializingBean, BeanFactoryAware {

private FilterSecurityInterceptor nextFilter;

private boolean isAlreadySetDef = false;

private FilterInvocationSecurityMetadataSource currentDefSource;

private PuppetDefinitionSource localDefinitionSource = new PuppetDefinitionSource();

private Resource resource;

private long curFileTimestamp = 0L;

private BeanFactory beanFactory;

@Override

public int getOrder() {

return 1650; //FilterSecurityInterceptorが1700なのでそれより前の値にした

}

@Override

public void destroy() {

}

@Override

public void init(FilterConfig conf) throws ServletException {

}

@Override

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)

throws IOException, ServletException {

//nextFilterにカスタマイズクラスを設定しておく

if(!this.isAlreadySetDef){

this.nextFilter.setSecurityMetadataSource(this.localDefinitionSource);

this.isAlreadySetDef = true;

}

//定義の読み込みと定義の設定

reload();

this.localDefinitionSource.setDefinitionSource(this.currentDefSource);

chain.doFilter(req, res);

}

/**タイムスタンプが新しい場合、

* AppilicationContextを読み込み直し、currentDefSource を設定しなおす

* @throws Exception

*/

synchronized

protected void reload() throws ServletException{

FileSystemXmlApplicationContext securityContext = null;

try{

//タイムスタンプが新しくない場合は何もしない

long fileTimestamp = this.resource.lastModified();

if(this.curFileTimestamp == fileTimestamp) return;

//context xmlファイルの読み込み

File file = this.resource.getFile();

securityContext = new FileSystemXmlApplicationContext(file.getAbsolutePath());

FilterInvocationSecurityMetadataSource def

= securityContext.getBean(FilterInvocationSecurityMetadataSource.class);

//Sourceを設定する

if(def == null){

throw new RuntimeException("FilterInvocationDefinitionSourceがNULLだった。file=" + file);

}

this.currentDefSource = def;

this.curFileTimestamp = fileTimestamp;

}catch(Exception e){

throw new ServletException("security contextをリロード中にエラー発生", e);

}finally{

//applicationContextのclose

if(securityContext != null){

try{ securityContext.close(); }catch(Exception e){}

}

}

}

//-------------------------------------------------

@Override

public void afterPropertiesSet() throws Exception {

this.nextFilter = this.beanFactory.getBean(FilterSecurityInterceptor.class);

Assert.notNull(this.nextFilter, "filter is required.");

Assert.notNull(this.resource, "resource is required.");

reload();

}

public void setResource(Resource resource) {

this.resource = resource;

}

@Override

public void setBeanFactory(BeanFactory factory) throws BeansException {

this.beanFactory = factory;

}

}

Spring Security2.x系からの変更点はパッケージ名が変更されたくらいです。

リロード用の外部Spring設定ファイル(c:/temp/security3.xml)

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:util="http://www.springframework.org/schema/util"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:sec="http://www.springframework.org/schema/security"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/util

http://www.springframework.org/schema/util/spring-util-3.0.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

http://www.springframework.org/schema/security

http://www.springframework.org/schema/security/spring-security-3.1.xsd">

<!-- 認可のカスタマイズ -->

<sec:filter-security-metadata-source id="myDefinitionSource">

<sec:intercept-url pattern="/error.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>

<sec:intercept-url pattern="/login-provs.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>

<sec:intercept-url pattern="/403.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>

<sec:intercept-url pattern="/top2.jsp" access="ROLE_ADMIN,ROLE_READ"/>

<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>

</sec:filter-security-metadata-source>

</beans>

2.x系からの変更点は、タグ名の変更です。

Spring設定ファイル(/WEB-INF/spring/applicationContext-security-interc.xml)

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:util="http://www.springframework.org/schema/util"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:sec="http://www.springframework.org/schema/security"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/util

http://www.springframework.org/schema/util/spring-util-3.0.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

http://www.springframework.org/schema/security

http://www.springframework.org/schema/security/spring-security-3.1.xsd">

<!-- 認可のカスタマイズ -->

<!-- SpringSecurityのメイン設定

-->

<sec:http path-type="ant" access-denied-page="/403.jsp" auto-config="false" >

<sec:intercept-url pattern="/error.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>

<sec:intercept-url pattern="/login-provs.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>

<sec:intercept-url pattern="/403.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>

<sec:intercept-url pattern="/top2.jsp" access="ROLE_ADMIN"/>

<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>

<sec:form-login login-page="/login-provs.jsp" default-target-url="/top.jsp"

authentication-failure-url="/login-provs.jsp?error=true"/>

<sec:logout logout-url="/logout" logout-success-url="/login-provs.jsp" invalidate-session="true"/>

<sec:anonymous granted-authority="ROLE_ANONYMOUS"/>

<!-- フィルタの挿入位置を指定する。ここでは1つ前を指定している。 -->

<sec:custom-filter ref="accessDefRefreshFilter" before="FILTER_SECURITY_INTERCEPTOR"/>

</sec:http>

<!-- 認証手形発行所 -->

<sec:authentication-manager>

<sec:authentication-provider>

<sec:user-service>

<sec:user name="taro" password="taro" authorities="ROLE_ADMIN"/>

<sec:user name="hanako" password="hanako" authorities="ROLE_READ"/>

</sec:user-service>

</sec:authentication-provider>

</sec:authentication-manager>

<!--

自作のフィルタ。

-->

<bean id="accessDefRefreshFilter"

class="com.my.security.DefinitionSourceRefreshFilter">

<property name="resource" value="file:c:/temp/security3.xml" />

</bean>

</beans>

Spring Security2.x系からの変更点は、タグの使い方です。

2.x系では、authentication-manager タグを記述しなくてもauthentication-providerタグを使用できましたが、できなくなったようです。

3.x系では上記のように、httpタグ内に記述してください。

Created Date: 2013/07/07