CallbackHandler in CXF 3.X & WSS4J 2.X - apache

I'm trying to upgrade our current application to CXF 3 and WSS4J 2. This is causing me quite a headache.
The current application code for the client:
private void secureWebService( Client client, final Credentials credentials ) {
// set some WS-Security information
Map<String,Object> outProps = new HashMap<String,Object>();
outProps.put( WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN );
outProps.put( WSHandlerConstants.USER, credentials.getUsername() );
outProps.put( WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT );
// Callback used to retrieve password for given user.
outProps.put( WSHandlerConstants.PW_CALLBACK_REF, new CallbackHandler() {
#Override
public void handle( Callback[] callbacks ) throws IOException, UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
pc.setPassword( credentials.getPassword() );
}
});
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor( outProps );
client.getOutInterceptors().clear();
client.getOutInterceptors().add( wssOut );
}
On the Server side...
public class ServerPasswordCallback implements CallbackHandler {
public void handle( Callback[] callbacks ) throws IOException, UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback)callbacks[0];
boolean result = false;
try {
LoginContext lc = new LoginContext( container, new CallbackHandler() {
public void handle( Callback[] callbacks ) throws IOException, UnsupportedCallbackException {
NameCallback nc = (NameCallback)callbacks[0];
nc.setName( myGetName() );
PasswordCallback pc2 = (PasswordCallback)callbacks[1];
String clientPasssword = pc.getPassword(); //Used to contain the password but is now NULL
pc2.setPassword( clientPasssword.toCharArray() );
}
} );
lc.login();
result = true;
} catch( LoginException le ) {
le.printStackTrace(); //current stack trace is a NULLPointerException since "clientPassword" is NULL
// We haven't authenticated, so false will be returned
} catch( SecurityException se ) {
throw new IOException( "Cannot create LoginContext. " + se.getMessage() );
}
return result;
}
}
My JAX-WS Endpoint Config:
<bean id="wss4jPasswordCallback" class="com.mycompany.webservice.security.ServerPasswordCallback"/>
<jaxws:endpoint id="customerEndpoint" implementor="#customerWebService" address="/Customer">
<jaxws:inInterceptors>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken"/>
<entry key="passwordType" value="PlainText"/>
<entry key="passwordCallbackRef">
<ref bean="wss4jPasswordCallback"/>
</entry>
</map>
</constructor-arg>
</bean>
<bean class="com.mycompany.webservice.security.Wss4jPrincipalInjectorInterceptor"/>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean class="com.mycompany.webservice.security.Wss4jPrincipalRemoverInterceptor"/>
</jaxws:outInterceptors>
<jaxws:outFaultInterceptors>
<bean class="com.mycompany.webservice.security.Wss4jPrincipalRemoverInterceptor"/>
</jaxws:outFaultInterceptors>
</jaxws:endpoint>
Specifically, the WSPasswordCallback object is now passing NULL rather than the password as it used to. From my reading, CXF just chose to stop doing this with insufficient documentation regarding what I would do for an upgrade path. What is an upgrade path for this?
Also, I've noticed that WSS4J is changing where it lives. It has moved from "org.apache.ws.security" to "org.apache.wss4j.common.ext". I have also updated all my constants to "org.apache.wss4j.dom.WSConstants" & "org.apache.wss4j.dom.handler.WSHandlerConstants" to get things to compile. This also has drastically changed the old "org.apache.ws.security.validate.Validator" class in "org.apache.commons.validator.Validator". The classes are quite different now. Maybe "org.apache.wss4j.dom.validate.KerberosTokenValidator" is the new replacement? Again, I could find no documentation for this fact.
Please note: This is all working code until moving to the new CXF and WSS4J version!

Due to the significant time I spent on this issue, I wanted to make sure I provided my solution. This may not be for everyone, but if your code looks like my question, this should get you on the right track.
First, what was the Validator class is now an interface after CXF 3. What I have working is the org.apache.wss4j.dom.validate.UsernameTokenValidator in place of what was org.apache.ws.security.validate.Validator. This critical piece of info was absent in my searches.
Therefore, if you are using CallbackHandler for doing custom authentication, you need to switch to the UsernameTokenValidator. Here is what my code now looks like.
JAX-WS Config:
<!-- Bean for custom authentication of web service -->
<bean id="UsernameTokenLDAPValidator" class="com.mycompany.webservice.security.UsernameTokenLDAPValidator"/>
<jaxws:endpoint id="customerEndpoint" implementor="#customerWebService" address="/Customer">
<jaxws:inInterceptors>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken"/>
<entry key="passwordType" value="PasswordText"/>
</map>
</constructor-arg>
</bean>
<bean class="com.mycompany.webservice.security.Wss4jPrincipalInjectorInterceptor"/>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean class="com.mycompany.webservice.security.Wss4jPrincipalRemoverInterceptor"/>
</jaxws:outInterceptors>
<jaxws:outFaultInterceptors>
<bean class="com.mycompany.webservice.security.Wss4jPrincipalRemoverInterceptor"/>
</jaxws:outFaultInterceptors>
<jaxws:properties>
<entry key="ws-security.enable.nonce.cache" value="false" />
<entry key="ws-security.enable.timestamp.cache" value="false" />
<entry key="ws-security.ut.validator" value-ref="UsernameTokenLDAPValidator"/>
</jaxws:properties>
</jaxws:endpoint>
NEW UsernameTokenLDAPValidator class
public class UsernameTokenLDAPValidator extends UsernameTokenValidator {
public Credential validate( Credential credential, RequestData request ) throws WSSecurityException {
UsernameToken userToken = credential.getUsernametoken();
final String userId = userToken.getName();
final String password = userToken.getPassword();
String securityDomainName = "SecurityDomainNameNameOfJBOSSConfig"; //<login-module>
LoginContext lc;
try {
lc = new LoginContext( securityDomainName, new CallbackHandler() {
public void handle( Callback[] callbacks ) throws IOException, UnsupportedCallbackException {
NameCallback nc = (NameCallback)callbacks[0];
nc.setName( userId );
PasswordCallback pc2 = (PasswordCallback)callbacks[1];
pc2.setPassword( password.toCharArray() );
}
} );
lc.login();
} catch( LoginException e ) {
throw new WSSecurityException( ErrorCode.FAILED_AUTHENTICATION, e );
}
return credential;
}
}
Notes:
I removed my old CallbackHandler class (ServerPasswordCallback) (in question)
I did not make any changes to the application code for the client (in question)

