스프링 시큐리티 3.0 RC1이 나온지가 언젠데 스프링 웹 플로우는 아직도 시큐리티 2점대 기준이더군요. 스프링 웹 플로우 때문에 시큐리티 버전을 낮출수도 없는 노릇이고, 안 돌아가는 클래스 소스를 가져다 스프링 시큐리티 3.X에서 돌아가도록 수정했습니다.

웹 플로우 2.X는 아직 자바 5 기능을 도입하지 않았더군요. 스프링 플젝만 자바 5 기준으로 변경한건지.. 흠.. 그래서 고치는 김에 자바5 Generic을 도입해서 타입 세이프티를 보장하게 코드를 아주 약간만 손 봤습니다.

필요하신 분은 쓰세요~

/*
 * Copyright 2004-2009 the original author or authors.
 *
 * 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.
 *
 */
package springsprout.common.webflow;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.vote.AbstractAccessDecisionManager;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.StateDefinition;
import org.springframework.webflow.definition.TransitionDefinition;
import org.springframework.webflow.execution.EnterStateVetoException;
import org.springframework.webflow.execution.FlowExecutionListenerAdapter;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.security.SecurityRule;

/**
 * Flow security integration with Spring Security
 *
 * @author Scott Andrews
 * @author Keesun Baik(Whiteship)
 */
public class SecurityFlowExecutionListener extends FlowExecutionListenerAdapter {

    private AccessDecisionManager accessDecisionManager;

    /**
     * Get the access decision manager that makes flow authorization decisions.
     * @return the decision manager
     */
    public AccessDecisionManager getAccessDecisionManager() {
        return accessDecisionManager;
    }

    /**
     * Set the access decision manager that makes flow authorization decisions.
     * @param accessDecisionManager the decision manager to user
     */
    public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
        this.accessDecisionManager = accessDecisionManager;
    }

    public void sessionCreating(RequestContext context, FlowDefinition definition) {
        SecurityRule rule = (SecurityRule) definition.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME);
        if (rule != null) {
            decide(rule, definition);
        }
    }

    public void stateEntering(RequestContext context, StateDefinition state) throws EnterStateVetoException {
        SecurityRule rule = (SecurityRule) state.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME);
        if (rule != null) {
            decide(rule, state);
        }
    }

    public void transitionExecuting(RequestContext context, TransitionDefinition transition) {
        SecurityRule rule = (SecurityRule) transition.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME);
        if (rule != null) {
            decide(rule, transition);
        }
    }

    /**
     * Performs a Spring Security authorization decision. Decision will use the provided AccessDecisionManager. If no
     * AccessDecisionManager is provided a role based manager will be selected according to the comparison type of the
     * rule.
     * @param rule the rule to base the decision
     * @param object the execution listener phase
     */
    protected void decide(SecurityRule rule, Object object) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        List<ConfigAttribute> configAttrs = getConfigAttributes(rule);
        if (accessDecisionManager != null) {
            accessDecisionManager.decide(authentication, object, configAttrs);
        } else {
            AbstractAccessDecisionManager abstractAccessDecisionManager;
            List<AccessDecisionVoter> voters = new ArrayList<AccessDecisionVoter>();
            voters.add(new RoleVoter());
            if (rule.getComparisonType() == SecurityRule.COMPARISON_ANY) {
                abstractAccessDecisionManager = new AffirmativeBased();
            } else if (rule.getComparisonType() == SecurityRule.COMPARISON_ALL) {
                abstractAccessDecisionManager = new UnanimousBased();
            } else {
                throw new IllegalStateException("Unknown SecurityRule match type: " + rule.getComparisonType());
            }
            abstractAccessDecisionManager.setDecisionVoters(voters);
            abstractAccessDecisionManager.decide(authentication, object, configAttrs);
        }
    }

    /**
     * Convert SecurityRule into a form understood by Spring Security
     * @param rule the rule to convert
     * @return list of ConfigAttributes for Spring Security
     */
    protected List<ConfigAttribute> getConfigAttributes(SecurityRule rule) {
        List<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
        Iterator<String> attributeIt = rule.getAttributes().iterator();
        while (attributeIt.hasNext()) {
            configAttributes.add(new SecurityConfig(attributeIt.next()));
        }
        return configAttributes;
    }
}

ps: 스프링 소스 직원님들.. 웹 플로우도 빨랑 3.0으로 올려줘요. 한 달에 이슈 한 개 처리하는건 너무 심한거 아니삼..??