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>