当前位置: 首页 > 工具软件 > Seam Security > 使用案例 >

myfaces1.x,seam1.2.1和spring security 2.0(acegi)的集成

傅志诚
2023-12-01
acegi升级为spring security2.0后配置有很大不同,按照老的配置方式需要更改很多包路径,以及属性名,这需要在源代码中查找,如果配置好是没有问题的。新的配置是基于老的方式并进行了简化,就直接给出我的配置作为参考:
新的配置方式:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
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.4.xsd">

<security:http auto-config="true" access-denied-page="/login.faces">
<security:intercept-url pattern="/jsp/*/yourpath" access="ROLE_COMMISSAR, ROLE_DRAFTOFFICE, ROLE_TRANSACT, ROLE_DRAFTSUPER, ROLE_URGER"/>
<security:intercept-url pattern="/jsp/*/yourpath" access="ROLE_LETTERPUBLIC,ROLE_LETTERTRANSACT,ROLE_LETTERTASTER,ROLE_LETTERPROCESS"/>
<security:intercept-url pattern="/jsp/*/yourpath" access="ROLE_POPULARSUBMIT,ROLE_POPULARPROCESS"/>
<security:intercept-url pattern="/**/*.gif" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/**/*.gif" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/**/*.jpg" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/**/*.css" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/login.faces" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<security:form-login
default-target-url="/"
always-use-default-target="true"
login-page="/login.faces"
login-processing-url="/j_spring_security_check"
authentication-failure-url="/login.faces"
/>
<security:anonymous key="doesNotMatter" username="anonymousUser"/>
<security:concurrent-session-control
max-sessions="1"
exception-if-maximum-exceeded="false"
expired-url="/login.faces"/>
<security:logout logout-url="/j_spring_security_logout" logout-success-url="/login.faces" />
</security:http>

<security:authentication-provider>
<security:jdbc-user-service
data-source-ref="dataSource"
users-by-username-query="SELECT USER_, PASSWORD_, SIGN_ FROM ORM_USER WHERE USER_= ?"
authorities-by-username-query="select user0_.USER_ as col_0_0_, role2_.VALUE_ as col_1_0_ from ORM_USER user0_ inner join ORM_USER_ROLE roles1_ on user0_.ID_=roles1_.USERID_ inner join ORM_ROLE role2_ on roles1_.ROLEID_=role2_.ID_ where user0_.USER_=?"
cache-ref="userCache"
/>
</security:authentication-provider>

<bean id="userCache" class="org.springframework.security.providers.dao.cache.EhCacheBasedUserCache">
<property name="cache"><ref local="userCacheBackend"/></property>
</bean>

<bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager"> <ref local="cacheManager"/> </property>
<property name="cacheName"> <value>userCache</value> </property>
</bean>

<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>

<!-- This bean is optional; it isn't used by any other bean as it only listens and logs -->
<bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener" />

<!-- <bean id="customAuthenticationFilter" class="**.authorization.CustomAuthenticationFilter">
<security:custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>
</bean> 使用自定义的过滤器,参考spring security中不同的自定义位置名称-->

</beans>


jsf集成:

jsf跳转机制使用的是redirect,所以登录的信息就不能很好的传递给acegi,根据myfaces wiki提供的方式:创建登录使用的loginBean,注意不要使用seam的@Name注册为seam组件,直接使用jsf的managed-bean机制注册成backingbean
	public String login() throws IOException, ServletException
{
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();

RequestDispatcher dispatcher = ((ServletRequest) context.getRequest())
.getRequestDispatcher("/j_spring_security_check");

dispatcher.forward((ServletRequest) context.getRequest(),
(ServletResponse) context.getResponse());
FacesContext.getCurrentInstance().responseComplete();
// It's OK to return null here because Faces is just going to exit.
logger.debug("=================================================" +
((HttpServletRequest)context.getRequest()).getSession().getId());
HttpSession session = ((HttpServletRequest)context.getRequest()).getSession();
// session.setAttribute(LOGIN_PROCESS, new Boolean(true));
//这是调整seam的集成部分
ContextControl.instance().begin(session);
//createAuthData(session, user);
return null;
}



public String logout() throws IOException, ServletException
{
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();

RequestDispatcher dispatcher = ((ServletRequest) context.getRequest())
.getRequestDispatcher("/j_spring_security_logout");

dispatcher.forward((ServletRequest) context.getRequest(),
(ServletResponse) context.getResponse());

FacesContext.getCurrentInstance().responseComplete();
// It's OK to return null here because Faces is just going to exit.
logger.debug("=================================================" +
((HttpServletRequest)context.getRequest()).getSession().getId());
HttpSession session = ((HttpServletRequest)context.getRequest()).getSession();
//这是调整seam的集成部分
ContextControl.instance().begin(session);
return null;
}

jsf页面的做法
<t:inputText id="j_username" forceId="true" styleClass="form1" size="20" value="#{loginBean.userName}"></t:inputText>
<t:inputSecret id="j_password" forceId="true" styleClass="form1" size="20" value="#{loginBean.password}"
<h:commandButton value="登录" action="#{loginBean.login}"></h:commandButton>


