Spring: MethodInvokingFactoryBean calls wrong method - java

I need to do the following Java line in XML:
usersConnectionRepository.setConnectionSignUp(new AccountConnectionSignUp());
So I did this:
<bean id="usersConnectionRepository"
class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository"
scope="singleton">
<constructor-arg ref="dataSource" />
<constructor-arg ref="connectionFactoryLocator" />
<constructor-arg ref="textEncryptor" />
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<bean id="accountConnectionSignUp" class="edu.kit.tm.cm.ksc.config.AccountConnectionSignUp" />
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="usersConnectionRepository" />
</property>
<property name="targetMethod">
<value>setConnectionSignUp</value>
</property>
<property name="arguments">
<list>
<ref local="accountConnectionSignUp" />
</list>
</property>
</bean>
The error occurs when the method is supposed to be called.
java.lang.NoSuchMethodException: com.sun.proxy.$Proxy12.setConnectionSignUp(edu.kit.tm.cm.ksc.config.AccountConnectionSignUp)
As you can see above, it's totally searching in the wrong package, and I have no idea why.
I have no idea how to debug this further. I'm inexperienced with Spring and it's XML-Notation. I hope someone can help me. Thank you.
UPDATE
As requested, the complete social.xml. Although, I do not think it is needed to solve this.
<?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:facebook="http://www.springframework.org/schema/social/facebook"
xmlns:twitter="http://www.springframework.org/schema/social/twitter"
xmlns:social="http://www.springframework.org/schema/social" xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/social/facebook http://www.springframework.org/schema/social/spring-social-facebook.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/social/twitter http://www.springframework.org/schema/social/spring-social-twitter.xsd
http://www.springframework.org/schema/social http://www.springframework.org/schema/social/spring-social.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<context:property-placeholder
location="classpath:/edu/kit/tm/cm/ksc/config/application.properties" />
<bean id="connectionFactoryLocator"
class="org.springframework.social.connect.support.ConnectionFactoryRegistry"
scope="singleton">
<property name="connectionFactories">
<list>
<bean
class="org.springframework.social.twitter.connect.TwitterConnectionFactory">
<constructor-arg value="${twitter.consumerKey}" />
<constructor-arg value="${twitter.consumerSecret}" />
</bean>
<bean
class="org.springframework.social.facebook.connect.FacebookConnectionFactory">
<constructor-arg value="${facebook.clientId}" />
<constructor-arg value="${facebook.clientSecret}" />
</bean>
</list>
</property>
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<bean id="usersConnectionRepository"
class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository"
scope="singleton">
<constructor-arg ref="dataSource" />
<constructor-arg ref="connectionFactoryLocator" />
<constructor-arg ref="textEncryptor" />
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<bean id="accountConnectionSignUp" class="edu.kit.tm.cm.ksc.config.AccountConnectionSignUp" />
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="usersConnectionRepository" />
</property>
<property name="targetMethod">
<value>setConnectionSignUp</value>
</property>
<property name="arguments">
<list>
<ref local="accountConnectionSignUp" />
</list>
</property>
</bean>
<bean id="connectionRepository" factory-method="createConnectionRepository"
factory-bean="usersConnectionRepository" scope="request">
<constructor-arg value="#{request.userPrincipal.name}" />
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<mvc:annotation-driven />
<bean
class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
<bean class="org.springframework.social.connect.web.ConnectController">
<!-- relies on by-type autowiring for the constructor-args -->
<!-- <constructor-arg ref="connectionFactoryLocator" /> -->
<!-- <constructor-arg ref="connectionRepository" /> -->
</bean>
<bean id="SimpleSignInAdapter" class="edu.kit.tm.cm.ksc.config.SimpleSignInAdapter" />
<bean class="org.springframework.social.connect.web.ProviderSignInController">
<!-- relies on by-type autowiring for the constructor-args -->
<constructor-arg ref="SimpleSignInAdapter" />
</bean>
Update 2
We wrote the Java-examples of the Spring-Social-Documentation to XML. In this case for the ProviderSigninControllers dependencies. Unfortunately there are no XML examples given in this case.

