tags:

views:

401

answers:

2

The intent here is to deal with obfuscated passwords for resources.

We have an Advisor that intercepts calls to setPassword and decrypts the argument.

We've set up a template that looks somewhat like this:

<bean id="pwAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
   <property name="advice"><bean class="our.advice.bean.class"/></property>
   <property name="mappedName" value="setPassword"/>
</bean>
<bean id="passwordHandlerTemplate" class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true">
   <property name="interceptorNames"><list><value>pwAdvisor</value></list></property>
</bean>

I'm unclear on the exact syntax to use it. The most obvious way is:

<bean id="myPasswordProtectedThing" parent="passwordHandlerTemplate">
   <property name="target">
      <bean class="the.target.class.name">
         <property name="password" value="encrypted garbage"/>
      </bean>
    </property>
 </bean>

But that doesn't work right, since the password property is applied to the inner bean, which means that the advisor won't wind up doing its work.

Well, what about this:

<bean id="myPasswordProtectedThing" parent="passwordHandlerTemplate">
   <property name="target"><bean class="the.target.class.name"/></property>
   <property name="password" value="encrypted garbage"/>
</bean>

Nope. Spring complains that the ProxyFactoryBean doesn't have a password property. And, of course, it doesn't. The thing that has the password property is the thing the factory bean creates.

Bueller?

A: 

I thought you wanted the beforeMethod advice to use the encrypted password String that's passed into the setPassword method. You want to decrypt that and have the advised class get an decrypted version.

I also don't see a proxy interface set in your proxy factory. "Spring In Action" says "...Creating a proxy with interfaces is favored over proxying classes..." Proxying classes should be the exception, not the rule.

Post your advice class.

duffymo
The object in question implements more than one interface, and all of them are proxied automatically by Spring.I could post the advice class, but it would be a red herring. Suffice to say that if you put a println in it, it doesn't get called when <property name="password"...> is in the appctx.
nsayer
+1  A: 

My first effort was poor, but I was in hurry. I apologize. Now I think I know how it should work, because I believe I've implemented what you want myself.

I started with a Credential class (note: no interface):


package aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Credential
{
   private static final String DEFAULT_USERNAME = "username";
   private static final String DEFAULT_PASSWORD = "password";

   private String username;
   private String password;

   public static void main(String[] args)
   {
      Credential cred1 = new Credential("foo", "bar");
      System.out.println("created using new: " + cred1);

      ApplicationContext context = new ClassPathXmlApplicationContext("classpath:aop-context.xml");
      Credential cred2 = (Credential) context.getBean("credential");

      System.out.println("created using app context: " + cred2);

      String password = ((args.length > 0) ? args[0] : "baz");
      cred2.setPassword(password);

      System.out.println("initialized using setter: " + cred2);      
   }

   public Credential()
   {
      this(DEFAULT_USERNAME, DEFAULT_PASSWORD);
   }

   public Credential(String username, String password)
   {
      this.setUsername(username);
      this.setPassword(password);
   }

   public String getUsername()
   {
      return username;
   }

   public void setUsername(String username)
   {
      this.username = username;
   }

   public String getPassword()
   {
      return password;
   }

   public void setPassword(String password)
   {
      this.password = password;
   }


   public String toString()
   {
      return new StringBuilder().append("Credential{").append("username='").append(username).append('\'').append(", password='").append(password).append('\'').append('}').toString();
   }
}

I created a Decryptor interface:


package aop;

public interface Decryptor
{
   String decrypt(String encrypted);
}

And a DecryptorImpl:


package aop;

public class DecryptorImpl implements Decryptor
{
   public static final String DEFAULT_DECRYPTED_VALUE = " - not secret anymore";

   public String decrypt(String encrypted)
   {
      // Any transform will do; this suffices to demonstrate
      return encrypted + DEFAULT_DECRYPTED_VALUE;
   }
}


I needed DecryptorAdvice to implement Spring's MethodBeforeAdvice:



package aop;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class DecryptionAdvice implements MethodBeforeAdvice
{
   private Decryptor decryptor;


   public DecryptionAdvice(Decryptor decryptor)
   {
      this.decryptor = decryptor;
   }

   public void before(Method method, Object[] args, Object target) throws Throwable
   {
      String encryptedPassword = (String) args[0];

      args[0] = this.decryptor.decrypt(encryptedPassword);
   }
}

And I wired it together in an aop-context.xml. (If you tell me how to get XML to display, I'll post it.) Note the passwordDecryptionAdvisor: it only matches the setPassword method.

The interesting part happens when I run it. Here's what I see in the console:


created using new: Credential{username='foo', password='bar'}
created using app context: Credential{username='stackoverflow', password='encrypted-password'}
initialized using setter: Credential{username='stackoverflow', password='baz - not secret anymore'}

What this tells me is:

  1. If I create an object with new it's not under Spring's control, advice isn't applied.
  2. If I call setPassword in the ctor before the app context is initialized, advice isn't applied.
  3. If I call setPassword in my code after the app context is initialized, advice is applied.

I hope this can help you.

duffymo
That's exactly what I see, but it begs the question: How can advice be applied when properties are set in the application context?
nsayer
can't be done until app context is initialized.
duffymo
If that's true, then it is truly teh suck.
nsayer
a bit harsh,in my opinion.
duffymo
maybe this isn't the way to do what you'd like. that doesn't mean it can't be done.
duffymo