Spring Security allowing unauthorized access to secured method - spring

I am trying to add security to my web application using Spring MVC, REST controllers and Spring Data.
The issue I am encountering is that the methods annotated with #PreAuthorized are accessible to anyone (no login is required).
My Controller:
#RestController
#RequestMapping("/controller")
public class Controller {
#RequestMapping(value = "/public/{name}", method = RequestMethod.GET)
public String storeEntityPublic(#PathVariable String name) {
String result = "Hello " + name + ", I am saving on the db. (PUBLIC)";
/* stuff */
return result;
}
#PreAuthorize("hasAnyRole('ROLE_USER,ROLE_ADMIN')")
#RequestMapping(value = "/user/{name}", method = RequestMethod.GET)
public String storeEntityUserOrAdmin(#PathVariable String name) {
String result = "Hello " + name
+ ", I am saving on the db. (USER OR ADMIN)";
controller.saveEntity(name);
return result;
}
#PreAuthorize("hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/admin/{name}", method = RequestMethod.GET)
public String storeEntityAdmin(#PathVariable String name) {
String result = "Hello Admin " + name
+ ", I am saving on the db. (ADMIN ONLY)";
controller.saveEntity(name);
return result;
}
}
My security config:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="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-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<global-method-security pre-post-annotations="enabled" />
<http auto-config="true" />
<!-- Configure Authentication mechanism -->
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="admin" password="admin" authorities="ROLE_ADMIN" />
<user name="user" password="user" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
My applicationContext correctly import the security configuration file.
In my web.xml, I added the following:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Finally, my applicationContext.xml
<?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:context="http://www.springframework.org/schema/context"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<context:property-placeholder location="classpath*:spring/*.properties" />
<context:component-scan base-package="org.my.project" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${database.driverClassName}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
<property name="initialSize" value="3" />
<property name="maxActive" value="10" />
</bean>
<tx:annotation-driven mode="proxy"
transaction-manager="transactionManager" />
<bean class="org.springframework.orm.jpa.JpaTransactionManager"
id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
id="entityManagerFactory">
<property name="persistenceUnitName" value="persistenceUnit" />
<property name="dataSource" ref="dataSource" />
</bean>
<import resource="classpath:spring/applicationContext-jpa.xml" />
<import resource="classpath:spring/applicationContext-security.xml" />
</beans>
The project is correctly deployed and I have no warnings concerning security.

Spring Security documentation says on annotated methods:
The annotated methods will only be secured for instances which are defined as Spring beans (in the same application context in which method-security is enabled).
I suppose that you define your controller beans in different context than you define your security context. Try to put the below element into context defining beans to be secured.
<global-method-security pre-post-annotations="enabled" />

Related

Differents application Context in Spring 4