The simple solution is to change proxy-target-class to true in your usersConnectionRepository bean definition and add CGLIB to your class path.
If you don't need the proxying, remove it completely.
Explanation:
First, with this bean declaration
<bean id="usersConnectionRepository"
class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository"
scope="singleton">
<constructor-arg ref="dataSource" />
<constructor-arg ref="connectionFactoryLocator" />
<constructor-arg ref="textEncryptor" />
<aop:scoped-proxy proxy-target-class="false" />
</bean>
Spring is creating a bean of type JdbcUsersConnectionRepository and wrapping it in a JDK proxy (since proxy-target-class is false). The serious shortcoming of JDK proxies, is that they only sub type interfaces.
In other words, Spring will see that the JdbcUsersConnectionRepository class implements the UsersConnectionRepository interface and use that when generating the Proxy. As the javadoc says
A proxy class extends java.lang.reflect.Proxy.
A proxy class implements exactly the interfaces specified at its creation, in the
same order.
So the generated proxy will be of type Proxy and UsersConnectionRepository.
This won't be an issue for MethodInvokingFactoryBean because it stores the reference in a field of type Object. However, when MethodInvokingFactoryBean tries to resolve the Method to invoke, it uses the target object's Class instance, ie. object.getClass(). Since the target object is actually of type Proxy, or com.sun.proxy.$Proxy12 to be exact, it does not have a JdbcUsersConnectionRepository#setConnectionSignUp method and that causes a NoSuchMethodException.

Related

authenticating Spring Web Service with LDAP

