Răsfoiți Sursa

Improve performance of JBoss container adapter (thanks to Sergio Bern�).

Ben Alex 21 ani în urmă
părinte
comite
ac3d3483b3

+ 93 - 9
adapters/jboss/src/main/java/org/acegisecurity/adapters/jboss/JbossAcegiLoginModule.java

@@ -25,6 +25,9 @@ import org.jboss.security.SimpleGroup;
 import org.jboss.security.SimplePrincipal;
 import org.jboss.security.auth.spi.AbstractServerLoginModule;
 
+import org.springframework.beans.factory.access.*;
+import org.springframework.beans.factory.access.SingletonBeanFactoryLocator;
+
 import org.springframework.context.support.ClassPathXmlApplicationContext;
 
 import java.security.Principal;
@@ -53,6 +56,7 @@ import javax.security.auth.login.LoginException;
  * </p>
  *
  * @author Ben Alex
+ * @author Sergio Berná
  * @version $Id$
  */
 public class JbossAcegiLoginModule extends AbstractServerLoginModule {
@@ -69,35 +73,99 @@ public class JbossAcegiLoginModule extends AbstractServerLoginModule {
         Map sharedState, Map options) {
         super.initialize(subject, callbackHandler, sharedState, options);
 
+        if (super.log.isInfoEnabled()) {
+            super.log.info("initializing jboss login module");
+        }
+
         this.key = (String) options.get("key");
 
         if ((key == null) || "".equals(key)) {
             throw new IllegalArgumentException("key must be defined");
         }
 
+        String singletonId = (String) options.get("singletonId");
+
         String appContextLocation = (String) options.get("appContextLocation");
 
-        if ((appContextLocation == null) || "".equals(appContextLocation)) {
+        if ((((singletonId == null) || "".equals(singletonId))
+            && (appContextLocation == null)) || "".equals(appContextLocation)) {
             throw new IllegalArgumentException(
                 "appContextLocation must be defined");
         }
 
+        String beanName = (String) options.get("authenticationManager");
+
         if (Thread.currentThread().getContextClassLoader().getResource(appContextLocation) == null) {
+            if (super.log.isInfoEnabled()) {
+                super.log.info("cannot locate " + appContextLocation);
+            }
+
             throw new IllegalArgumentException("Cannot locate "
                 + appContextLocation);
         }
 
-        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(appContextLocation);
-        Map beans = ctx.getBeansOfType(AuthenticationManager.class, true, true);
+        ClassPathXmlApplicationContext ctx = null;
 
-        if (beans.size() == 0) {
-            throw new IllegalArgumentException(
-                "Bean context must contain at least one bean of type AuthenticationManager");
+        if ((singletonId == null) || "".equals(singletonId)) {
+            try {
+                ctx = new ClassPathXmlApplicationContext(appContextLocation);
+            } catch (Exception e) {
+                if (super.log.isInfoEnabled()) {
+                    super.log.info("error loading spring context "
+                        + appContextLocation + " " + e);
+                }
+
+                throw new IllegalArgumentException(
+                    "error loading spring context " + appContextLocation + " "
+                    + e);
+            }
+        } else {
+            if (super.log.isInfoEnabled()) {
+                super.log.debug("retrieving singleton instance " + singletonId);
+            }
+
+            BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
+            BeanFactoryReference bf = bfl.useBeanFactory(singletonId);
+            ctx = (ClassPathXmlApplicationContext) bf.getFactory();
+
+            if (ctx == null) {
+                if (super.log.isInfoEnabled()) {
+                    super.log.info("singleton " + beanName + " does not exists");
+                }
+
+                throw new IllegalArgumentException("singleton " + singletonId
+                    + " does not exists");
+            }
+        }
+
+        if ((beanName == null) || "".equals(beanName)) {
+            Map beans = null;
+
+            try {
+                beans = ctx.getBeansOfType(AuthenticationManager.class, true,
+                        true);
+            } catch (Exception e) {
+                if (super.log.isInfoEnabled()) {
+                    super.log.info("exception in getBeansOfType " + e);
+                }
+
+                throw new IllegalStateException(
+                    "spring error in get beans by class");
+            }
+
+            if (beans.size() == 0) {
+                throw new IllegalArgumentException(
+                    "Bean context must contain at least one bean of type AuthenticationManager");
+            }
+
+            beanName = (String) beans.keySet().iterator().next();
         }
 
-        String beanName = (String) beans.keySet().iterator().next();
-        authenticationManager = (AuthenticationManager) beans.get(beanName);
-        super.log.info("Successfully started JbossSpringLoginModule");
+        authenticationManager = (AuthenticationManager) ctx.getBean(beanName);
+
+        if (super.log.isInfoEnabled()) {
+            super.log.info("Successfully started JbossSpringLoginModule");
+        }
     }
 
     public boolean login() throws LoginException {
@@ -121,13 +189,27 @@ public class JbossAcegiLoginModule extends AbstractServerLoginModule {
             password = "";
         }
 
+        if (super.log.isDebugEnabled()) {
+            super.log.debug("checking identity");
+        }
+
         if (identity == null) {
+            super.log.debug("creating usernamepassword token");
+
             Authentication request = new UsernamePasswordAuthenticationToken(username,
                     password);
             Authentication response = null;
 
             try {
+                if (super.log.isDebugEnabled()) {
+                    super.log.debug("attempting authentication");
+                }
+
                 response = authenticationManager.authenticate(request);
+
+                if (super.log.isDebugEnabled()) {
+                    super.log.debug("authentication succeded");
+                }
             } catch (AuthenticationException failed) {
                 if (super.log.isDebugEnabled()) {
                     super.log.debug("Bad password for username=" + username);
@@ -137,6 +219,8 @@ public class JbossAcegiLoginModule extends AbstractServerLoginModule {
                     "Password Incorrect/Password Required");
             }
 
+            super.log.debug("user is logged. redirecting to jaas classes");
+
             identity = new PrincipalAcegiUserToken(this.key,
                     response.getPrincipal().toString(),
                     response.getCredentials().toString(),

+ 50 - 5
doc/docbook/index.xml

@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
 "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
-
 <!--
  * ========================================================================
  * 
@@ -21,7 +20,6 @@
  * 
  * ========================================================================
 -->
-
 <book>
   <bookinfo>
     <title>Acegi Security System for Spring</title>
@@ -2479,14 +2477,17 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
       <sect2 id="security-container-adapters-joss">
         <title>JBoss Installation</title>
 
-        <para>The following was tested with JBoss 3.2.3. We automatically test
+        <para>The following was tested with JBoss 3.2.6. We automatically test
         the following directions using our container integration test system
         and this version of JBoss.</para>
 
         <para><literal>$JBOSS_HOME</literal> refers to the root of your JBoss
         installation.</para>
 
-        <para>Edit your
+        <para>There are two different ways of making spring context available
+        to the Jboss integration classes. </para>
+
+        <para>The first approach is by editing your
         <literal>$JBOSS_HOME/server/your_config/conf/login-config.xml</literal>
         file so that it contains a new entry under the
         <literal>&lt;Policy&gt;</literal> section:</para>
@@ -2504,7 +2505,51 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
         <para>Copy <literal>acegisecurity.xml</literal> into
         <literal>$JBOSS_HOME/server/your_config/conf</literal>.</para>
 
-        <para>Copy the following files into
+        <para>In this configuration <literal>acegisecurity.xml</literal>
+        contains the spring context definition including all the
+        authentication manager beans. You have to bear in mind though, that
+        <literal>SecurityContext</literal> is created and destroyed on each
+        login request, so the login operation might become costly.
+        Alternatively, the second approach is to use Spring singleton
+        capabilities through
+        <literal>org.springframework.beans.factory.access.SingletonBeanFactoryLocator</literal>.
+        The required configuration for this approach is:</para>
+
+        <para><programlisting>    &lt;application-policy name = "SpringPoweredRealm"&gt;
+       &lt;authentication&gt;
+          &lt;login-module code = "net.sf.acegisecurity.adapters.jboss.JbossSpringLoginModule"
+            flag = "required"&gt;
+            &lt;module-option name = "singletonId"&gt;springRealm&lt;/module-option&gt;
+            &lt;module-option name = "key"&gt;my_password&lt;/module-option&gt;
+            &lt;module-option name = "authenticationManager"&gt;authenticationManager&lt;/module-option&gt;
+         &lt;/login-module&gt;
+       &lt;/authentication&gt;
+    &lt;/application-policy&gt;</programlisting></para>
+
+        <para>In the above code fragment,
+        <literal>authenticationManager</literal> is a helper property that
+        defines the expected name of the
+        <literal>AuthenticationManager</literal> in case you have several
+        defined in the IoC container. The <literal>singletonId</literal>
+        property references a bean defined in a
+        <literal>beanRefFactory.xml</literal> file. This file needs to be
+        available from anywhere on the JBoss classpath, including
+        <literal>$JBOSS_HOME/server/your_config/conf</literal>. The
+        <literal>beanRefFactory.xml</literal> contains the following
+        declaration:</para>
+
+        <para><programlisting>&lt;beans&gt;
+  &lt;bean id="springRealm" singleton="true" lazy-init="true" class="org.springframework.context.support.ClassPathXmlApplicationContext"&gt;
+    &lt;constructor-arg&gt;
+      &lt;list&gt;
+        &lt;value&gt;acegisecurity.xml&lt;/value&gt;
+      &lt;/list&gt;
+    &lt;/constructor-arg&gt;
+  &lt;/bean&gt;
+&lt;/beans&gt;</programlisting></para>
+
+        <para>Finally, irrespective of the configuration approach you need to
+        copy the following files into
         <literal>$JBOSS_HOME/server/your_config/lib</literal>:<itemizedlist>
             <listitem>
               <para><literal>aopalliance.jar</literal></para>

+ 1 - 0
doc/xdocs/changes.xml

@@ -46,6 +46,7 @@
       <action dev="benalex" type="update">Improved BasicAclProvider to only respond to specified ACL object requests</action>
       <action dev="benalex" type="update">Refactored MethodDefinitionSource to work with Method, not MethodInvocation</action>
       <action dev="benalex" type="update">Refactored AbstractSecurityInterceptor to better support other AOP libraries</action>
+      <action dev="benalex" type="update">Improved performance of JBoss container adapter (see reference docs)</action>
       <action dev="benalex" type="fix">Fixed AbstractProcessingFilter to use removeAttribute (JRun compatibility)</action>
       <action dev="benalex" type="fix">Fixed GrantedAuthorityEffectiveAclResolver support of UserDetails principals</action>
       <action dev="benalex" type="update">Moved MethodSecurityInterceptor to ...intercept.method.aopalliance package</action>

+ 3 - 0
project.xml

@@ -120,6 +120,9 @@
     <contributor>
       <name>Andrew Petro</name>
     </contributor>
+    <contributor>
+      <name>Sergio Berná</name>
+    </contributor>
   </contributors>
   <dependencies>
     <dependency>