acegi security实践教程—简单定制logoutFilter


  回顾:

    logoutFilter过滤器,我已经带大家了解过了。其中注销是由handler.logout完成的。这就需要在配置文件中配置具体的handler,比如securitycontextlogouthandler,或者tokenbasedremembermeservices,大家可以看到logouthandler具体的实现类如下:
 
  
  若我程序中就想只用SecurityContextLogoutHandler,对于利用cookie自动登录的效果暂时不需要,我们应该如何做呢?
  第一:利用上篇博客的做法,在list中只配置SecurityContextLogoutHandler
  第二:重写这个logoutFilter,程序中使用自己的过滤器。
  
  因为上篇博客已经讲解了关键代码,其实那几句关键代码几乎就是整个类代码了。这篇博客同上篇博客的作用一样,实现注销功能,只不过是利用自己Filter而已。

  具体开发步骤:

  开发环境:

acegi security实践教程—简单定制logoutFilter_第1张图片

   配置文件:

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

<beans xmlns="http://www.springframework.org/schema/beans"
	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">
	
	<!-- 通过过滤连形式,acegi提供很多filter,其中过滤器执行也有一定的顺序 ,同事支持正则和ant匹配-->
	
	<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
		<property name="filterInvocationDefinitionSource">
			<value>
				PATTERN_TYPE_APACHE_ANT
				/**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
			</value>
		</property>
	</bean>
   	
	<bean id="httpSessionContextIntegrationFilter" 
   		class="org.acegisecurity.context.HttpSessionContextIntegrationFilter" />	
   		
    <!--自己改写的注销功能-->
    <bean id="logoutFilter"  class="com.extend.MyLogoutFilter">
      <property name="logoutSuccessUrl"  value="/login.jsp"/>
    </bean>
   		
 <!-- 表单认证处理filter -->  
     <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">  
        <!-- 认证管理器,然后委托给Provides -->
        <property name="authenticationManager" ref="authenticationManager"/>  
        <!-- 认证失败后转向的url,包含出错信息的的登陆页面 -->
        <property name="authenticationFailureUrl" value="/login.jsp?login_error=1"/>  
        <!-- 登陆成功后转向的url -->
        <property name="defaultTargetUrl" value="/userinfo.jsp"/>  
        <!-- 登陆的url,这个是默认的acegi自带的 -->
        <property name="filterProcessesUrl" value="/j_acegi_security_check"/>  
    </bean> 
	
	<bean id="authenticationManager"
		class="org.acegisecurity.providers.ProviderManager">
		<property name="providers">
			<list>
				<ref local="daoAuthenticationProvider" />
			</list>
		</property>
	</bean>
	
	
    <!-- 从数据库中读取用户信息验证身份 -->
	<bean id="daoAuthenticationProvider"
		class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
		<property name="userDetailsService" ref="inMemDaoImpl" />
	</bean>

    <!-- 基于内存实现方式-->
	<bean id="inMemDaoImpl"
		class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
		<property name="userMap">
			<value>
				test=1,ROLE_USER
				lisi=1,ROLE_SUPERVISOR
				zhangsan=1,ROLE_SUPERVISOR,disabled
			</value>
		</property>
	</bean>
	
	<!-- exception filter -->
   <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
     <!-- 尚未登录, 进入非法(未认证不可访问)区域 -->  
        <property name="authenticationEntryPoint">  
            <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">  
                <property name="loginFormUrl" value="/login.jsp"/>  <!--若没登陆,则转向 用户登陆页面 -->
                <property name="forceHttps" value="false"/>  <!-- 是否强制使用https -->
            </bean>  
        </property> 
     <!-- 登录后, 进入非授权区域 --> 
        <property name="accessDeniedHandler">  
            <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">  
                <property name="errorPage" value="/accessDenied.jsp"/>  <!-- 进入无权限页面 ,根据需求写相应的信息-->
            </bean>  
        </property>  
    </bean>      
	
   <bean id="filterInvocationInterceptor"
		class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
		<property name="authenticationManager" ref="authenticationManager" />
		<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager" />
		<property name="objectDefinitionSource">
			<value><![CDATA[
				CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
				PATTERN_TYPE_APACHE_ANT
				/userinfo.jsp=ROLE_SUPERVISOR
			]]></value>
		</property>
	</bean>

	<bean id="httpRequestAccessDecisionManager"
		class="org.acegisecurity.vote.AffirmativeBased">
		<property name="decisionVoters">
			<list>
				<bean class="org.acegisecurity.vote.RoleVoter"/>
			</list>
		</property>
	</bean>
</beans>

  自己的Filter:

package com.extend;
	/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
	 *
	 * 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.
	 */

	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 javax.servlet.http.HttpServletRequest;
	import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

	import org.acegisecurity.Authentication;
	import org.acegisecurity.context.SecurityContextHolder;
	import org.apache.commons.logging.Log;
	import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;

	/**
	 * Logs a principal out.
	 * <p>
	 * Polls a series of {@link LogoutHandler}s. The handlers should be specified in the order they are required.
	 * Generally you will want to call logout handlers <code>TokenBasedRememberMeServices</code> and
	 * <code>SecurityContextLogoutHandler</code> (in that order).
	 * </p>
	 * <p>
	 * After logout, the URL specified by {@link #logoutSuccessUrl} will be shown.
	 * </p>
	 * <p>
	 * <b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the
	 * {@link org.acegisecurity.util.FilterToBeanProxy}.
	 * </p>
	 *
	 * @author Ben Alex
	 * @version $Id: LogoutFilter.java 1967 2007-08-28 11:37:05Z luke_t $
	 */
	public class MyLogoutFilter implements Filter {
	    //~ Static fields/initializers =====================================================================================


	    //~ Instance fields ================================================================================================

	    private String filterProcessesUrl = "/j_acegi_logout";
	    private String logoutSuccessUrl;


	    //~ Methods ========================================================================================================

	    /**
	     * Not used. Use IoC container lifecycle methods instead.
	     */
	    public void destroy() {
	    }

	    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
	            ServletException {
	        if (!(request instanceof HttpServletRequest)) {
	            throw new ServletException("Can only process HttpServletRequest");
	        }

	        if (!(response instanceof HttpServletResponse)) {
	            throw new ServletException("Can only process HttpServletResponse");
	        }

	        HttpServletRequest httpRequest = (HttpServletRequest) request;
	        HttpServletResponse httpResponse = (HttpServletResponse) response;

	        if (requiresLogout(httpRequest, httpResponse)) {
	            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
	            
				HttpSession session = httpRequest.getSession(false);
				if (session != null) {
					session.invalidate();
				}

			 SecurityContextHolder.clearContext();
	         sendRedirect(httpRequest, httpResponse, logoutSuccessUrl);
	         return;
	        }

	        chain.doFilter(request, response);
	    }

	    /**
	     * Not used. Use IoC container lifecycle methods instead.
	     *
	     * @param arg0 ignored
	     *
	     * @throws ServletException ignored
	     */
	    public void init(FilterConfig arg0) throws ServletException {
		}

	    /**
	     * Allow subclasses to modify when a logout should take place.
	     *
	     * @param request the request
	     * @param response the response
	     *
	     * @return <code>true</code> if logout should occur, <code>false</code> otherwise
	     */
	    protected boolean requiresLogout(HttpServletRequest request, HttpServletResponse response) {
	        String uri = request.getRequestURI();
	        int pathParamIndex = uri.indexOf(';');

	        if (pathParamIndex > 0) {
	            // strip everything from the first semi-colon
	            uri = uri.substring(0, pathParamIndex);
	        }

	        int queryParamIndex = uri.indexOf('?');

	        if (queryParamIndex > 0) {
	            // strip everything from the first question mark
	            uri = uri.substring(0, queryParamIndex);
	        }

	        if ("".equals(request.getContextPath())) {
	            return uri.endsWith(filterProcessesUrl);
	        }

	        return uri.endsWith(request.getContextPath() + filterProcessesUrl);
	    }

	    /**
	     * Allow subclasses to modify the redirection message.
	     *
	     * @param request  the request
	     * @param response the response
	     * @param url      the URL to redirect to
	     *
	     * @throws IOException in the event of any failure
	     */
	    protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
	            throws IOException {
	        if (!url.startsWith("http://") && !url.startsWith("https://")) {
	            url = request.getContextPath() + url;
	        }

	        response.sendRedirect(response.encodeRedirectURL(url));
	    }

	    public void setFilterProcessesUrl(String filterProcessesUrl) {
	        Assert.hasText(filterProcessesUrl, "FilterProcessesUrl required");
	        this.filterProcessesUrl = filterProcessesUrl;
	    }

	    protected String getFilterProcessesUrl() {
	        return filterProcessesUrl;
	    }

		public String getLogoutSuccessUrl() {
			return logoutSuccessUrl;
		}

		public void setLogoutSuccessUrl(String logoutSuccessUrl) {
			this.logoutSuccessUrl = logoutSuccessUrl;
		}
	}

  项目下载:

  ps:哈哈,这个logoutFilter说白了就是自己改写的,这样的话,配置文件中只配置注销后转向的页面即可。至于如何改写的,这个大家看一眼源码即可,害羞的速速略过啦~

  预告:

  前几篇完成了一个小雏形,至于这篇算小插曲吧,前几篇从数据库读取用户身份信息是基于内存形式,而非真正数据库中,为了进一步完善,接下来我们开始设计自己的系统或者如何嵌入现有系统。
  

你可能感兴趣的:(LogoutFilter,Acegi,form认证)