I want to expose a sample Spring web service which is authenticated using LDAP.
First, I have created the web service:
import javax.jws.WebMethod;
import javax.jws.WebService;
import com.domain.SampleEntity;
/**
* Actual web service implementation.
*
*/
#WebService
public class SampleEntityWebService {
/**
* Read and return SampleEntity by a supplied id.
*/
#WebMethod
public SampleEntityByIdResponse readSampleEntityById(Long id) {
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setId(id);
SampleEntityByIdResponse sampleEntityByIdResponse = new SampleEntityByIdResponse();
sampleEntityByIdResponse.setSampleEntity(sampleEntity);
return sampleEntityByIdResponse;
}
}
Web Service Provider configuration contains:
<?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:int="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context" xmlns:ws="http://www.springframework.org/schema/integration/ws"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:sws="http://www.springframework.org/schema/web-services"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/integration/ws http://www.springframework.org/schema/integration/ws/spring-integration-ws-2.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
">
<!-- TOOD: Check if required or not -->
<!-- <bean id="simpleJaxWzServiceExporter"
class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter">
<property name="baseAddress" value="${ws.base.url}" />
</bean> -->
<!-- <context:component-scan base-package="com.integration.ws.provider" /> -->
<!-- <context:property-placeholder location="classpath:META-INF/spring/web-service.properties" /> -->
<bean id="sampleEntityMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.integration.ws.provider.SampleEntityByIdRequest</value>
<value>com.integration.ws.provider.SampleEntityByIdResponse</value>
<value>com.domain.SampleEntity</value>
</list>
</property>
</bean>
<bean
class="org.springframework.ws.server.endpoint.mapping.UriEndpointMapping">
<property name="mappings">
<props>
<prop key="${ws.base.url}/sampleEntityById">sampleEntity-by-id-gateway</prop>
</props>
</property>
<property name="interceptors">
<list>
<ref local="wsSecurityInterceptor" />
</list>
</property>
</bean>
**<bean id="wsSecurityInterceptor"
class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="classpath:META-INF/securityPolicy.xml" />
<property name="callbackHandlers">
<list>
<ref bean="authenticationHandler"/>
</list>
</property>
</bean>**
<bean id="authenticationHandler"
class="org.springframework.ws.soap.security.xwss.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService">
<bean class="org.springframework.security.core.userdetails.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
${wsUserName}=${wsUserPassword},ROLE_USER
</value>
</property>
</bean>
</property>
</bean>
<ws:inbound-gateway id="sampleEntity-by-id-gateway"
request-channel="sampleEntityRequestById" marshaller="sampleEntityMarshaller"
unmarshaller="sampleEntityMarshaller" reply-channel="sampleEntityResponse" />
<int:channel id="sampleEntityRequestById" />
<int:channel id="sampleEntityResponse" />
<int:service-activator
expression="#sampleEntityWebService.readSampleEntityById(payload.id)"
input-channel="sampleEntityRequestById" output-channel="sampleEntityResponse" requires-reply="true"/>
<int:channel id="sampleEntitys" />
</beans>
The Security policy file referred contains:
<xwss:SecurityConfiguration dumpMessages="true" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:RequireUsernameToken passwordDigestRequired="true" nonceRequired="true"/>
</xwss:SecurityConfiguration>
The service is working fine as such. Now I want to authenticate the users who access this service using LDAP.
I am new to Spring web services and security. Can anyone please suggest about the configuration changes required to integrate a Spring web service with LDAP.
You can change the user details service from InMemoryDaoImpl to LdapUserDetailsService.
The configuration I can derive is:
<bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://monkeymachine:389/dc=springframework,dc=org"/>
<property name="userDn" value="cn=manager,dc=springframework,dc=org"/>
<property name="password" value="password"/>
</bean>
<bean id="ldapPopulator" class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<constructor-arg ref="contextSource"/>
<constructor-arg value="ou=groups"/>
<property name="groupRoleAttribute" value="ou"/>
</bean>
<bean id="userSearch"
class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0"
value="ou=People,o=MyCompany,o=Intranet" />
<constructor-arg index="1" value="(uid={0})" />
<constructor-arg index="2" ref="contextSource" />
</bean>
<bean id="authenticationHandler" class="org.springframework.ws.soap.security.xwss.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService">
<bean class="org.springframework.security.ldap.userdetails.LdapUserDetailsService">
<constructor-arg ref="userSearch">
<constructor-arg ref="ldapPopulator">
</bean>
</property>
</bean>
Be in mind I haven't tried it yet, and most of the part I copied from another source. What you need is a UserDetailsService, and you just need to set that to the authenticationHandler. From the LdapUserDetailsService source code, it needs two constructor, LdapUserSearch, and LdapAuthoritiesPopulator. I googled an example on how to instantiate LdapUserSearch bean and found example from here. I found LdapPopulator bean example from the official documentation.
More details about Ldap Authentication with Spring Security can be found at the official documentation.
I hope you understand about LDAP, because I have no knowledge of LDAP. Good luck.

How do I set Spring Batch jobParameters in xml?