seam的集成:
因为在登录过程中,acegi注销掉了上一个session,创建了一个新的session,那么SeamListener这个session监听器就会执行sessionDestroyed和sessionCreated,但是seam并不认为你是真正销毁了所有的上下文包括request和application,我认为这是seam为了在页面过期的环境中使用,因为它的原则是由seam的Seam.invalidateSession()来处理session的销毁(事实上根本就没有销毁session而是给出了销毁的标记,也许这样就可以由它的内部解决所有上下文的管理)。那么就会产生异常,同样在这一次请求中seam的阶段监听也会产生异常,因为Conversation绑定的session仍然是已经过期的session。我的做法很傻,hack了!

一:自定义SeamListener
	@Override
public void sessionCreated(HttpSessionEvent event) {
// TODO Auto-generated method stub
super.sessionCreated(event);
}

@Override
public void sessionDestroyed(HttpSessionEvent event) {
// TODO Auto-generated method stub
// event.getSession().getAttribute("org.jboss.seam.sessionInvalid");
try {
super.sessionDestroyed(event);
} catch (IllegalStateException e) {
// TODO: handle exception
Seam.invalidateSession();
HttpSession session = event.getSession();
ContextControl.instance().end(session.getServletContext(), new ServletSessionImpl(session));
}
}


二 创建自定义的seam上下文控制类,直接操作各个上下文,对于session销毁和创建手动去清理或者创建其他各上下文实例。因为各个上下文的操作都是protect,所以只有将类建在它的包下了!
package org.jboss.seam.contexts;

import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.core.ConversationEntries;
import org.jboss.seam.servlet.ServletSessionImpl;

public class ContextControl {
private static final Log log = LogFactory.getLog(ContextControl.class);

private static final ContextControl contextControl = new ContextControl();

public static ContextControl instance() {
return contextControl;
}

public void begin(HttpSession session) {
boolean applicationContextActive = Contexts.isApplicationContextActive();
boolean eventContextActive = Contexts.isEventContextActive();
boolean conversationContextActive = Contexts.isConversationContextActive();
if ( !applicationContextActive )
{
Context tempApplicationContext = new WebApplicationContext(session.getServletContext());
Contexts.applicationContext.set(tempApplicationContext);
}
Context tempEventContext = null;
if ( !eventContextActive )
{
tempEventContext = new MapContext(ScopeType.EVENT);
Contexts.eventContext.set(tempEventContext);
}
Context tempConversationContext = null;
if ( !conversationContextActive )
{
tempConversationContext = new MapContext(ScopeType.CONVERSATION);
Contexts.conversationContext.set(tempConversationContext);
}

Context tempSessionContext = new WebSessionContext(new ServletSessionImpl(session));
Contexts.sessionContext.set(tempSessionContext);

//instantiate all session-scoped @Startup components
for ( String name : Contexts.getApplicationContext().getNames() )
{
Object object = Contexts.getApplicationContext().get(name);
if ( object!=null && (object instanceof Component) )
{
Component component = (Component) object;
if ( component.isStartup() && component.getScope() == ScopeType.SESSION )
{
startup(component);
}
}
}
}

public void end(ServletContext servletContext, ContextAdaptor session) {

Context tempApplicationContext = new WebApplicationContext(servletContext);
Contexts.applicationContext.set(tempApplicationContext);

//this is used just as a place to stick the ConversationManager
Context tempEventContext = new MapContext(ScopeType.EVENT);
Contexts.eventContext.set(tempEventContext);

//this is used (a) for destroying session-scoped components
//and is also used (b) by the ConversationManager
Context tempSessionContext = new WebSessionContext(session);
Contexts.sessionContext.set(tempSessionContext);

Set<String> conversationIds = ConversationEntries.instance().getConversationIds();
log.debug("destroying conversation contexts: " + conversationIds);
for (String conversationId: conversationIds)
{
Lifecycle.destroyConversationContext(session, conversationId);
}

//we need some conversation-scope components for destroying
//the session context...
Context tempConversationContext = new MapContext(ScopeType.CONVERSATION);
Contexts.conversationContext.set(tempConversationContext);

log.debug("destroying session context");
Contexts.destroy(tempSessionContext);
Contexts.sessionContext.set(null);

Contexts.destroy(tempConversationContext);
Contexts.conversationContext.set(null);

Contexts.destroy(tempEventContext);
Contexts.eventContext.set(null);

Contexts.applicationContext.set(null);
}

private static void startup(Component component)
{
if ( component.isStartup() )
{
for ( String dependency: component.getDependencies() )
{
Component dependentComponent = Component.forName(dependency);
if (dependentComponent!=null)
{
startup( dependentComponent );
}
}
}

if ( !component.getScope().getContext().isSet( component.getName() ) )
{
log.info("starting up: " + component.getName());
component.newInstance();
}
}
}


这些只是我的做法!
注意的地方:
websphere 6.1.0.3以上版本默认对于找不到资源的url路径直接过滤到错误页面,这样spring的/j_spring_security_check是访问不到的,需要参考[url]http://topic.csdn.net/u/20080620/14/9c05c7d7-a6a0-4646-95b2-4bdadcb1002c.html[/url]
 类似资料: