[Spring Wev Flow(2.0.8)] SecurityFlowExecutionListener 패치 for Spring Security 3.X
스프링 시큐리티 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으로 올려줘요. 한 달에 이슈 한 개 처리하는건 너무 심한거 아니삼..??