浏览代码

SEC-278: Initial commit of tutorial sample.

Ben Alex 19 年之前
父节点
当前提交
da187147f2

+ 47 - 2
doc/docbook/acegi.xml

@@ -2035,7 +2035,7 @@ if (obj instanceof UserDetails) {
 
           <para>Most JAAS <literal>LoginModule</literal>s require a callback
           of some sort. These callbacks are usually used to obtain the
-          username and password from the user. </para>
+          username and password from the user.</para>
 
           <para>In an Acegi Security deployment, Acegi Security is responsible
           for this user interaction (via the authentication mechanism). Thus,
@@ -2079,7 +2079,7 @@ if (obj instanceof UserDetails) {
           principal, and multiple <literal>GrantedAuthority</literal>[]s. To
           facilitate mapping between these different concepts, Acegi
           Security's JAAS package includes an
-          <literal>AuthorityGranter</literal> interface. </para>
+          <literal>AuthorityGranter</literal> interface.</para>
 
           <para>An <literal>AuthorityGranter</literal> is responsible for
           inspecting a JAAS principal and returning a
@@ -5955,6 +5955,51 @@ END;
         if you're ambitious: try <literal>client _cas_stateless_
         YOUR-SERVICE-TICKET-ID</literal>.</para>
       </sect1>
+
+      <sect1 id="tutorial-sample">
+        <title>Tutorial Sample</title>
+
+        <para>Whilst the <link linkend="contacts-sample">Contacts
+        Sample</link> is quite advanced in that illustrates the more powerful
+        features of domain object access control lists and so on, sometimes
+        you just want to start with a nice basic template. The tutorial sample
+        is intended to provide this initial base.</para>
+
+        <para>The compiled tutorial is included in the distribution ZIP file,
+        ready to be deployed into your web container. Authentication is
+        handled by the <link
+        linkend="dao-provider">DaoAuthenticationProvider</link>, using the
+        <link linkend="in-memory-service">in-memory</link>
+        <literal>UserDetailsService</literal> that sources information from
+        the <literal>users.properties</literal> file located in the WAR's
+        <literal>/WEB-INF</literal> directory. The <link
+        linkend="form">form-based</link> authentication mechanism is used,
+        with the commonly-used <link linkend="remember-me">remember-me</link>
+        authentication provider used to automatically remember the login using
+        cookies.</para>
+
+        <para>In terms of authorization, to keep things simple we've
+        configured the tutorial to only perform some basic <link
+        linkend="filter-invocation-authorization">web filter
+        authorization</link>. We've wired two common <link
+        linkend="pre-invocation">pre-invocation access decision voters</link>,
+        being the <literal>RoleVoter</literal> and
+        <literal>AuthenticatedVoter</literal>, such that
+        <literal>ROLE_*</literal> configuration attributes and
+        <literal>IS_AUTHENTICATED_*</literal> configuration attributes may be
+        used. Of course, it's extremely easy to add in other providers, with
+        most users probably starting with some services-layer security using
+        <link linkend="aop-alliance">MethodSecurityInterceptor</link>.</para>
+
+        <para>We recommend you start with the tutorial sample, as the XML is
+        minimal and easy to follow. All of the needed <link
+        linkend="filters">filters</link> are configured properly, and using
+        best practise. Most importantly, you can easily this one XML file (and
+        its corresponding <literal>web.xml</literal> entries) to your existing
+        application. Only when this basic integration is achieved do we
+        suggest you attempt adding in method authorization or domain object
+        security.</para>
+      </sect1>
     </chapter>
 
     <chapter id="community">

+ 1 - 0
samples/tutorial/project.properties

@@ -0,0 +1 @@
+maven.multiproject.type=war

+ 13 - 0
samples/tutorial/project.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project>
+  <extend>${basedir}/../project.xml</extend>
+  <pomVersion>3</pomVersion>
+  <artifactId>acegi-security-sample-tutorial</artifactId>
+  <name>Acegi Security System for Spring - Tutorial sample</name>
+  <siteDirectory>/home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-sample-tutorial</siteDirectory>
+  <repository>
+    <connection>scm:svn:https://svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</connection>
+    <developerConnection>scm:svn:https://svn.sourceforge.net/svnroot/acegisecurity/trunk/acegisecurity</developerConnection>
+    <url>http://svn.sourceforge.net/viewcvs.cgi/acegisecurity/trunk/acegisecurity/samples/tutorial/</url>
+  </repository>
+</project>

+ 148 - 0
samples/tutorial/src/webapp/WEB-INF/applicationContext-acegi-security.xml

@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+
+<!--
+  - A simple "base bones" Acegi Security configuration.
+  -
+  - The sample includes the "popular" features that people tend to use.
+  - Specifically, form authentication, remember-me, and anonymous processing.
+  - Other features aren't setup, as these can be added later by inserting
+  - the relevant XML fragments as specified in the Reference Guide.
+  -
+  - To assist new users, the filters specified in the FilterChainProxy are
+  - declared in the application context in the same order. Collaborators
+  - required by those filters are placed at the end of the file.
+  -
+  - $Id$
+  -->
+
+<beans>
+
+	<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
+		<property name="filterInvocationDefinitionSource">
+			<value>
+				CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
+				PATTERN_TYPE_APACHE_ANT
+				/**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
+			</value>
+		</property>
+	</bean>
+
+	<bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>
+
+	<bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
+		<constructor-arg value="/index.jsp"/> <!-- URL redirected to after logout -->
+		<constructor-arg>
+			<list>
+				<ref bean="rememberMeServices"/>
+				<bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
+			</list>
+		</constructor-arg>
+	</bean>
+
+	<bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
+		<property name="authenticationManager" ref="authenticationManager"/>
+		<property name="authenticationFailureUrl" value="/acegilogin.jsp?login_error=1"/>
+		<property name="defaultTargetUrl" value="/"/>
+		<property name="filterProcessesUrl" value="/j_acegi_security_check"/>
+		<property name="rememberMeServices" ref="rememberMeServices"/>
+	</bean>
+   
+	<bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/>
+
+	<bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
+		<property name="authenticationManager" ref="authenticationManager"/>
+		<property name="rememberMeServices" ref="rememberMeServices"/>
+	</bean>
+
+	<bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
+		<property name="key" value="changeThis"/>
+		<property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
+	</bean>
+
+	<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
+		<property name="authenticationEntryPoint">
+			<bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
+				<property name="loginFormUrl" value="/acegilogin.jsp"/>
+				<property name="forceHttps" value="false"/>
+			</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">
+			<bean class="org.acegisecurity.vote.AffirmativeBased">
+				<property name="allowIfAllAbstainDecisions" value="false"/>
+				<property name="decisionVoters">
+					<list>
+						<bean class="org.acegisecurity.vote.RoleVoter"/>
+						<bean class="org.acegisecurity.vote.AuthenticatedVoter"/>
+					</list>
+				</property>
+			</bean>
+		</property>
+		<property name="objectDefinitionSource">
+			<value>
+				CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
+				PATTERN_TYPE_APACHE_ANT
+				/secure/extreme/**=ROLE_SUPERVISOR
+				/secure/**=IS_AUTHENTICATED_REMEMBERED
+				/**=IS_AUTHENTICATED_ANONYMOUSLY
+			</value>
+		</property>
+	</bean>
+
+	<bean id="rememberMeServices" class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
+		<property name="userDetailsService" ref="userDetailsService"/>
+		<property name="key" value="changeThis"/>
+	</bean>
+
+	<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
+		<property name="providers">
+			<list>
+				<ref local="daoAuthenticationProvider"/>
+				<bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
+					<property name="key" value="changeThis"/>
+				</bean>
+				<bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
+					<property name="key" value="changeThis"/>
+				</bean>
+			</list>
+		</property>
+	</bean>
+
+	<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
+		<property name="userDetailsService" ref="userDetailsService"/>
+		<property name="userCache">
+			<bean class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
+				<property name="cache">
+					<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
+						<property name="cacheManager">
+							<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
+						</property>
+						<property name="cacheName" value="userCache"/>
+					</bean>
+				</property>
+			</bean>
+		</property>
+	</bean>
+
+	<!-- UserDetailsService is the most commonly frequently Acegi Security interface implemented by end users -->
+	<bean id="userDetailsService" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
+		<property name="userProperties">
+			<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
+				<property name="location" value="/WEB-INF/users.properties"/>
+			</bean>
+		</property>
+	</bean>
+
+	<!-- This bean is optional; it isn't used by any other bean as it only listens and logs -->
+	<bean id="loggerListener" class="org.acegisecurity.event.authentication.LoggerListener"/>
+
+</beans>

+ 18 - 0
samples/tutorial/src/webapp/WEB-INF/classes/log4j.properties

@@ -0,0 +1,18 @@
+# Global logging configuration
+log4j.rootLogger=WARN, stdout, fileout
+
+log4j.logger.org.acegisecurity=DEBUG, stdout, fileout
+
+# Console output...
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.conversionPattern=[%p,%c{1},%t] %m%n
+
+# Rolling log file output...
+log4j.appender.fileout=org.apache.log4j.RollingFileAppender
+log4j.appender.fileout.File=contacts.log
+#log4j.appender.fileout.File=${webapp.root}/WEB-INF/log4j.log
+log4j.appender.fileout.MaxFileSize=1024KB
+log4j.appender.fileout.MaxBackupIndex=1
+log4j.appender.fileout.layout=org.apache.log4j.PatternLayout
+log4j.appender.fileout.layout.conversionPattern=%d{ABSOLUTE} %5p %c{1},%t:%L - %m%n

+ 4 - 0
samples/tutorial/src/webapp/WEB-INF/users.properties

@@ -0,0 +1,4 @@
+marissa=koala,ROLE_SUPERVISOR
+dianne=emu,ROLE_USER
+scott=wombat,ROLE_USER
+peter=opal,disabled,ROLE_USER

+ 54 - 0
samples/tutorial/src/webapp/WEB-INF/web.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN' 'http://java.sun.com/dtd/web-app_2_3.dtd'>
+
+<!--
+  - Contacts web application
+  -
+  - web.xml for "filter" artifact only.
+  -
+  - $Id$
+  -->
+
+<web-app>
+
+    <display-name>Acegi Security Tutorial Application</display-name>
+    
+	<!--
+	  - Location of the XML file that defines the root application context
+	  - Applied by ContextLoaderListener.
+	  -->
+	<context-param>
+		<param-name>contextConfigLocation</param-name>
+		<param-value>
+			/WEB-INF/applicationContext-acegi-security.xml
+		</param-value>
+	</context-param>
+	
+    <filter>
+        <filter-name>Acegi Filter Chain Proxy</filter-name>
+        <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
+        <init-param>
+            <param-name>targetClass</param-name>
+            <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
+        </init-param>
+    </filter>
+
+    <filter-mapping>
+      <filter-name>Acegi Filter Chain Proxy</filter-name>
+      <url-pattern>/*</url-pattern>
+    </filter-mapping>
+	
+	<!--
+	  - Loads the root application context of this web app at startup.
+	  - The application context is then available via 
+	  - WebApplicationContextUtils.getWebApplicationContext(servletContext).
+    -->
+	<listener>
+		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+	</listener>
+
+ 	<welcome-file-list>
+		<welcome-file>index.jsp</welcome-file>
+	</welcome-file-list>
+
+</web-app>

+ 16 - 0
samples/tutorial/src/webapp/accessDenied.jsp

@@ -0,0 +1,16 @@
+<%@ page import="org.acegisecurity.context.SecurityContextHolder" %>
+<%@ page import="org.acegisecurity.Authentication" %>
+<%@ page import="org.acegisecurity.ui.AccessDeniedHandlerImpl" %>
+
+<h1>Sorry, access is denied</h1>
+
+
+<p>
+<%= request.getAttribute(AccessDeniedHandlerImpl.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY)%>
+
+<p>
+
+<%		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+		if (auth != null) { %>
+			Authentication object as a String: <%= auth.toString() %><BR><BR>
+<%      } %>

+ 45 - 0
samples/tutorial/src/webapp/acegilogin.jsp

@@ -0,0 +1,45 @@
+<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core' %>
+<%@ page import="org.acegisecurity.ui.AbstractProcessingFilter" %>
+<%@ page import="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter" %>
+<%@ page import="org.acegisecurity.AuthenticationException" %>
+
+<html>
+  <head>
+    <title>Login</title>
+  </head>
+
+  <body>
+    <h1>Login</h1>
+
+	<P>Valid users:
+	<P>
+	<P>username <b>marissa</b>, password <b>koala</b> (supervisor)
+	<P>username <b>dianne</b>, password <b>emu</b> (normal user)
+	<p>username <b>scott</b>, password <b>wombat</b> (normal user)
+	<p>username <b>peter</b>, password <b>opal</b> (user disabled)
+	<p>
+	
+    <%-- this form-login-page form is also used as the 
+         form-error-page to ask for a login again.
+         --%>
+    <c:if test="${not empty param.login_error}">
+      <font color="red">
+        Your login attempt was not successful, try again.<BR><BR>
+        Reason: <%= ((AuthenticationException) session.getAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %>
+      </font>
+    </c:if>
+
+    <form action="<c:url value='j_acegi_security_check'/>" method="POST">
+      <table>
+        <tr><td>User:</td><td><input type='text' name='j_username' <c:if test="${not empty param.login_error}">value='<%= session.getAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_LAST_USERNAME_KEY) %>'</c:if>></td></tr>
+        <tr><td>Password:</td><td><input type='password' name='j_password'></td></tr>
+        <tr><td><input type="checkbox" name="_acegi_security_remember_me"></td><td>Don't ask for my password for two weeks</td></tr>
+
+        <tr><td colspan='2'><input name="submit" type="submit"></td></tr>
+        <tr><td colspan='2'><input name="reset" type="reset"></td></tr>
+      </table>
+
+    </form>
+
+  </body>
+</html>

+ 9 - 0
samples/tutorial/src/webapp/index.jsp

@@ -0,0 +1,9 @@
+<html>
+<body>
+<h1>Home Page</h1>
+Anyone can view this page.
+
+<p><a href="secure/index.jsp">Secure page</a>
+<p><a href="secure/extreme/index.jsp">Extremely secure page</a>
+</body>
+</html>

+ 9 - 0
samples/tutorial/src/webapp/secure/extreme/index.jsp

@@ -0,0 +1,9 @@
+<html>
+<body>
+<h1>VERY Secure Page</h1>
+This is a protected page. You can only see me if you are a supervisor.
+
+<p><a href="../../">Home</a>
+<p><a href="../../j_acegi_logout">Logout</a>
+</body>
+</html>

+ 10 - 0
samples/tutorial/src/webapp/secure/index.jsp

@@ -0,0 +1,10 @@
+<html>
+<body>
+<h1>Secure Page</h1>
+This is a protected page. You can get to me if you've been remembered,
+or if you've authenticated this session.
+
+<p><a href="../">Home</a>
+<p><a href="../j_acegi_logout">Logout</a>
+</body>
+</html>