欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Acegi鉴权管理之基础模式(BASIC)

程序员文章站 2022-04-26 11:42:13
...

Acegi久负盛名,这个家伙是一个spring中广泛使用的认证和安全工具,最初由spring社区爱好者发起,目的是为spring应用提供一个安全服务,比如用户认证及授权等。后来spring官方觉得这个东西很不错,就收编了,并且在2006年发布了spring官方的1.0版本。虽然是基于Acegi,但springsecurity已经在原有基础上增加了很多新的特性进来。为了能够方便一窥Acegi的真容,我们通过一个basic模式来看下Acegi是如何来处理用户认证及授权工作。

1、配置安全所需过滤器org.acegisecurity.util.FilterChainProxy,填充 filterInvocationDefinitionSource 属性如下所示:

<bean id ="filterChainProxy" class= "org.acegisecurity.util.FilterChainProxy">
    <property name ="filterInvocationDefinitionSource">
	 <value>
	     PATTERN_TYPE_APACHE_ANT
	     /**=basicProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
	 </value>
    </property>
</bean>

 注:默认情况下,在filterInvocationDefinitionSource属性中指明使用PATTERN_TYPE_APACHE_ANT,则说明,这里的配置信息是启用Apache Ant路径风格的URL匹配模式,FilterInvocationDefinitionSourceEditor会实例化PathBasedFilterInvocationDefinitionMap实例。如果这里没有指定则采用默认的正是表达式,此时RegExpBasedFilterInvocationDefinitionMap会被实例化。

FilterInvocationDefinitionSourceEditor在进行初始化过程中,acegi源码处理过程的片段代码如下:

if ((s == null) || "".equals(s)) {
    // Leave target object empty
    source.setDecorated(new RegExpBasedFilterInvocationDefinitionMap());
} else {
    // Check if we need to override the default definition map
    if (s.lastIndexOf(DIRECTIVE_PATTERN_TYPE_APACHE_ANT) != -1) {
        source.setDecorated(new PathBasedFilterInvocationDefinitionMap());

        if (logger.isDebugEnabled()) {
            logger.debug(("Detected " + DIRECTIVE_PATTERN_TYPE_APACHE_ANT
                + " directive; using Apache Ant style path expressions"));
        }
    } else {
        source.setDecorated(new RegExpBasedFilterInvocationDefinitionMap());
    }

    if (s.lastIndexOf(DIRECTIVE_CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON) != -1) {
        if (logger.isDebugEnabled()) {
            logger.debug("Detected " + DIRECTIVE_CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                + " directive; Instructing mapper to convert URLs to lowercase before comparison");
        }

        source.setConvertUrlToLowercaseBeforeComparison(true);
    }

 另外需要说明的是,PathBasedFilterInvocationDefinitionMap和RegExpBasedFilterInvocationDefinitionMap都是接口FilterInvocationDefinitionSource的实现类。

FilterInvocationDefinitionSourceEditor在初始化athBasedFilterInvocationDefinitionMap和RegExpBasedFilterInvocationDefinitionMap类时,提供了2个常量用:

  • DIRECTIVE_CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON:FilterInvocationDefinitionSourceEditor类通过该常量的设值情况判断是否对当前路径进行小写转换
  • PATTERN_TYPE_APACHE_ANT:FilterInvocationDefinitionSourceEditor通过这个常量决定具体是采用正则模式还是ant路径风格模式。

我们这里根据执行顺序,指明了3个filter类过滤执行安全策略:basicProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor

basicProcessingFilter:在Basic模式下,用户名和密码通过对称加密算法,将用户的登录信息存放在http请求的header信息中。服务器在收到浏览器发送来的验证请求后,将加密过的用户名密码通过Apache提供的commons-codec工具包中的org.apache.commons.codec.binary.Base64进行解码。

例如:发起一次请求验证通过后的http头摘要如下:

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:zh-CN,zh;q=0.8
Authorization:Basic dGVzdDox
Connection:keep-alive
Cookie:JSESSIONID=A749345B4E56805343189AA5A1223655
Host:localhost:8080
Referer:http://localhost:8080/rest-common-acegi/secure.jsp
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36

 

basicProcessingFilter片段代码如下:

String header = httpRequest.getHeader("Authorization");

if (logger.isDebugEnabled()) {
    logger.debug("Authorization header: " + header);
}

if ((header != null) && header.startsWith("Basic ")) {
    String base64Token = header.substring(6);
    String token = new String(Base64.decodeBase64(base64Token.getBytes()));

    String username = "";
    String password = "";
    int delim = token.indexOf(":");

    if (delim != -1) {
        username = token.substring(0, delim);
        password = token.substring(delim + 1);
    }

    if (authenticationIsRequired(username)) {
        UsernamePasswordAuthenticationToken authRequest =
                new UsernamePasswordAuthenticationToken(username, password);
        authRequest.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));

        Authentication authResult;

        try {
            authResult = authenticationManager.authenticate(authRequest);
        } catch (AuthenticationException failed) {
            // Authentication failed
            if (logger.isDebugEnabled()) {
                logger.debug("Authentication request for user: " + username + " failed: " + failed.toString());
            }

            SecurityContextHolder.getContext().setAuthentication(null);

            if (rememberMeServices != null) {
                rememberMeServices.loginFail(httpRequest, httpResponse);
            }

            if (ignoreFailure) {
                chain.doFilter(request, response);
            } else {
                authenticationEntryPoint.commence(request, response, failed);
            }

            return;
        }

        // Authentication success
        if (logger.isDebugEnabled()) {
            logger.debug("Authentication success: " + authResult.toString());
        }

        SecurityContextHolder.getContext().setAuthentication(authResult);

        if (rememberMeServices != null) {
            rememberMeServices.loginSuccess(httpRequest, httpResponse, authResult);
        }
    }
}

chain.doFilter(request, response);
}

 

可以看出,basicProcessingFilter从Header中获取Authorization信息,并通过Apache的codec包中的解码工具对token进行解码,从而获取用户输入的用户名、密码信息,用于后面的校验动作。

basicProcessingFilter在获取到验证请求需要用到的用户名及密码信息后,实际的用户有效性验证,交给了org.acegisecurity.providers.ProviderManager来管理的org.acegisecurity.providers.dao.DaoAuthenticationProvider类的执行实际验证处理过程。
对basicProcessingFilter的详细配置如下:

<bean id ="basicProcessingFilter" class= "org.acegisecurity.ui.basicauth.BasicProcessingFilter">
    <property name ="authenticationManager" ref= "authenticationManager" />
    <property name ="authenticationEntryPoint" ref= "basicProcessingFilterEntryPoint" />
</bean>

<bean id ="basicProcessingFilterEntryPoint" class= "org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
    <property name ="realmName" value="Acegi First Realm Name" />
</bean>

<bean id ="authenticationManager" class= "org.acegisecurity.providers.ProviderManager">
    <property name ="providers">
         <list>
              <ref bean="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=111111,ROLE_SUPERVISOR
            zhangsan=111111,ROLE_SUPERVISOR,disabled
        </value>
    </property>
</bean>

 

接下来开始配置exceptionTranslationFilter,配置信息如下:

<!-- exception filter -->
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
    <property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint" />
</bean>

 注:ExceptionTranslationFilter类用来处理权限验证失败时页面的路由情况,我们这里给ExceptionTranslationFilter配置了一个默认的basicProcessingFilterEntryPoint

对异常处理的片段代码如下:

public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException)
    throws IOException, ServletException {
    HttpServletResponse httpResponse = (HttpServletResponse) response;
    httpResponse.addHeader("WWW-Authenticate", "Basic realm=\"" + realmName + "\"");
    httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
}

 从上面的代码可以看到,当权限验证失败后,response请求被指向了HttpServletResponse.SC_UNAUTHORIZED页面(“401”访问受限页面)

在HttpServletResponse中定义的返回取值范围及常量定义如下所示:

public static final int SC_CONTINUE = 100;
public static final int SC_SWITCHING_PROTOCOLS = 101;
public static final int SC_OK = 200;
public static final int SC_CREATED = 201;
public static final int SC_ACCEPTED = 202;
public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
public static final int SC_NO_CONTENT = 204;
public static final int SC_RESET_CONTENT = 205;
public static final int SC_PARTIAL_CONTENT = 206;
public static final int SC_MULTIPLE_CHOICES = 300;
public static final int SC_MOVED_PERMANENTLY = 301;
public static final int SC_MOVED_TEMPORARILY = 302;
public static final int SC_FOUND = 302;
public static final int SC_SEE_OTHER = 303;
public static final int SC_NOT_MODIFIED = 304;
public static final int SC_USE_PROXY = 305;
public static final int SC_TEMPORARY_REDIRECT = 307;
public static final int SC_BAD_REQUEST = 400;
public static final int SC_UNAUTHORIZED = 401;
public static final int SC_PAYMENT_REQUIRED = 402;
public static final int SC_FORBIDDEN = 403;
public static final int SC_NOT_FOUND = 404;
public static final int SC_METHOD_NOT_ALLOWED = 405;
public static final int SC_NOT_ACCEPTABLE = 406;
public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
public static final int SC_REQUEST_TIMEOUT = 408;
public static final int SC_CONFLICT = 409;
public static final int SC_GONE = 410;
public static final int SC_LENGTH_REQUIRED = 411;
public static final int SC_PRECONDITION_FAILED = 412;
public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;
public static final int SC_REQUEST_URI_TOO_LONG = 414;
public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
public static final int SC_EXPECTATION_FAILED = 417;
public static final int SC_INTERNAL_SERVER_ERROR = 500;
public static final int SC_NOT_IMPLEMENTED = 501;
public static final int SC_BAD_GATEWAY = 502;
public static final int SC_SERVICE_UNAVAILABLE = 503;
public static final int SC_GATEWAY_TIMEOUT = 504;
public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;

 

最后配置filterInvocationInterceptor:

<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
            /secure.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>

 注:FilterSecurityInterceptor是filterchain中比较复杂,也是比较核心的过滤器,主要负责授权的工作

spring通过HttpConfigurationBuilder类来为filter构造过滤器实例,代码片段如下:

	private void createFilterSecurityInterceptor(BeanReference authManager) {
		boolean useExpressions = FilterInvocationSecurityMetadataSourceParser
				.isUseExpressions(httpElt);
		RootBeanDefinition securityMds = FilterInvocationSecurityMetadataSourceParser
				.createSecurityMetadataSource(interceptUrls, addAllAuth, httpElt, pc);

		RootBeanDefinition accessDecisionMgr;
		ManagedList<BeanDefinition> voters = new ManagedList<BeanDefinition>(2);

		if (useExpressions) {
			//表达式模式,这里省略,不是本例重点
		}
		else {
			voters.add(GrantedAuthorityDefaultsParserUtils.registerWithDefaultRolePrefix(pc, RoleVoterBeanFactory.class));
			voters.add(new RootBeanDefinition(AuthenticatedVoter.class));
		}
		accessDecisionMgr = new RootBeanDefinition(AffirmativeBased.class);
		accessDecisionMgr.getConstructorArgumentValues().addGenericArgumentValue(voters);
		accessDecisionMgr.setSource(pc.extractSource(httpElt));

		// Set up the access manager reference for http
		String accessManagerId = httpElt.getAttribute(ATT_ACCESS_MGR);

		if (!StringUtils.hasText(accessManagerId)) {
			accessManagerId = pc.getReaderContext().generateBeanName(accessDecisionMgr);
			pc.registerBeanComponent(new BeanComponentDefinition(accessDecisionMgr,
					accessManagerId));
		}

		BeanDefinitionBuilder builder = BeanDefinitionBuilder
				.rootBeanDefinition(FilterSecurityInterceptor.class);

		builder.addPropertyReference("accessDecisionManager", accessManagerId);
		builder.addPropertyValue("authenticationManager", authManager);

		if ("false".equals(httpElt.getAttribute(ATT_ONCE_PER_REQUEST))) {
			builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE);
		}

		builder.addPropertyValue("securityMetadataSource", securityMds);
		BeanDefinition fsiBean = builder.getBeanDefinition();
		String fsiId = pc.getReaderContext().generateBeanName(fsiBean);
		pc.registerBeanComponent(new BeanComponentDefinition(fsiBean, fsiId));

		// Create and register a DefaultWebInvocationPrivilegeEvaluator for use with
		// taglibs etc.
		BeanDefinition wipe = new RootBeanDefinition(
				DefaultWebInvocationPrivilegeEvaluator.class);
		wipe.getConstructorArgumentValues().addGenericArgumentValue(
				new RuntimeBeanReference(fsiId));

		pc.registerBeanComponent(new BeanComponentDefinition(wipe, pc.getReaderContext()
				.generateBeanName(wipe)));

		this.fsi = new RuntimeBeanReference(fsiId);
	}

 从上面的代码片段可以看出,在FilterInvocationSecurityMetadataSourceParser类中定义了一个静态方法用于处理鉴权元数据,代码片段如下:

	static RootBeanDefinition createSecurityMetadataSource(List<Element> interceptUrls,
			boolean addAllAuth, Element httpElt, ParserContext pc) {
		MatcherType matcherType = MatcherType.fromElement(httpElt);
		boolean useExpressions = isUseExpressions(httpElt);

		ManagedMap<BeanMetadataElement, BeanDefinition> requestToAttributesMap = parseInterceptUrlsForFilterInvocationRequestMap(
				matcherType, interceptUrls, useExpressions, addAllAuth, pc);
		BeanDefinitionBuilder fidsBuilder;

		if (useExpressions) {
			Element expressionHandlerElt = DomUtils.getChildElementByTagName(httpElt,
					Elements.EXPRESSION_HANDLER);
			String expressionHandlerRef = expressionHandlerElt == null ? null
					: expressionHandlerElt.getAttribute("ref");

			if (StringUtils.hasText(expressionHandlerRef)) {
				logger.info("Using bean '" + expressionHandlerRef
						+ "' as web SecurityExpressionHandler implementation");
			}
			else {
				expressionHandlerRef = registerDefaultExpressionHandler(pc);
			}

			fidsBuilder = BeanDefinitionBuilder
					.rootBeanDefinition(ExpressionBasedFilterInvocationSecurityMetadataSource.class);
			fidsBuilder.addConstructorArgValue(requestToAttributesMap);
			fidsBuilder.addConstructorArgReference(expressionHandlerRef);
		}
		else {
			fidsBuilder = BeanDefinitionBuilder
					.rootBeanDefinition(DefaultFilterInvocationSecurityMetadataSource.class);
			fidsBuilder.addConstructorArgValue(requestToAttributesMap);
		}

		fidsBuilder.getRawBeanDefinition().setSource(pc.extractSource(httpElt));

		return (RootBeanDefinition) fidsBuilder.getBeanDefinition();
	}

 

完整的spring-acegi.xml配置如下所示(完整路径:src/main/resources/META-INF/spring/spring-acegi.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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:webflow="http://www.springframework.org/schema/webflow-config"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
         http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.4.xsd"
	default-autowire="byName">
     <bean id ="filterChainProxy" class= "org.acegisecurity.util.FilterChainProxy">
            <property name ="filterInvocationDefinitionSource">
                 <value>
                     PATTERN_TYPE_APACHE_ANT
                     /**=basicProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
                 </value>
            </property>
     </bean>

     <bean id ="basicProcessingFilter" class= "org.acegisecurity.ui.basicauth.BasicProcessingFilter">
            <property name ="authenticationManager" ref= "authenticationManager" />
            <property name ="authenticationEntryPoint" ref= "basicProcessingFilterEntryPoint" />
     </bean>

     <bean id ="basicProcessingFilterEntryPoint" class= "org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
            <property name ="realmName" value="Acegi First Realm Name" />
     </bean>

     <bean id ="authenticationManager" class= "org.acegisecurity.providers.ProviderManager">
            <property name ="providers">
                 <list><ref bean="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_SUPERVISOR
                     zhangsan=1,ROLE_SUPERVISOR,disabled
                 </value>
            </property>
     </bean>

     <!-- exception filter -->
     <bean id ="exceptionTranslationFilter" class= "org.acegisecurity.ui.ExceptionTranslationFilter">
            <property name ="authenticationEntryPoint" ref= "basicProcessingFilterEntryPoint" />
     </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
                     /secure.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>

 

对应的web.xml文件配置信息如下所示:

<?xml version="1.0" encoding= "UTF-8"?>  
<web-app xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xmlns= "http://java.sun.com/xml/ns/javaee" xmlns:web= "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version= "2.5">
  <display-name>Archetype Created Web Application</display-name>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:META-INF/spring/spring-acegi.xml</param-value>
  </context-param>
  
  <filter>
    <filter-name>AcegiFilterChainProxy</filter-name>
    <filter-class>
                org.acegisecurity.util.FilterToBeanProxy
           </filter-class>
    <init-param>
      <param-name>targetBean</param-name>
      <param-value>filterChainProxy</param-value>
   </init-param>
 </filter>
  <filter-mapping>
    <filter-name>AcegiFilterChainProxy</filter-name>
    <url-pattern>/j_acegi_security_check</url-pattern>
 </filter-mapping>
  <filter-mapping>
    <filter-name>AcegiFilterChainProxy</filter-name>
    <url-pattern>/j_acegi_logout</url-pattern>
 </filter-mapping>
  <filter-mapping>
    <filter-name>AcegiFilterChainProxy</filter-name>
    <url-pattern>*.do</url-pattern>
 </filter-mapping>
  <filter-mapping>
    <filter-name>AcegiFilterChainProxy</filter-name>
    <url-pattern>*.jsp</url-pattern>
 </filter-mapping>
  <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
 </welcome-file-list>

  <listener>
       <listener-class>
        org.springframework.web.context.ContextLoaderListener
      </listener-class>
  </listener>
</web-app>

 

不失完整性,用于构建工程用到的指令如下:

mvn archetype:generate -DgroupId=com.myteay -DartifactId=rest-common-acegi -Dversion=1.0.0 -Dpackage=com.myteay -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeGroupId=org.apache.maven.archetypes  -DinteractiveMode=false

 

方便使用起见,贴出工程用到的完整pom

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.myteay</groupId>
  <artifactId>rest-common-acegi</artifactId>
  <packaging>war</packaging>
  <version>1.0.0</version>
  <name>rest-common-acegi Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
		<dependency>  
		    <groupId>org.springframework.security</groupId>  
		    <artifactId>spring-security-core</artifactId>  
		    <version>4.2.2.RELEASE</version>  
		</dependency> 
		
		<dependency>  
		    <groupId>org.springframework.security</groupId>  
		    <artifactId>spring-security-web</artifactId>  
		    <version>4.2.2.RELEASE</version>  
		</dependency>

		<dependency>  
		    <groupId>org.springframework.security</groupId>  
		    <artifactId>spring-security-config</artifactId>  
		    <version>4.2.2.RELEASE</version>  
		    <scope>runtime</scope>  
		</dependency>
		<dependency>
	        <groupId>org.apache.geronimo.specs</groupId>
	        <artifactId>geronimo-servlet_2.4_spec</artifactId>
	        <version>1.1.1</version>
	        <scope>provided</scope>
	    </dependency>
		<!-- spring dependency start -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-expression</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-oxm</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>4.2.2.RELEASE</version>
		</dependency>
		<!-- spring dependency end -->

		<!-- unit test start -->
		<dependency>
			<groupId>jmock</groupId>
			<artifactId>jmock</artifactId>
			<version>1.2.0</version>
		</dependency>
		<dependency>
			<groupId>jmock</groupId>
			<artifactId>jmock-cglib</artifactId>
			<version>1.2.0</version>
		</dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
	<dependency>
		<groupId>org.acegisecurity</groupId>
		<artifactId>acegi-security</artifactId>
		<version>1.0.7</version>
	</dependency>
  </dependencies>
  <build>
    <finalName>rest-common-acegi</finalName>
    <plugins>
		<plugin>
			<groupId>org.mortbay.jetty</groupId>
			<artifactId>maven-jetty-plugin</artifactId>
			<version>6.1.26</version>
			<configuration>
				<scanIntervalSeconds>3</scanIntervalSeconds>
				<connectors>
					<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
						<port>80</port>
					</connector>
				</connectors>
				<scanTargetPatterns>
					<scanTargetPattern>
						<directory>src/main/webapp/WEB-INF</directory>
						<excludes>
							<exclude>**/*.jsp</exclude>
						</excludes>
						<includes>
							<include>**/*.properties</include>
							<include>**/*.xml</include>
						</includes>
					</scanTargetPattern>
				</scanTargetPatterns>
			</configuration>
		</plugin>
    </plugins>
  </build>
</project>