I have a login service in my application implementing UserDetailsService:
#Service
#Transactional
public class LoginService implements UserDetailsService {
#Autowired
UserService userService;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Assert.notNull(username);
UserDetails result = userService.loadUserDetailsByUsername(username);
Assert.notNull(result);
// WARNING: The following sentences prevent lazy initialisation problems!
Assert.notNull(result.getAuthorities());
result.getAuthorities().size();
return result;
}
}
The app dies with the error Exception encountered during context initialization. The start of the trace is:
Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#0' while setting bean property 'sourceList' with key [0] ...
The end is:
Cannot resolve reference to bean 'loginService' while setting bean property 'userDetailsService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'loginService' is defined
So it cannot find the loginService.
My web.xml is:
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<display-name>SimpleSpringProject</display-name>
<description>All you need!</description>
<!-- Loads Spring Security config file -->
<!--
contextConfigLocation is the context parameter where we provide the spring security beans configuration file name.
It is used by ContextLoaderListener to configure authentication in our application
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/spring-*.xml</param-value>
</context-param>
<!-- Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Creates the Spring Container shared by all Servlet and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Handles Spring requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/webmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
In the contextConfigLocation I am loading 3 files: spring-security.xml, spring-datasource.xml and spring-jpa.xml.
spring-security.xml:
<?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.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http auto-config="true" use-expressions="true">
<security:intercept-url pattern="/user/**" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/role/**" access="hasRole('ROLE_ADMIN')" />
<security:form-login
login-processing-url="/login"
login-page="/loginForm"
default-target-url="/"
authentication-failure-url="/loginForm?error"
username-parameter="username"
password-parameter="password" />
<security:logout logout-url="/logout" logout-success-url="/" delete-cookies="JSESSIONID" />
<security:csrf />
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="loginService"></security:authentication-provider>
</security:authentication-manager>
</beans>
spring-jpa.xml:
<?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:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<jpa:repositories base-package="com.ssp" />
</beans>
spring-datasource.xml:
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- Enable #Transactional annotation -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- MySQL Datasource with Commons DBCP connection pooling -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/simplesp" />
<property name="username" value="root" />
<property name="password" value="betis000" />
<property name="testOnBorrow" value="true"/>
<property name="testOnReturn" value="true"/>
<property name="testWhileIdle" value="true"/>
<property name="timeBetweenEvictionRunsMillis" value="1800000"/>
<property name="numTestsPerEvictionRun" value="3"/>
<property name="minEvictableIdleTimeMillis" value="1800000"/>
<property name="validationQuery" value="SELECT 1"/>
</bean>
<!-- EntityManagerFactory -->
<bean
id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="persistenceUnit" />
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Transaction Manager -->
<bean
id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
</beans>
In the DispatcherServlet I am loading webmvc.xml:
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Enable annotation-based Spring MVC controllers (eg: #Controller annotation) -->
<mvc:annotation-driven />
<!-- Classpath scanning of #Component, #Service, etc annotated class -->
<context:component-scan base-package="com.ssp" />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving
up static resources in the ${webappRoot}/resources directory -->
<mvc:resources mapping="/resources/**" location="/WEB-INF/resources/" />
<!-- Register "global" interceptor beans to apply to all registered HandlerMappings -->
<mvc:interceptors>
<!-- Set the language in variable lang -->
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
p:paramName="lang" />
</mvc:interceptors>
<!-- Resolve view name into jsp file located on /WEB-INF -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- Tiles -->
<bean id="tilesViewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.tiles3.TilesView" />
</bean>
<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles/tiles-definitions.xml</value>
</list>
</property>
</bean>
<!-- Resolves localized messages*.properties and application.properties
files in the application to allow for internationalization. The messages*.properties
files translate messages which are part of the admin interface, the application.properties
resource bundle localizes all application specific messages such as entity
names and menu items. -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource"
p:basenames="i18n/messages,i18n/application" />
<!-- Store preferred language configuration in a cookie -->
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver"
p:cookieName="locale" p:defaultLocale="en" />
</beans>
I am thinking that I have a problem with applicationContext because if I remove the #Service annotation and define the bean LoginService in the spring-security.xml:
<bean id="loginService" class="com.ssp.service.LoginService" />
then the applications starts and when I submit a login its UserService is null, so it seems that the context of LoginService is different to the context of the beans with:
<context:component-scan base-package="com.ssp" />
If anyone wants to see the full code of the app is in https://github.com/pedrogonzalezgutierrez/simplespringproject
I think you will need wrap your loginService with userDetailsServiceWrapper, try this:
<bean id="loginService" class="path.to.LoginService" />
<bean id="preAuthenticationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="loginService" />
</bean>
</property>
</bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="preAuthenticationProvider" />
</authentication-manager>
After research more and more I am sure that it was a problem with the application context. It seems that I had two differents context in my app. One of them was loaded by the contextConfigLocation and another one by the DispatcherServlet.
Just leave the param-value of the DispatcherServlet empty and load all the configurations by contextConfigLocation.
Also I updated the beans definitions to the version 3.2 except for Spring Security that requires 4.0

Unable to autowire a field in UserDetailsService

I know there are some questions about this topic but mine is a little different. I'm trying to include openID authentication into my project developed with Spring, Spring-security and Spring-MVC.
To achieve the openID auth, some clases are necessary: AccessDeniedHandler and UserDetailsService are configured in applicationContext-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:security="http://www.springframework.org/schema/security"
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-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- turn on global security -->
<security:global-method-security secured-annotations="enabled"/>
<bean id="openIdAuthFailureHandler" class="es.institution.dept.security.MyAccessDeniedHandler"/>
<bean id="userDetailsService" class="es.institution.dept.service.impl.UserDetailsServiceImpl"/>
<security:http auto-config="true">
<security:intercept-url pattern="/welcome*" access="ROLE_USER, ROLE_ADMIN" />
<security:intercept-url pattern="/user/*" access="ROLE_USER, ROLE_ADMIN" />
<security:intercept-url pattern="/rest/*" access="ROLE_USER, ROLE_ADMIN" />
<security:intercept-url pattern="/admin/*" access="ROLE_ADMIN" />
<security:logout logout-success-url="/" />
<security:openid-login login-page="/openidLogin" default-target-url="/welcome" authentication-failure-url="/loginfailed" user-service-ref="userDetailsService"/>
<security:access-denied-handler ref="openIdAuthFailureHandler"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:password-encoder hash="md5"/>
<security:jdbc-user-service data-source-ref="dataSource"
users-by-username-query="
SELECT username, password, active as enabled
FROM users WHERE username=?"
authorities-by-username-query="
select ur.username, ur.rolename as authority from users_roles ur
where ur.username=?" />
</security:authentication-provider>
</security:authentication-manager>
</beans>
UserDetailsService is called by Spring when it needs to know user data (username, password, roles...) For this reason, I need to call one of my services (UserService) in UserDetailsService:
public class UserDetailsServiceImpl implements UserDetailsService{
#Autowired
UserService userService;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
es.institution.dept.model.User user = userService.getUserByUsername("mannuk");
if(user == null)
throw new UsernameNotFoundException("User does not exist");
return new User(user.getUsername(), user.getPassword(), user.isActive(), false, false, false, getGrantedAuthorities(username));
}
public List<GrantedAuthority> getGrantedAuthorities(String username) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (Role role : userService.getAllRoles(username)) {
authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
}
return authorities;
}
}
I tried two options:
1)Define #Service annotation in UserDetailsService which throws an Exception during the start up. It says that UserDetails bean does not exist (it is necessary in applicationSecurity-context.xml)
2)Declare a bean definition in applicationContext-security.xml. The startup is OK (no errors) but the UserService is not autowired.
This is my applicationContext.xml:
<?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:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="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">
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/adminDB"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="/WEB-INF/mybatis-config.xml" />
</bean>
<bean id="usersMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.institution.dept.dao.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="rolesMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.institution.dept.dao.RoleMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="groupMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.institution.dept.dao.GroupMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="policyMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.institution.dept.dao.PolicyMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<!-- Json converter bean -->
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
<bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"></bean>
</beans>
This is my app-servlet.xml:
<?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:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
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.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd">
<!-- Enabling Spring beans auto-discovery -->
<context:component-scan base-package="es.institution.dept" />
<!-- Enabling Spring MVC configuration through annotations -->
<mvc:annotation-driven />
<!-- Enabling Spring Async tasks through annotations -->
<task:annotation-driven />
<mvc:view-controller path="/" view-name="login" />
<!-- Load resources -->
<mvc:resources mapping="/resources/**" location="/resources/"/>
<!-- Bean definitions i18n -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
<!-- Intercepts the change of the locale: example.html?ln=en -->
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="ln" />
</bean>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" >
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
</list>
</property>
</bean>
<!-- Register the messages.properties -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="WEB-INF/classes/locale/messages" />
</bean>
<!-- Defining which view resolver to use -->
<bean class= "org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
Note that UserService is working fine in other places like controllers. It seems to be a problem with the UserDetailsService itself.
If you need more info do not hesitate to write to me. I hope to solve this issue. The solution will be voted and checked.
Beans declared in root application context (applicationContext.xml and applicationContext-security.xml) cannot access beans declared in servler-specific context (app-servlet.xml)
Components of Spring Security (including UserDetailsService) must be declared in root application context
So, you need to declare UserService in applicationContext.xml instead of picking it up by <context:component-scan> in app-servlet.xml.

Spring Security cannot reach custom userDetailsService Bean in Service layer

Edit: I just realized that the issue does not come from spring-security not being able to instantiate userDetailsServiceImpl that is referenced in the spring-security.xml file, it is the addUserFormController that cannot be instantiated because it cannot autowire the UserDetailsServiceImpl. So the problem comes from the fact that this Controller cannot reach, for whatever reasons, this Service bean UserDetailsServiceImpl. I think I did properly made my component scan, by only scanning the controllers in mvc-dispatcher-servlet.xml, and all the other one except controller in the applicationContext.
I am using Spring MVC and Hibernate and I am trying to integrate Spring Security with a custom UserDetailsService and an Assembler.
It looks like my spring security cannot access the beans that are supposed to be scanned by the applicationContext.xml.
When loading my application I am running into the following error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'addUserFormController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.controller.AddUserFormController.setUserDetailsService(com.service.UserDetailsServiceImpl); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.service.UserDetailsServiceImpl] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:287)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106)
Here is my spring-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<http auto-config='true' use-expressions='true'>
<intercept-url pattern="/login*" access="isAnonymous()" />
<intercept-url pattern="/secure/**" access="hasRole('ROLE_Admin')" />
<logout logout-success-url="/listing.htm" />
<form-login login-page="/login.htm" login-processing-url="/j_spring_security_check"
authentication-failure-url="/login_error.htm" default-target-url="/listing.htm"
always-use-default-target="true" />
</http>
<beans:bean id="com.daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService" />
</beans:bean>
<beans:bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<beans:property name="providers">
<beans:list>
<beans:ref local="com.daoAuthenticationProvider" />
</beans:list>
</beans:property>
</beans:bean>
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
<password-encoder hash="plaintext" />
</authentication-provider>
</authentication-manager>
</beans:beans>
Here is my web.xml:
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Spring MVC Application</display-name>
<!-- Spring MVC -->
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- This listener creates the root application Context -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml,
/WEB-INF/spring-security.xml
</param-value>
</context-param>
<!-- Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Here is my applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<!-- Load everything except #Controllers -->
<context:component-scan base-package="com">
<context:exclude-filter expression="org.springframework.stereotype.Controller"
type="annotation" />
</context:component-scan>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- <property name="packagesToScan">
<list>
<value>com.service</value>
<value>com.dao</value>
</list>
</property> -->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/tutospringsource" />
<property name="username" value="root" />
<property name="password" value="password" />
</bean>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>mymessages</value>
</list>
</property>
</bean>
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="save*" />
<tx:method name="*" read-only="false" />
</tx:attributes>
</tx:advice>
And finally, my mvc-dispatcher-servlet.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
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/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:annotation-config />
<context:property-placeholder location="classpath:hibernate.properties" />
<!-- Load #Controllers only -->
<context:component-scan base-package="com.controller"
use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller"
type="annotation" />
</context:component-scan>
</beans>
My AddUserFormController:
#Controller
#RequestMapping("/register.htm")
public class AddUserFormController {
private UserDetailsServiceImpl userDetailsService;
#Autowired
public void setUserDetailsService(UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
My UserDetailsServiceImpl:
package com.service;
#Service("userDetailsService")
#Transactional
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserEntityDAO dao;
#Autowired
private Assembler assembler;
I have been able to fix that issue.
In my AddUserFormController, I had this:
#Controller
#RequestMapping("/register.htm")
public class AddUserFormController {
private UserDetailsService userDetailsService;
#Autowired
public void setUserDetailsService(UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
Here is how it looks now:
#Controller
#RequestMapping("/register.htm")
public class AddUserFormController {
#Autowired
private UserDetailsService userDetailsService;
public void setUserDetailsService(UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
I moved the #Autowired from the setter directly to the attribute, AND most important, I changed the type of userDetailsService to UserDetailsService, rather than UserDetailsServiceImpl.

Spring with Thymeleaf view integration

Starting out with Spring MVC and Thymeleaf for a project - But I am facing the issue of loading the index page -
My Controller is as follows -
#Controller
public class HomeController {
#RequestMapping(method=RequestMethod.GET, value="/")
public ModelAndView index(){
ModelAndView mav = new ModelAndView();
mav.setViewName("index");
mav.addObject("user", new UserCredential());
return mav;
}
and my servlet.xml file is
<?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:context="http://www.springframework.org/schema/context"
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.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<context:component-scan base-package="web.controller" />
<!-- Enabling Spring MVC configuration through annotations -->
<mvc:annotation-driven />
<!-- Mapping Static Resources -->
<mvc:resources mapping="/resources/**" location="/resources/" />
<bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5" />
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
</bean>
<bean class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<property name="order" value="1" />
<property name="viewNames" value="*.html"/>
</bean>
</beans>
My file structure is --
WebContent
-- WEB_INF
-- views
-- index.html
However I get the following exception -
javax.servlet.ServletException: Could not resolve view with name 'index1' in servlet with name 'springMVC'
Am I placing the file in the wrong place or missing something in my controller?
Solved it, I had to return the view name with the extension since I was telling my viewResolver to only resolve to names ending in .html ..
This thread pointed me to the answer http://forum.thymeleaf.org/Issue-with-my-Thymeleaf-Spring-configuration-td4024996.html

Pointcut to Spring #RequestMapping

I'm just now trying to enable AOP on my Spring project. I want to execute code (a session cleaning) AFTER the #RequestMapping has completed. Here is my applicationContext.xml (simply load referenced resources
<?xml version="1.0" encoding="UTF-8" standalone="no"?><beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">
<!-- ******************************************************************** -->
<!-- Include context files from different layers -->
<!-- ******************************************************************** -->
<import resource="classpath:appname-security-context.xml"/>
<import resource="classpath:appname-service-context.xml"/>
<import resource="classpath:appname-dao-context.xml"/>
</beans>
the security context:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<http auto-config="true">
<intercept-url pattern='/pages/login.jsp' />
<intercept-url pattern="/secure/*" access="IS_AUTHENTICATED_REMEMBERED" />
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<form-login login-page="/pages/login.jsp" authentication-failure-url="/pages/login.jsp?login_error=true" />
<logout logout-success-url="/pages/logout-redirect.jsp" invalidate-session="true" />
<remember-me key="appnameRMKey" user-service-ref="userDetailsService" />
</http>
<authentication-manager alias="authenticationManager" >
<authentication-provider user-service-ref='userDetailsService' >
<password-encoder hash="plaintext"/>
</authentication-provider>
</authentication-manager>
<beans:bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<beans:property name="rolePrefix" value="ROLE_" />
<beans:property name="dataSource" ref="springSecurityDataSource" />
<beans:property name="usersByUsernameQuery" value="SELECT username,password,enabled FROM Users WHERE username = ?" />
<beans:property name="authoritiesByUsernameQuery" value="SELECT u.username, a.authorityname FROM Users u JOIN Users_Authorities ua on u.id = ua.user_id JOIN Authorities a on ua.authorities_id = a.id WHERE u.username = ?" />
</beans:bean>
<!-- ******************************************************************** -->
<!-- Apply security for all beans where security was set -->
<!-- ******************************************************************** -->
<global-method-security jsr250-annotations="enabled" proxy-target-class = "true" secured-annotations="enabled">
<protect-pointcut expression="execution(* appname.UsersDAO.*(..))" access="IS_AUTHENTICATED_REMEMBERED"/>
<protect-pointcut expression="execution(* appname.AuthoritiesDAO.*(..))" access="IS_AUTHENTICATED_REMEMBERED"/>
</global-method-security>
</beans:beans>
my service context:
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-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/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/lang
http://www.springframework.org/schema/lang/spring-lang-3.0.xsd">
<!-- ******************************************************************** -->
<!-- Scan for service layer annotated beans -->
<!-- ******************************************************************** -->
<context:component-scan base-package="appname" scoped-proxy="interfaces" />
<aop:config proxy-target-class="true">
</aop:config>
<aop:aspectj-autoproxy proxy-target-class="true">
</aop:aspectj-autoproxy>
<!-- ******************************************************************** -->
<!-- Mark bean transactions as annotation driven -->
<!-- ******************************************************************** -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
my web context:(BEAN DEFINITION OMITTED FOR SHORTNESS!!)
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="multipartResolver" class="org.skyway.spring.util.web.binary.ModelBindingMultipartResolver" />
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
<bean id="streamedBinaryContentView" class="org.skyway.spring.util.web.binary.ModelAttributeStreamer" />
<bean id="beanNameViewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/pages/" />
</bean>
<bean id="iPhoneUserAgentViewResolver" class="org.skyway.spring.util.viewresolution.UserAgentViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="agentSubstring" value="iPhone" />
<property name="prefix" value="/WEB-INF/iphone/" />
<property name="order" value="0" />
</bean><bean id="webInfViewResolver" class="org.skyway.spring.util.viewresolution.AbsolutePathViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="order" value="-1" />
</bean>
<context:component-scan base-package="appname" scoped-proxy="interfaces" />
<aop:config proxy-target-class="true">
</aop:config>
<aop:aspectj-autoproxy proxy-target-class="true">
</aop:aspectj-autoproxy>
</beans>
this is the interface of aspect class
package appname;
import it.pstmarche.model.HibernateSessionFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
#Component
#Aspect
public interface SessionInterceptor {
#Pointcut("execution(public * appname.ImplantManager+.*(..))")
public void pc() ;
#Before("pc()")
public void print();
}
the implementation is trivial
#Before("pc()")
public void print(){
System.out.println("About to make call to print Hello World"
}
I also tried some other expressions like:
#Pointcut("execution(#org.springframework.web.bind.annotation.RequestMapping public * appname.*(..))")
and many others
with no luck. I start thinking the problem is not the expression (i already tried about 10-15 types...) but the class not being considered in the context.
For answer, take in consideration:
the name appname is intended for example, obviusly
inserting the aop:config and aop:aspectj-autoproxy are only a try because i also readed in other threads that the config must be enabled in every file that is loaded by applicationContext. I also tried inserting only in one but no result
the controller has the annotated interface + annotated implementation. i also tried without annotating the interface or removing completely with no result
any help? :)
EDIT: in response to axtavt .. sorry i forgot to add my web.xml, here is a piece of it
<servlet>
<description>context-servlet</description>
<servlet-name>appname Servlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:appname-web-context.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
i correctly load the contextConfigLocation via web-context.xml file. Sorry i forgot it :-(
EDIT2: I got the answer following my mistake by the suggestion of axtavt! thanks!
AOP is configured at per-context basis.
You have two contexts - root web application context (applicationContext.xml, which includes config files imported into it), and application context of DispatcherServlet, where controllers beans are declared.
So, in order to apply aspects to controllers you need to add <aop:aspectj-autoproxy> to servlet-specific context.
Also note that you shouldn't use <context:component-scan> over the same base package in different contexts, see #Service are constructed twice.

Resources