It sounds like you are upgrading from an old version of CXF that used WSS4J 1.5.x From WSS4J 1.6.x, the CallbackHandler is no longer supplied with the password, but must instead set the password on the Callback. See here:
http://coheigea.blogspot.ie/2011/02/usernametoken-processing-changes-in.html
Colm.

Related

Have users of activeMQ own a queue which name is the user's name

In my application, a user may create an account freely, and it needs to own a queue (or topic) to communicate 2 backend processes between them. I don't want to have to modify activemq's configuration every time that someone creates an account. I have already created a jaasAuthenticationPlugin and it works fine. Here is the relevant part of my activemq.xml file:
<plugins>
<!-- 'activemq-domain' defined in conf/login.conf -->
<jaasAuthenticationPlugin configuration="activemq-domain" />
<authorizationPlugin>
<map>
<authorizationMap>
<authorizationEntries>
<authorizationEntry queue="foobarQueue"
write="foobarGroup"
read="foobarGroup"
admin="foobarGroup"
/>
</authorizationEntries>
</authorizationMap>
</map>
</authorizationPlugin>
</plugins>
As you may deduct, the authentication plugin is authenticating a user (foobar in this example) and putting the user in the foobarGroup group. The AuthorizationEntry is granting read, write and admin privileges to the foobarQueue to this foobarGroup. This is working well, but now if I create a new user, I must come to this file and add a new AuthorizationEntry. Is it possible with a simple configuration line in the activemq.xml to do something like:
<authorizationEntry
queue="<% Username %>"
write="<% Username %>"
read="<% Username %>"
admin="<% Username %>"
/>
or should I write some JAAS authorization class to do that?
Finally I have written a class to handle the Authorization part. It was a bit difficult because documentation is difficult to find and I couldn't find any good example. Digging in the source code of the default LDAPAuthorizationMap was key. Anyway, the source for anyone interested:
package com.example.activemq;
import org.apache.activemq.advisory.AdvisorySupport;
import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.jaas.GroupPrincipal;
import org.apache.activemq.security.AuthorizationMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.HashSet;
import java.util.Set;
public class OwnedUserQueueAuthorizator implements AuthorizationMap {
private static final Log log =
LogFactory.getLog(OwnedUserQueueAuthorizator.class);
private boolean debug = false;
// the Destination will be the name of the user, and we should return that
// the group with user name has read,write and admin privileges to the
// topic/queue named like the username
// for temporary destinations, if null is returned, then everybody has
// permission.
public Set<GroupPrincipal> getTempDestinationAdminACLs() {
return null;
}
public Set<GroupPrincipal> getTempDestinationReadACLs() {
return null;
}
public Set<GroupPrincipal> getTempDestinationWriteACLs() {
return null;
}
// for persistent destinations
public Set<GroupPrincipal> getAdminACLs(ActiveMQDestination destination) {
if (debug) {
log.debug("getAdminACLs: " + destination.getPhysicalName());
}
return getACLs(destination);
}
public Set<GroupPrincipal> getReadACLs(ActiveMQDestination destination) {
if (debug) {
log.debug("getReadACLs: " + destination.getPhysicalName());
}
return getACLs(destination);
}
public Set<GroupPrincipal> getWriteACLs(ActiveMQDestination destination) {
if (debug) {
log.debug("getwriteACLs: " + destination.getPhysicalName());
}
return getACLs(destination);
}
private Set<GroupPrincipal> getACLs(ActiveMQDestination destination) {
Set<GroupPrincipal> result;
if (AdvisorySupport.isAdvisoryTopic(destination)) {
result = getACLsForAdvisory();
} else {
result = new HashSet<GroupPrincipal>();
// Destination should be something like UUID or UUID.whatever...,
// so we must add only the first component as the group principal
result.add(new GroupPrincipal(
destination.getDestinationPaths()[0])
);
}
if (debug) {
String s = "";
for (GroupPrincipal gp : result) {
s += ", " + gp.getName();
}
log.debug("groupPrincipals: " + "[" + s.substring(2) + "]");
}
return result;
}
private Set<GroupPrincipal> getACLsForAdvisory() {
Set<GroupPrincipal> result = new HashSet<GroupPrincipal>();
GroupPrincipal g = new GroupPrincipal("advisories");
result.add(g);
return result;
}
// Properties
// -------------------------------------------------------------------------
// if the <bean> definition in the activemq.xml has some
// <property name="foo" value="..." />
// defined, they will call this.setFoo($value), so, even if these get/set
// methods aren't called from here, they are really needed.
public void setDebug(String debug) {
this.debug = "true".equalsIgnoreCase(debug);
}
public String getDebug() {
return String.valueOf(debug);
}
}
The conf/activemq.xml file:
<beans ...>
...
<broker ...>
...
<plugins>
<!-- 'activemq-domain' defined in conf/login.conf -->
<jaasAuthenticationPlugin configuration="activemq-domain" />
<authorizationPlugin>
<map>
<bean id="OwnedUserQueueAuthorizationMap"
class="com.example.activemq.OwnedUserQueueAuthorizator"
xmlns="http://www.springframework.org/schema/beans">
<property name="debug" value="false"/>
</bean>
</map>
</authorizationPlugin>
</plugins>
...
</broker>
...
</beans>