This job.xml works great when using parameters.
<beans>
<bean id="testFileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
<property name="resource" value="#{jobParameters['paramFileOuput']}"/>
<property name="shouldDeleteIfExists" value="true" />
<property name="lineAggregator">
...
</property>
</bean>
</beans>
But what if I don't want to use job parameters but instead use Spring profiles...
<beans>
<bean id="testFileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
<property name="resource" ref="testFileOutput"/>
<property name="shouldDeleteIfExists" value="true" />
<property name="lineAggregator">
....
</property>
</bean>
<beans profile="dev">
<bean id="testFileOutput" class="java.lang.String">
<constructor-arg type="java.lang.String" value="file:c:/temp/testfile.txt" />
</bean>
</beans>
<beans profile="prod">
<bean id="testFileOutput" class="java.lang.String">
<constructor-arg type="java.lang.String" value="file:pathtoprod.txt" />
</bean>
</beans>
</beans>
This appears to work. But this just doesn't feel right. Is there a way to set the parameter in the xml using profiles?
Something along these lines (this obviously doesn't work)
<beans profile="dev">
<setJobParameter key="testFileOuput" value="file:c:/temp/testfile.txt" />
</beans>
I can use any version of spring or springbatch.
How about this:
public FileOutput {
private String filename;
...
}
and
<beans profile="dev">
<bean id="testFileOutput" class="FileOutput " c:filename="file:pathtoprod.txt">
</beans>
Unluckily, you cant use p/c namespace with java.lang.String, thats why you need a little helper class. Anyway, the solution would shrink your context.xml a few lines and looks more similar to your desired solution.

Spring+Maven Property File not loading

I am trying to access a property file containing db configurations in a Maven + Spring project.
I get following error:
Cannot load JDBC driver class '${db_driver}'
My Property file is placed in src/resources folder.
Below is the tag to load property files:
<bean id="dbPropertyReader"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="1" />
<property name="locations">
<value>classpath:${appenv.deployment}.properties</value>
</property>
</bean>
Following tag uses properties loaded:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="url" value="${db_url}" />
<property name="driverClassName" value="${db_driver}" />
<property name="username" value="${db_username}" />
<property name="password" value="${db_password}" />
</bean>
Below are contents of properties file:
#JDBC Properties
db_driver=com.mysql.jdbc.Driver
db_url=jdbc\:mysql\://hostname\:3306/xxx_dbxxx?useUnicode\=true
db_username=abcdefgh
db_password=ijklmnopq
db_removeabadoned=true
db_initialsize=1
db_maxactive=2
${appenv.deployment} is a VMArgument set as follows:
-Dappenv.deployment=development
I have checked, this value is getting populated properly.
I am getting following line in logs:
Found key 'appenv.deployment' in [systemProperties] with type [String] and value 'development'
Then after this I am also getting following:
Loading properties file from class path resource [development.properties]
But some how, the values are not getting loaded.
Spring-Datasource.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="dbPropertyReader"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="1" />
<property name="locations">
<value>classpath:${appenv.deployment}.properties</value>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="url" value="${db_url}" />
<property name="driverClassName" value="${db_driver}" />
<property name="username" value="${db_username}" />
<property name="password" value="${db_password}" />
<property name="initialSize" value="${db_initialsize}" />
<property name="maxActive" value="${db_maxactive}" />
</bean>
<bean id="firstConfigDataFromDB" class="org.apache.commons.configuration.DatabaseConfiguration">
<constructor-arg type="javax.sql.DataSource" ref="dataSource" />
<constructor-arg index="1" value="tablename1" />
<constructor-arg index="2" value="propertyname2" />
<constructor-arg index="3" value="propertyvalue2" />
</bean>
<bean id="firstConfigDataFromDBFactory"
class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
<constructor-arg ref="firstConfigDataFromDB" />
</bean>
<!-- DB Properties Initialization -->
<bean id="firstConfigurationPlaceHolder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="2" />
<property name="ignoreUnresolvablePlaceholders" value="false"/>
<property name="properties" ref="firstConfigDataFromDBFactory" />
</bean>
<bean id="secondConfigurationFromDB"
class="org.apache.commons.configuration.DatabaseConfiguration">
<constructor-arg type="javax.sql.DataSource" ref="dataSource" />
<constructor-arg index="1" value="tablename2" />
<constructor-arg index="2" value="propertyname2" />
<constructor-arg index="3" value="propertyvalue2" />
</bean>
<bean id="secondConfigurationFromDBFactory"
class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
<constructor-arg ref="secondConfigurationFromDB" />
</bean>
<!--
Error Map Initialization
Subtype of org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
-->
<bean id="secondConfigurationPlaceHolder"
class="com.application.SecondConfigurationPlaceHolder">
<property name="order" value="3" />
<property name="ignoreUnresolvablePlaceholders" value="false"/>
<property name="properties" ref="secondConfigurationFromDBFactory" />
</bean>
</beans>
Generic.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
>
<!-- Enable annotation scanning -->
<context:annotation-config/>
<!-- Initialise connection to Database -->
<import resource="Spring-Datasource.xml"/>
<!-- Initialize mail connection -->
<import resource="Spring-Mail.xml"/>
<!-- Inject database connection to DAO -->
<import resource="Spring-DAO.xml"/>
<!-- Other Beans Below -->
</beans>
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
>
<import resource="generic.xml" />
<bean id="applicationBean" class="com.application.ApplicationBean" scope="singleton" >
<property .. />
<property .. />
</bean>
</beans>
I am loading applicationContext.xml with following statement in code:
`appContext = new ClassPathXmlApplicationContext("applicationContext.xml");`
applicationContext.xml imports generic.xml.
generic.xml imports Spring-DataSource.xml.
have you added this to your application context file
<context:property-placeholder location="Path for .properties file"/>
add this line before the beans

Securing controllers between users with same Role in Spring security

In my application users can manipulate with things, that linked only to them. All users have same role in spring security. So, to forbid user to view not his stuff, I need to implement in some controllers methods my own function to validate users rights.
public void securityValidation(User currentUser, Thing thing) {
if(!thing.has(currentUser)) {
log.warn("Security Control. User: " + user .getId());
}
}
I think It's not cool. It's hard to find in code is the method secured or not.
May be Spring has more elegant way for this task?
Or may be I can write my own annotation for securing methods? Do I need annotation processor for that?
You are referring to Domain Object Security, it is not part of the base security package. Spring Security ACL is what you need, with it you can assert ownership of actual items, for example user 123 can edit item 789. The code below makes sure the current user has admin rights over the entity he is editing:
#PreAuthorize("hasPermission(#entity, 'ADMINISTRATION')")
public SomeEntity update(SomeEntity entity) {
...
}
But keep in mind, you now have to manage those permissions and give/remove them to individual users. There is also a way to do it as part of a group. You can say user 123 and 345 belong to GROUP_SOME_ID and then if you give GROUP_SOME_ID admin permission over an object, users 123 and 345 will get them automatically. Removing user 123 from the group would automatically remove his permission as well.
---- UPDATE ------
Below is sample application context that wires up Spring Security ACL:
<?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:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>
<!-- We'll rely on the standard AclPermissionEvaluator implementation -->
<bean class="org.springframework.security.acls.AclPermissionEvaluator" id="permissionEvaluator">
<constructor-arg ref="aclService" />
<property name="sidRetrievalStrategy" ref="sidRetrievalStrategy" />
<property name="permissionFactory" ref="permissionFactory"/>
</bean>
<bean class="org.springframework.security.acls.domain.SidRetrievalStrategyImpl" id="sidRetrievalStrategy" >
<constructor-arg ref="roleHierarchy" />
</bean>
<!-- Declare an acl service -->
<bean class="org.springframework.security.acls.jdbc.JdbcMutableAclService" id="aclService">
<constructor-arg ref="dataSource" />
<constructor-arg ref="lookupStrategy" />
<constructor-arg ref="aclCache" />
<property name="classIdentityQuery" value="select currval(pg_get_serial_sequence('acl_class', 'id'))" />
<property name="sidIdentityQuery" value="select currval(pg_get_serial_sequence('acl_sid', 'id'))" />
</bean>
<!-- Declare a lookup strategy -->
<bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
<constructor-arg ref="dataSource" />
<constructor-arg ref="aclCache" />
<constructor-arg ref="aclAuthorizationStrategy" />
<constructor-arg ref="permissionGrantingStrategy" />
<property name="permissionFactory" ref="permissionFactory"/>
</bean>
<bean id="permissionFactory" class="org.springframework.security.acls.domain.DefaultPermissionFactory" />
<!-- Declare an acl cache -->
<bean id="aclCache" class="org.springframework.security.acls.domain.SpringCacheBasedAclCache">
<constructor-arg>
<bean class="com.example.NoOpCache">
<constructor-arg value="aclCache" />
</bean>
</constructor-arg>
<constructor-arg ref="permissionGrantingStrategy" />
<constructor-arg ref="aclAuthorizationStrategy" />
</bean>
<!-- Declare an acl authorization strategy -->
<bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
<constructor-arg>
<list>
<bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
<constructor-arg value="ROLE_ADMIN" />
</bean>
<bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
<constructor-arg value="ROLE_ADMIN" />
</bean>
<bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
<constructor-arg value="ROLE_ADMIN" />
</bean>
</list>
</constructor-arg>
</bean>
<bean id="permissionGrantingStrategy" class="org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy" >
<constructor-arg>
<bean class="org.springframework.security.acls.domain.ConsoleAuditLogger" />
</constructor-arg>
</bean>
<bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy">
<value>
ROLE_USER > ROLE_ANONYMOUS
ROLE_SUPER_USER > ROLE_USER
ROLE_ADMIN > ROLE_SUPER_USER
</value>
</property>
</bean>
</beans>
#PreAuthorize("hasRole('ROLE_ADMIN')")
above any controller method you want to restrict to admins, for example.
Making sure you define methodSecurityExpressionHandler bean in web context.

Validate format parameter via config in Spring MVC

Here is my situation:
I have my mvc-config.xml file for my web service set up to have JSON as the default media type. I also have favorParameter for the ContentNegotiatingViewResolver as true. Additionally, I have useNotAcceptableStatusCode as true so that not accepted formats will return a 406.
My question is: Is there a way, in the config, to trigger the 406 status code when someone passes in an unacceptable format parameter (format=foo)? Or must that be done with code?
Here is the config file:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>
<property name="defaultViews">
<list>
<bean class="com.work.stuff.web.view.json.ExtendedMappingJacksonJsonView">
<property name="objectMapper">
<ref bean="JacksonObjectMapper" />
</property>
</bean>
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="marshaller">
<ref bean="Jaxb2Marshaller" />
</property>
</bean>
</list>
</property>
<property name="defaultContentType" value="application/json" />
<property name="favorParameter" value="true" />
<property name="useNotAcceptableStatusCode" value="true" />
</bean>
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper">
<ref bean="JacksonObjectMapper" />
</property>
</bean>
<ref bean="marshallingHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="marshallingHttpMessageConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<property name="marshaller" ref="Jaxb2Marshaller" />
<property name="unmarshaller" ref="Jaxb2Marshaller" />
</bean>
<bean id="JacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper" />
<bean id="JacksonSerializationConfig" class="org.codehaus.jackson.map.SerializationConfig"
factory-bean="JacksonObjectMapper" factory-method="getSerializationConfig" />
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="JacksonSerializationConfig" />
<property name="targetMethod" value="setSerializationInclusion" />
<property name="arguments">
<list>
<value type="org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion">NON_NULL</value>
</list>
</property>
</bean>
<bean id="Jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.work.stuff.Concepts</value>
<value>com.work.stuff.Concept</value>
<value>com.work.stuff.Terms</value>
<value>com.work.stuff.Term</value>
<value>com.work.stuff.Namespaces</value>
<value>com.work.stuff.Namespace</value>
<value>com.work.stuff.Subsets</value>
<value>com.work.stuff.Subset</value>
<value>com.work.stuff.Associations</value>
<value>com.work.stuff.Association</value>
</list>
</property>
</bean>
</beans>
ContentNegotiatingViewResolver doesn't seem to support such behaviour. For now, I think your best bet is to subclass it and override the getMediaTypeFromParameter() method to throw an exception if the media type is not supported.
You can throw any RuntimeException from that method, and if you annotate the exception class with #ResponseStatus, you can control the HTTP response code, e.g.
#ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
public class FormatNotSupportedException extends RuntimeException {
}
In the longer term, I strongly encourage you to file an issue with http://jira.springsource.org, asking for such functionality to be added to ContentNegotiatingViewResolver. They should be able to add this as an optional behavioural parameter. It's requests like these that mean Spring keeps getting better.

Categories