Why is data getting stored with weird keys in Redis when using Jedis with Spring Data?

I am using Spring Data Redis with Jedis. I am trying to store a hash with key vc:${list_id}. I was able to successfully insert to redis. However, when I inspect the keys using the redis-cli, I don't see the key vc:501381. Instead I see \xac\xed\x00\x05t\x00\tvc:501381.
Why is this happening and how do I change this?
Ok, googled around for a while and found help at http://java.dzone.com/articles/spring-data-redis.
It happened because of Java serialization.
The key serializer for redisTemplate needs to be configured to StringRedisSerializer i.e. like this:
<bean
id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.server}"
p:port="${redis.port}"
p:use-pool="true"/>
<bean
id="stringRedisSerializer"
class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<bean
id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"
p:keySerializer-ref="stringRedisSerializer"
p:hashKeySerializer-ref="stringRedisSerializer"
/>
Now the key in redis is vc:501381.
Or like #niconic says, we can also set the default serializer itself to the string serializer as follows:
<bean
id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"
p:defaultSerializer-ref="stringRedisSerializer"
/>
which means all our keys and values are strings. Notice however that this may not be preferable, since you may want your values to be not just strings.
If your value is a domain object, then you can use Jackson serializer and configure a serializer as mentioned here i.e. like this:
<bean id="userJsonRedisSerializer" class="org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer">
<constructor-arg type="java.lang.Class" value="com.mycompany.redis.domain.User"/>
</bean>
and configure your template as:
<bean
id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"
p:keySerializer-ref="stringRedisSerializer"
p:hashKeySerializer-ref="stringRedisSerializer"
p:valueSerialier-ref="userJsonRedisSerializer"
/>
It's a very old question, but my answer might be helpful for someone who got the same issue while working with Redis using Spring Boot. I was stuck on the same issue while storing hash type data in redis. I have written the required config file changes for the RedisTemplate.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages = "com.redis")
public class AppCofiguration {
#Bean
JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory jedisConFactory = new JedisConnectionFactory();
jedisConFactory.setHostName("127.0.0.1");
jedisConFactory.setPort(6379);
return jedisConFactory;
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
final RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(jedisConnectionFactory());
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
// the following is not required
template.setHashValueSerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
return template;
}
}
If the data type is String then template.setHashValueSerializer(new StringRedisSerializer()); and template.setHashKeySerializer(new StringRedisSerializer()); are not required.
I know this question has been a while, but I did some research on this topic again recently, so I would like to share how this "semi-hashed" key is generated by going thru part of the spring source code here.
First of all, Spring leverages AOP to resolve annotations like #Cacheable, #CacheEvict or #CachePut etc. The advice class is CacheInterceptor from Spring-context dependency, which is a subclass of CacheAspectSupport (also from Spring-context). For the ease of this explanation, I would use #Cacheable as an example to go thru part of the source code here.
When the method annotated as #Cacheable is invoked, AOP would route it to this method protected Collection<? extends Cache> getCaches(CacheOperationInvocationContext<CacheOperation> context, CacheResolver cacheResolver) from CacheAspectSupport class, in which it would try to resolve this #Cacheable annotation. In turn, it leads to the invocation of this method public Cache getCache(String name) in the implementing CacheManager. For this explanation, the implementing CacheManage would be RedisCacheManager (from Spring-data-redis dependency).
If the cache was not hit, it will go ahead to create the cache. Below is the key methods from RedisCacheManager:
protected Cache getMissingCache(String name) {
return this.dynamic ? createCache(name) : null;
}
#SuppressWarnings("unchecked")
protected RedisCache createCache(String cacheName) {
long expiration = computeExpiration(cacheName);
return new RedisCache(cacheName, (usePrefix ? cachePrefix.prefix(cacheName) : null), redisOperations, expiration,
cacheNullValues);
}
Essentially, it will instantiate an RedisCache object. To do this, it requires 4 parameters, namely, cacheName, prefix (this is the key parameter with regards to answering this question), redisOperation (aka, the configured redisTemplate), expiration (default to 0) and cacheNullValues (default to false). The constructor below shows more details about RedisCache.
/**
* Constructs a new {#link RedisCache} instance.
*
* #param name cache name
* #param prefix must not be {#literal null} or empty.
* #param redisOperations
* #param expiration
* #param allowNullValues
* #since 1.8
*/
public RedisCache(String name, byte[] prefix, RedisOperations<? extends Object, ? extends Object> redisOperations,
long expiration, boolean allowNullValues) {
super(allowNullValues);
Assert.hasText(name, "CacheName must not be null or empty!");
RedisSerializer<?> serializer = redisOperations.getValueSerializer() != null ? redisOperations.getValueSerializer()
: (RedisSerializer<?>) new JdkSerializationRedisSerializer();
this.cacheMetadata = new RedisCacheMetadata(name, prefix);
this.cacheMetadata.setDefaultExpiration(expiration);
this.redisOperations = redisOperations;
this.cacheValueAccessor = new CacheValueAccessor(serializer);
if (allowNullValues) {
if (redisOperations.getValueSerializer() instanceof StringRedisSerializer
|| redisOperations.getValueSerializer() instanceof GenericToStringSerializer
|| redisOperations.getValueSerializer() instanceof JacksonJsonRedisSerializer
|| redisOperations.getValueSerializer() instanceof Jackson2JsonRedisSerializer) {
throw new IllegalArgumentException(String.format(
"Redis does not allow keys with null value ¯\\_(ツ)_/¯. "
+ "The chosen %s does not support generic type handling and therefore cannot be used with allowNullValues enabled. "
+ "Please use a different RedisSerializer or disable null value support.",
ClassUtils.getShortName(redisOperations.getValueSerializer().getClass())));
}
}
}
So what the use of prefix in this RedisCache? --> As shown in the constructor about, it is used in this statement this.cacheMetadata = new RedisCacheMetadata(name, prefix);, and the constructor of RedisCacheMetadata below shows more details:
/**
* #param cacheName must not be {#literal null} or empty.
* #param keyPrefix can be {#literal null}.
*/
public RedisCacheMetadata(String cacheName, byte[] keyPrefix) {
Assert.hasText(cacheName, "CacheName must not be null or empty!");
this.cacheName = cacheName;
this.keyPrefix = keyPrefix;
StringRedisSerializer stringSerializer = new StringRedisSerializer();
// name of the set holding the keys
this.setOfKnownKeys = usesKeyPrefix() ? new byte[] {} : stringSerializer.serialize(cacheName + "~keys");
this.cacheLockName = stringSerializer.serialize(cacheName + "~lock");
}
At this point, we know that some prefix parameter has been set to RedisCacheMetadata, but how exactly is this prefix used to form the key in Redis (e.g.,\xac\xed\x00\x05t\x00\tvc:501381 as you mentioned)?
Basically, the CacheInterceptor will subsequently move forward to invoke a method private RedisCacheKey getRedisCacheKey(Object key) from the above-mentioned RedisCache object, which returns an instance of RedisCacheKey by utilizing the prefix from RedisCacheMetadata and keySerializer from RedisOperation.
private RedisCacheKey getRedisCacheKey(Object key) {
return new RedisCacheKey(key).usePrefix(this.cacheMetadata.getKeyPrefix())
.withKeySerializer(redisOperations.getKeySerializer());
}
By reaching this point, the "pre" advice of CacheInterceptor is completed, and it would go ahead to execute the actual method annotated by #Cacheable. And after completing the execution of the actual method, it will do the "post" advice of CacheInterceptor, which essentially put the result to RedisCache. Below is the method of putting the result to redis cache:
public void put(final Object key, final Object value) {
put(new RedisCacheElement(getRedisCacheKey(key), toStoreValue(value))
.expireAfter(cacheMetadata.getDefaultExpiration()));
}
/**
* Add the element by adding {#link RedisCacheElement#get()} at {#link RedisCacheElement#getKeyBytes()}. If the cache
* previously contained a mapping for this {#link RedisCacheElement#getKeyBytes()}, the old value is replaced by
* {#link RedisCacheElement#get()}.
*
* #param element must not be {#literal null}.
* #since 1.5
*/
public void put(RedisCacheElement element) {
Assert.notNull(element, "Element must not be null!");
redisOperations
.execute(new RedisCachePutCallback(new BinaryRedisCacheElement(element, cacheValueAccessor), cacheMetadata));
}
Within the RedisCachePutCallback object, its callback method doInRedis() actually invoke a method to form the actual key in redis, and the method name is getKeyBytes() from RedisCacheKey instance. Below shows the details of this method:
/**
* Get the {#link Byte} representation of the given key element using prefix if available.
*/
public byte[] getKeyBytes() {
byte[] rawKey = serializeKeyElement();
if (!hasPrefix()) {
return rawKey;
}
byte[] prefixedKey = Arrays.copyOf(prefix, prefix.length + rawKey.length);
System.arraycopy(rawKey, 0, prefixedKey, prefix.length, rawKey.length);
return prefixedKey;
}
As we can see in the getKeyBytes method, it utilizes both the raw key (vc:501381 in your case) and prefix key (\xac\xed\x00\x05t\x00\t in your case).
Use StringRedisTemplate to replace RedisTemplate.
By default, RedisTemplate uses Java serialization, StringRedisTemplate uses StringRedisSerializer.
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
You have to serialize teh objects that you are sending it to redis. Below is the complete running example of it. It uses interface DomainObject as Serializable
Below are the steps
1) make your maven pom.xml with following jars
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.0</version>
</dependency>
2) make your configuration xml as follows
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<bean id="jeidsConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="localhost" p:port="6379" p:password="" />
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jeidsConnectionFactory" />
<bean id="imageRepository" class="com.self.common.api.poc.ImageRepository">
<property name="redisTemplate" ref="redisTemplate"/>
</bean>
</beans>
3) Make your classes as follows
package com.self.common.api.poc;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class RedisMainApp {
public static void main(String[] args) throws IOException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("mvc-dispatcher-servlet.xml");
ImageRepository imageRepository = (ImageRepository) applicationContext.getBean("imageRepository");
BufferedImage img = ImageIO.read(new File("files/img/TestImage.png"));
BufferedImage newImg;
String imagestr;
imagestr = encodeToString(img, "png");
Image image1 = new Image("1", imagestr);
img = ImageIO.read(new File("files/img/TestImage2.png"));
imagestr = encodeToString(img, "png");
Image image2 = new Image("2", imagestr);
imageRepository.put(image1);
System.out.println(" Step 1 output : " + imageRepository.getObjects());
imageRepository.put(image2);
System.out.println(" Step 2 output : " + imageRepository.getObjects());
imageRepository.delete(image1);
System.out.println(" Step 3 output : " + imageRepository.getObjects());
}
/**
* Decode string to image
* #param imageString The string to decode
* #return decoded image
*/
public static BufferedImage decodeToImage(String imageString) {
BufferedImage image = null;
byte[] imageByte;
try {
BASE64Decoder decoder = new BASE64Decoder();
imageByte = decoder.decodeBuffer(imageString);
ByteArrayInputStream bis = new ByteArrayInputStream(imageByte);
image = ImageIO.read(bis);
bis.close();
} catch (Exception e) {
e.printStackTrace();
}
return image;
}
/**
* Encode image to string
* #param image The image to encode
* #param type jpeg, bmp, ...
* #return encoded string
*/
public static String encodeToString(BufferedImage image, String type) {
String imageString = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ImageIO.write(image, type, bos);
byte[] imageBytes = bos.toByteArray();
BASE64Encoder encoder = new BASE64Encoder();
imageString = encoder.encode(imageBytes);
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
return imageString;
}
}
package com.self.common.api.poc;
public class Image implements DomainObject {
public static final String OBJECT_KEY = "IMAGE";
public Image() {
}
public Image(String imageId, String imageAsStringBase64){
this.imageId = imageId;
this.imageAsStringBase64 = imageAsStringBase64;
}
private String imageId;
private String imageAsStringBase64;
public String getImageId() {
return imageId;
}
public void setImageId(String imageId) {
this.imageId = imageId;
}
public String getImageName() {
return imageAsStringBase64;
}
public void setImageName(String imageAsStringBase64) {
this.imageAsStringBase64 = imageAsStringBase64;
}
#Override
public String toString() {
return "User [id=" + imageAsStringBase64 + ", imageAsBase64String=" + imageAsStringBase64 + "]";
}
#Override
public String getKey() {
return getImageId();
}
#Override
public String getObjectKey() {
return OBJECT_KEY;
}
}
package com.self.common.api.poc;
import java.io.Serializable;
public interface DomainObject extends Serializable {
String getKey();
String getObjectKey();
}
package com.self.common.api.poc;
import java.util.List;
import com.self.common.api.poc.DomainObject;
public interface Repository<V extends DomainObject> {
void put(V obj);
V get(V key);
void delete(V key);
List<V> getObjects();
}
package com.self.common.api.poc;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import com.self.common.api.poc.DomainObject;
public class ImageRepository implements Repository<Image>{
#Autowired
private RedisTemplate<String,Image> redisTemplate;
public RedisTemplate<String,Image> getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(RedisTemplate<String,Image> redisTemplate) {
this.redisTemplate = redisTemplate;
}
#Override
public void put(Image image) {
redisTemplate.opsForHash()
.put(image.getObjectKey(), image.getKey(), image);
}
#Override
public void delete(Image key) {
redisTemplate.opsForHash().delete(key.getObjectKey(), key.getKey());
}
#Override
public Image get(Image key) {
return (Image) redisTemplate.opsForHash().get(key.getObjectKey(),
key.getKey());
}
#Override
public List<Image> getObjects() {
List<Image> users = new ArrayList<Image>();
for (Object user : redisTemplate.opsForHash().values(Image.OBJECT_KEY) ){
users.add((Image) user);
}
return users;
}
}
For more reference on sprinf jedis you can see http://www.javacodegeeks.com/2012/06/using-redis-with-spring.html
Sample Code is taken from http://javakart.blogspot.in/2012/12/spring-data-redis-hello-world-example.html

After logout main window the popup window links still working

I am using struts 2 login interceptor. Code is working fine. In my application many popup windows have used.when I open my popup window and logout from my main window popup window showing login page as I coded but only for this scenario, i want that it will showing any message ( either session expired or u have already logged out) instead of login page.
Please go thru my code if any modification is required
LoginInterceptor.java
public class LoginInterceptor extends AbstractInterceptor implements
StrutsStatics{
private AdminUserSessionInfo objAdminUserSessionInfo = new AdminUserSessionInfo();
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(LoginInterceptor.class);
private static final String LOGIN_ATTEMPT = "loginAttempt";
private static final String LOGIN_OUT = "loginOut";
private static final String USER_HANDLE = "loggedInUser";
Map sessionMap = null;
public void init() {
log.info("Intializing LoginInterceptor");
}
public void destroy() {
}
public String intercept(ActionInvocation invocation) throws Exception {
final ActionContext context = invocation.getInvocationContext();
HttpServletRequest request = (HttpServletRequest) context
.get(HTTP_REQUEST);
HttpServletResponse response = (HttpServletResponse) context
.get(HTTP_RESPONSE);
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
HttpSession session = request.getSession(true);
Object user = session.getAttribute(USER_HANDLE);
String loginOut = request.getParameter(LOGIN_OUT);
if (user == null) {
String loginAttempt = request.getParameter(LOGIN_ATTEMPT);
System.out.println("loginAttemp---->"+loginAttempt);
/* The user is attempting to log in. */
if (!StringUtils.isBlank(loginAttempt)) {
return invocation.invoke();
}
return "login";
} else {
return invocation.invoke();
}
}
web.xml
<interceptors>
<interceptor class="org.iaf.aos.common.LoginInterceptor"
name="loginInterceptor"></interceptor>
<interceptor-stack name="loginStack">
<interceptor-ref name="loginInterceptor" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="loginStack"></default-interceptor-ref>
<global-results><result name="login">aos.jsp</result></global-results>
<action name="checkUserLogin" class="org.iaf.aos.web.login.action.AdminUserAction" method="checkUserLogin">
<!-- <result name="success">index.jsp</result> -->
<interceptor-ref name="loginStack"></interceptor-ref>
<result name="success" type="chain">HomePage</result>
<result name="error">WEB-INF/jsp/admin/Error.jsp</result>
<result name="selectRole">aos1.jsp</result>
<!--<result name="selectRole">WEB-INF/jsp/admin/SelectRole.jsp</result>-->
</action>
<action name="home">
<!-- <result>index.jsp</result>-->
<interceptor-ref name="loginStack"></interceptor-ref>
<result name="success" type="chain">HomePage</result>
</action>
<action name="logOutUser" class="org.iaf.aos.web.login.action.LogOutUserAction">
<interceptor-ref name="loginStack"></interceptor-ref>
<result name="logout">WEB-INF/jsp/admin/LoggedOut.jsp
</result>
</action>
LogOutUserAction.java
public class LogOutUserAction extends ActionSupport {
private static final long serialVersionUID = 1L;
public String execute() throws Exception {
System.out.println("inside :: LogOutUserAction------");
Map session = ActionContext.getContext().getSession();
session.remove("loggedInUser");
return "logout";
}
}
logout.jsp
<td width="*" align="right" valign="top">
<s:url var="urlLogOut" action="logOutUser.action">
<s:param name="loginOut" value="%{'2'}"/>
</s:url>
<sx:a href="%{#urlLogOut}" targets="divAddEditUser">
<font color="white">Log Out</font>
</sx:a>
<!--<font color="white">Log Out</font>
--></td>
</tr>
AdminUserAction.java
ServletActionContext.getRequest().getSession().setAttribute("loggedInUser", loginId);
return "selectRole";
You will have to do that explicitly write that logic, It wont do by default.
In your logout action after removing the user from session, invalidate it too.
session.remove("loggedInUser");
session.invalidate();
session = null;
Then in your interceptor you check if session is valid or not, if not then you add an attribute, stating your session has either expired or you have logged out.
So your interceptor code become something like this:
HttpSession session = request.getSession(true);
if(session == null){
request.setAttribute("SessionExpired","Your session has expired or you have logged out");
}
Object user = session.getAttribute(USER_HANDLE);
Then if your jsp page when you show the login screen you check if request has "SessionExpired" attribute, if yes show that to the user.

Spring Security Method Security Interceptor not picking up authenticationManager

I'm trying to write a custom method security interceptor. However, it isn't using the authentication manager I added to the bean properties in my security context and returning null when I check to see if the authentication manager exists. Could anyone shed light on why the authentication manager bean property isn't being used? I'm using spring security 3.0.5 on WebSphere 7.0
Here's the bean containing the method interceptor
<beans:bean id="methodInterceptor"
class="bigbank.security.CustomMethodSecInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="universalAccessDecisionManager" />
<beans:property name="securityMetadataSource" ref="tspmMethodSecurityMetaData" />
Here's my method security interceptor
public class CustomMethodSecInterceptor extends MethodSecurityInterceptor {
private static final Log logger = LogFactory
.getLog(WebSphere2SpringSecurityPropagationInterceptor.class);
private AuthenticationManager authenticationManager = null;
private AuthenticationDetailsSource authenticationDetailsSource = new WebSpherePreAuthenticatedAuthenticationDetailsSource();
private final WASUsernameAndGroupsExtractor wasHelper;
public CustomMethodSecInterceptor() {
wasHelper = new DefaultWASUsernameAndGroupsExtractor();
}
#Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
logger.debug("Performing Spring Security authentication with WebSphere credentials");
System.out.println("##going through ss authentication");
authenticateSpringSecurityWithWASCredentials();
InterceptorStatusToken token = super.beforeInvocation(mi);
logger.debug("Proceeding with method invocation");
Object result = mi.proceed();
return super.afterInvocation(token, result);
} finally {
logger.debug("Clearing Spring Security security context");
SecurityContextHolder.clearContext();
}
}
private void authenticateSpringSecurityWithWASCredentials() {
Assert.notNull(authenticationManager); // This is where the error is coming up
Assert.notNull(authenticationDetailsSource);
String userName = wasHelper.getCurrentUserName();
if (logger.isDebugEnabled()) {
logger.debug("Creating authentication request for user " + userName);
}
PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(
userName, "N/A");
authRequest.setDetails(authenticationDetailsSource.buildDetails(null));
if (logger.isDebugEnabled()) {
logger.debug("Authentication request for user " + userName + ": "
+ authRequest);
}
Authentication authResponse = authenticationManager
.authenticate(authRequest);
if (logger.isDebugEnabled()) {
logger.debug("Authentication response for user " + userName + ": "
+ authResponse);
}
SecurityContextHolder.getContext().setAuthentication(authResponse);
}
public void setAuthenticationManager(
AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
}
Here's the error:
Caused by: java.lang.IllegalArgumentException: An AuthenticationManager is required
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.afterPropertiesSet(AbstractSecurityInterceptor.java:118)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1469)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1409)
... 119 more
You have overridden the setAuthenticationManager method, so when it is invoked by Spring to inject the AuthenticationManager, it doesn't set the corresponding field in AbstractSecurityInterceptor.
Since the base class contains a getter for this property, you would be best to remove the field and setter method, and just use the getter to access the authentication manager in your code.

AppFabric: Could not contact the cache service

Update: I have now implemented this properly. For more information see my blog post about it.
I'm trying to use AppFabric with NHibernate as my second level cache provider but I'm getting the following error: ErrorCode:Initialization: Could not contact the cache service. Contact administrator and refer to product help documentation for possible reasons.
I presume that the problem is with my configuration in web.config:
<section name="dcacheClient"
type="Microsoft.ApplicationServer.Caching.DataCacheClientSection, Microsoft.ApplicationServer.Caching.Core"
allowLocation="true"
allowDefinition="Everywhere"/>
...
<dcacheClient deployment="routing" localCache="False">
<localCache isEnabled="false" sync="TimeoutBased" ttlValue="300" />
<hosts>
<host name="localhost" cachePort="22233" cacheHostName="AppFabricCachingService" />
</hosts>
</dcacheClient>
I've downloaded the NHibernate.Caches source code to try and discover where the problem lies and the exception is being thrown in the VelocityClient constructor when the GetCache method is called:
public VelocityClient(string regionName, IDictionary<string, string> properties)
{
region = regionName.GetHashCode().ToString(); //because the region name length is limited
var cacheCluster = new CacheFactory();
cache = cacheCluster.GetCache(CacheName);
try
{
cache.CreateRegion(region, true);
}
catch (CacheException) {}
}
If I add a watch to the cacheCluster variable, I can find a _servers private variable which has one System.Data.Caching.EndpointID which has the MyURI property set to net.tcp://localhost:22234/AppFabricCachingServive which I presume has come from the configuration in web.config.
If you don't know the exact cause of the problem but have some ideas on how to go about troubleshooting this problem, that would be much appreciated as well.
Additional Info
I get the following results from the command, Get-CacheHostConfig -HostName tn-staylor-02 -CachePort 22233:
HostName : tn-staylor-02
ClusterPort : 22234
CachePort : 22233
ArbitrationPort : 22235
ReplicationPort : 22236
Size : 3001 MB
ServiceName : AppFabricCachingService
HighWatermark : 90%
LowWatermark : 70%
IsLeadHost : True
So I think the values I've got configured in web.config are OK.
Googling this problem and investigating how to set up AppFabric in the first place, I have come across two slightly different ways of how to configure the cache in web.config. The way I have described above and the way Hanselman has it in his AppFabric blog post
I actually started with it like this however, I got the following error which is how I came to have it configured how I have it now:
ErrorCode:"dcacheClient" tag not specified in the application configuration file. Specify valid tag in configuration file.
Full stack trace of the exception that gets thrown in VelocityClient:
System.Data.Caching.CacheException occurred
Message="ErrorCode:\"dcacheClient\" tag not specified in the application configuration file. Specify valid tag in configuration file."
Source="CacheBaseLibrary"
ErrorCode="ERRCMC0004"
StackTrace:
at System.Data.Caching.ClientConfigFile.ThrowException(String errorCode, String param)
at System.Data.Caching.ClientConfigReader.GetDeployementMode()
at System.Data.Caching.ClientConfigurationManager.InitializeDepMode(ClientConfigReader cfr)
at System.Data.Caching.ClientConfigurationManager.Initialize(String path)
at System.Data.Caching.ClientConfigurationManager..ctor()
at System.Data.Caching.CacheFactory.InitCacheFactory()
at System.Data.Caching.CacheFactory.GetCache(String cacheName)
at NHibernate.Caches.Velocity.VelocityClient..ctor(String regionName, IDictionary`2 properties) in C:\Source\Projects\NHibernate.contrib\trunk\src\NHibernate.Caches\Velocity\NHibernate.Caches.Velocity\VelocityClient.cs:line 67
InnerException:
EDIT: Added output from get-cachehost as requested by #PhilPursglove
Output from get-cachehost:
HostName : CachePort Service Name Service Status Version Info
-------------------- ------------ -------------- ------------
tn-staylor-02:22233 AppFabricCachingService UP 1 [1,1][1,1]
SOLUTION: #PhilPursglove was spot on. The NHibernate velocity provider was using old dll's so upgrading them and making a few code changes resolved my problems. I thought I would include my complete solution here.
Downloaded the NHibernate.contrib source from the SVN repository at https://nhcontrib.svn.sourceforge.net/svnroot/nhcontrib/trunk
Opened up the NHibernate.Caches.Everything solution and removed the references to the old velocity dll's from the NHibernate.Caches.Velocity project.
Added references to the App Fabric dll's which were installed when I installed App Fabric. This isn't the normal case of adding a reference to an assembly in the GAC, but this article describes how to do it.
Adding the new references meant that the VelocityClient class no longer compiled. With a little bit of help from this I came up with the version of VelocityClient.cs below.
I added a reference to the new version of NHibernate.Caches.Velocity to my project, made the changes below to my configuration and everything worked.
VelocityClient.cs
using System;
using System.Collections.Generic;
using Microsoft.ApplicationServer.Caching;
using log4net;
using NHibernate.Cache;
using CacheException = Microsoft.ApplicationServer.Caching.DataCacheException;
using CacheFactory = Microsoft.ApplicationServer.Caching.DataCacheFactory;
namespace NHibernate.Caches.Velocity
{
public class VelocityClient : ICache
{
private const string CacheName = "nhibernate";
private static readonly ILog log;
private readonly DataCache cache;
private readonly string region;
private Dictionary<string, DataCacheLockHandle> locks = new Dictionary<string, DataCacheLockHandle>();
static VelocityClient()
{
log = LogManager.GetLogger(typeof (VelocityClient));
}
public VelocityClient() : this("nhibernate", null) {}
public VelocityClient(string regionName) : this(regionName, null) {}
public VelocityClient(string regionName, IDictionary<string, string> properties)
{
region = regionName.GetHashCode().ToString(); //because the region name length is limited
var cacheCluster = new CacheFactory();
cache = cacheCluster.GetCache(CacheName);
try
{
cache.CreateRegion(region);
}
catch (CacheException) {}
}
#region ICache Members
public object Get(object key)
{
if (key == null)
{
return null;
}
if (log.IsDebugEnabled)
{
log.DebugFormat("fetching object {0} from the cache", key);
}
DataCacheItemVersion version = null;
return cache.Get(key.ToString(), out version, region);
}
public void Put(object key, object value)
{
if (key == null)
{
throw new ArgumentNullException("key", "null key not allowed");
}
if (value == null)
{
throw new ArgumentNullException("value", "null value not allowed");
}
if (log.IsDebugEnabled)
{
log.DebugFormat("setting value for item {0}", key);
}
cache.Put(key.ToString(), value, region);
}
public void Remove(object key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
if (log.IsDebugEnabled)
{
log.DebugFormat("removing item {0}", key);
}
if (Get(key.ToString()) != null)
{
cache.Remove(region, key.ToString());
}
}
public void Clear()
{
cache.ClearRegion(region);
}
public void Destroy()
{
Clear();
}
public void Lock(object key)
{
DataCacheLockHandle lockHandle = null;
if (Get(key.ToString()) != null)
{
try
{
cache.GetAndLock(key.ToString(), TimeSpan.FromMilliseconds(Timeout), out lockHandle, region);
locks.Add(key.ToString(), lockHandle);
}
catch (CacheException) {}
}
}
public void Unlock(object key)
{
DataCacheLockHandle lockHandle = null;
if (Get(key.ToString()) != null)
{
try
{
if (locks.ContainsKey(key.ToString()))
{
cache.Unlock(key.ToString(), locks[key.ToString()], region);
locks.Remove(key.ToString());
}
}
catch (CacheException) {}
}
}
public long NextTimestamp()
{
return Timestamper.Next();
}
public int Timeout
{
get { return Timestamper.OneMs * 60000; } // 60 seconds
}
public string RegionName
{
get { return region; }
}
#endregion
}
}
NHibernate.config:
...
<property name="cache.provider_class">NHibernate.Caches.Velocity.VelocityProvider, NHibernate.Caches.Velocity</property>
<property name="cache.use_second_level_cache">true</property>
<property name="cache.use_query_cache">true</property>
...
web.config
...
<section name="dataCacheClient"
type="Microsoft.ApplicationServer.Caching.DataCacheClientSection, Microsoft.ApplicationServer.Caching.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
allowLocation="true"
allowDefinition="Everywhere"/>
...
<dataCacheClient>
<!-- cache host(s) -->
<hosts>
<host
name="localhost"
cachePort="22233"/>
</hosts>
</dataCacheClient>
...
I didn't make any further changes to my App Fabric configuration or anything.
I think there are two possible culprits here:
In your web.config under the hosts element, you're listing localhost - I'd try swapping that out for the actual server name tn-staylor-02
That exception stack trace refers to CacheBaseLibrary - I don't know a great deal (read: anything!) about NHibernate but I would hazard a guess that that cache might not be built with the release version of AppFabric - CacheBaseLibrary was an assembly that appeared in the CTPs and betas but I didn't think it was used in the RTM version. Note that in the section element for dcacheclient, it refers to the Microsoft.ApplicationServer.Caching.Core assembly.