#Service instantiated twice - spring

Trying to make some experiments with Spring MVC and Spring Security:
#Controller
#RequestMapping("/auth")
public class AuthController {
#Autowired
// #Qualifier("userDetailsService") - tried adding this
private MyUserDetailsService userDetailsService;
...
}
// #Scope("singleton") - tried adding this
#Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
...
}
Complete context.xml that I have:
<?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"
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.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
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<!-- ORIGINAL springmvc-servlet.xml -->
<mvc:annotation-driven />
<mvc:resources mapping="/static/**" location="/static/" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<context:annotation-config />
<context:component-scan base-package="com.xxxxxxxxx" />
<!-- end ORIGINAL springmvc-servlet.xml -->
<!-- FROM springmvc-security.xml -->
<security:global-method-security secured-annotations="enabled">
</security:global-method-security>
<security:http auto-config="true" access-denied-page="/auth/denied">
<security:intercept-url pattern="/admin/*" access="ROLE_ADMIN"/>
<security:intercept-url pattern="/user/*" access="ROLE_USER"/>
<security:intercept-url pattern="/auth/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/auth/register" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:form-login login-page="/auth/login" authentication-failure-url="/auth/login?login_error=true" default-target-url="/user"/>
<security:logout logout-url="/auth/logout" logout-success-url="/" invalidate-session="true"/>
<security:openid-login authentication-failure-url="/login?login_error=t" user-service-ref="openIdUserDetailsService" />
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="userDetailsService" />
</security:authentication-manager>
<!-- end FROM springmvc-security.xml -->
</beans>
For some reason, there are 2 instances of MyUserDetailsService created. The first one is used by Spring Security and the second one is injected to AuthController. What's the right approach in case I want to have a single instance of MyUserDetailsService?

You haven't shown enough configuration to be certain, but I'd bet money that you're confused about how Spring ApplicationContexts should be managed in a Spring MVC app. My answer to another question about the same problem is almost certainly what you need to read:
Declaring Spring Bean in Parent Context vs Child Context
You've most likely declared your service bean (either explicitly or with a component-scan) in both the root and child contexts of your app. Being a service bean, it should live only in the root context. You may also benefit from reading this answer:
Spring XML file configuration hierarchy help/explanation

this config works for me :
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
and
<bean id="userService" class="com.mydomain.service.UserDetailsServiceImpl" />
A tutorial here.

Related

Spring security SessionRegistry not working

First of all I kept the listener in web.xml
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
Then my springSecurity.xml goes like
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:security="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<security:http auto-config="true" use-expressions="true">
<security:intercept-url pattern="/*" access="permitAll" />
<security:session-management invalid-session-url="/" session-fixation-protection="newSession">
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" session-registry-alias="sessionRegistry"/>
</security:session-management>
<!-- access denied page -->
<security:access-denied-handler error-page="/loginerror" />
<security:form-login
login-page="/login?login_error=1"
default-target-url="/employee/listEmployee"
authentication-failure-url="/login/error"
/>
<security:logout invalidate-session="true" logout-success-url="/login" delete-cookies="JSESSIONID" />
<!-- enable csrf protection -->
<!-- <csrf/>-->
</security:http>
<!-- Select users and user_roles from database -->
<security:authentication-manager>
<security:authentication-provider ref="authenticationProvider"></security:authentication-provider>
</security:authentication-manager>
<bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService">
<bean id="userAuthenticationService" class="com.elitenet.los.security.UserDetailsServiceImpl" />
</property>
<property name="passwordEncoder">
<bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" />
</property>
</bean>
The controller goes like:I need the list of userNames which are logged in. But the sessionRegistry isn't working.
#Autowired
#Qualifier("sessionRegistry")
private SessionRegistry sessionRegistry;
#RequestMapping(value = "/showUserStatus",method = RequestMethod.GET)
public ModelAndView showUserStatus() {
List<String> usersNamesList = new ArrayList<String>();
List<User> userList = new ArrayList<User>();
try {
List<Object> principals =sessionRegistry.getAllPrincipals();//the principals here is empty
for (Object principal: principals) {
//import org.springframework.security.core.userdetails for User class
//User is a built in class of spring security core
if (principal instanceof User) {
getLog().info(((User) principal).getUserName());
getLog().info("going to list userNameList");
usersNamesList.add(((User) principal).getUserName());
}
}
getLog().info("going to list user");
userList = getUserService().getList();
} catch (Exception er) {
getLog().error("error while listing userList" + er);
}
return new ModelAndView("/user/showUserStatus", "userList", userList);
}
Can anyone help me what am I doing wrong
Please try mentioning in xml file
<bean id="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl" />
#Controller class
Try injecting like below
#Resource(name="sessionRegistry")
private SessionRegistryImpl sessionRegistry;
I think you are almost there. The only thing you've probably missed is the use of session-registry-alias. By using that attribute on the concurrency-control element you expose the session registry, so that it can be injected to your own beans.
Now you have a reference to the session registry that will be populated by the ConcurrentSessionControlStrategy which is set up implicitly by the above configuration. To use it you would just inject it to your bean as normal:
<security:session-management>
<security:concurrency-control max-sessions="10" session-registry-ref="sessionRegistry"/>
</security:session-management>
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
or something like below
<bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy"
p:maximumSessions="1" >
<constructor-arg name="sessionRegistry" ref="sessionRegistry" />
</bean>

How to add LDAP cache in Spring LDAP?

I want to cache LDAP user data locally to allow faster queries. Do the Spring LDAP offers such a functionality? How can I do this?
I am using Spring Security 3.1 and Spring LDAP 1.3.1 for authentication and authorization. It would be nice to have a cache for LDAP using built-in mechanism if exists..
Spring LDAP configuration:
applicationContext-ldap.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:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd
">
<!-- Ldap -->
<jee:jndi-lookup id="ldapUrl" jndi-name="appName/ldapUrl" expected-type="java.lang.String" />
<jee:jndi-lookup id="ldapUser" jndi-name="appName/ldapUser" expected-type="java.lang.String" />
<jee:jndi-lookup id="ldapPassword" jndi-name="appName/ldapPassword" expected-type="java.lang.String" />
<!-- for authentication and search purpose -->
<bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" ref="ldapUrl" />
<property name="userDn" ref="ldapUser" />
<property name="password" ref="ldapPassword" />
<property name="pooled" value="true" />
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<property name="contextSource" ref="ldapContextSource" />
</bean>
<!-- for pagination search purpose -->
<bean id="dirContext" factory-bean="ldapContextSource" factory-method="getReadOnlyContext" scope="session"/>
<bean id="singleLdapContextSource" class="org.springframework.ldap.core.support.SingleContextSource" scope="session">
<constructor-arg ref="dirContext"/>
</bean>
<bean id="singleLdapTemplate" class="org.springframework.ldap.core.LdapTemplate" scope="session">
<property name="contextSource" ref="singleLdapContextSource" />
</bean>
</beans>
Spring Security configuration:
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-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- This is where we configure Spring-Security -->
<security:http
auto-config="true"
use-expressions="true"
access-denied-page="/auth/denied"
>
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/app/admin" access="permitAll"/>
<security:intercept-url pattern="/app/common" access="hasRole('User')"/>
<security:intercept-url pattern="/viol/home" access="permitAll"/>
<security:intercept-url pattern="/app/users" access="permitAll"/>
<security:intercept-url pattern="/admin/edit/*" access="hasRole('Administrator')"/>
<security:form-login
login-page="/auth/login"
authentication-failure-url="/auth/loginFailure"
default-target-url="/auth/authorize"/>
<security:logout
invalidate-session="true"
logout-success-url="/auth/login"
logout-url="/logout"/>
</security:http>
<security:authentication-manager>
<security:ldap-authentication-provider
server-ref="ldapContextSource"
user-search-filter="(sAMAccountName={0})"
user-search-base="dc=myDomain,dc=com"
/>
</security:authentication-manager>
</beans>
Thank you very much for your help!
If you configure EhCacheBasedUserCache and use ldap-user-service then you can use cache as:
<authentication-manager>
<authentication-provider>
<ldap-user-service
user-search-filter="(sAMAccountName={0})" user-search-base="dc=myDomain,dc=com" cache-ref="userCache" />
</authentication-provider>
</authentication-manager>
I don't think Spring offers client side LDAP caching out of the box, as caching LDAP query results on the client would pose a security risk. The cache will certainly hold stale data at some point, which is not a huge problem if it's e.g. the email/home address of the user, but much worse when it comes to e.g. role assignments and other authentication/authorization related data. You will be much better off by scaling up the server side, so that it's able to handle the load.
That's being said, introducing caching is pretty easy since Spring 3.1, because it provides excellent support for it. In your case it would be enough to use a custom LdapContextSource like the following:
public class CachingLdapContextSource extends AbstractContextSource {
#Override
protected DirContext getDirContextInstance(Hashtable environment)
throws NamingException
{
InitialLdapContext context = new InitialLdapContext(environment, null);
return new CachingDirContextWrapper(context);
}
}
The wrapper class simply delegates all DirContext methods to the underlying implementation and decorates methods to be cached with #Cacheable.
class CachingDirContextWrapper implements DirContext {
private final DirContext delegate;
CachingDirContextWrapper(DirContext delegate) {
this.delegate = delegate;
}
#Override
#Cacheable(value = "search")
public NamingEnumeration<SearchResult> search(...)
{
return delegate.search(name, matchingAttributes, attributesToReturn);
}
...
}
Refer to the official documentation, and this tutorial on details about how to configure a cache storage to be used by Spring.
But once again, you'd better not do this, I think.

spring beans Context Configuration

I am a newbie to spring beans. I'm referring books and blogs. In some the context configuration is given as <beans:bean> and in some just <beans>. What is the difference? Should we give the XML namespace in the context file? Will it refer the acutal site at the time of application deployment?
It doesn't matter as far as Spring is concerned - the XML must be valid so that Spring can understand it, that's it. It is up to you which format you choose. Normally you use default namespace to avoid typing too much (all examples based on Appendix C. XML Schema-based configuration):
<?xml version="1.0" encoding="UTF-8"?>
<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-3.0.xsd">
<bean id="..."/>
</beans>
xmlns="..." attribute defines default namespace (the one used if you don't specify any namespace at all, like <beans/>. This is fine as long as you use only single beans namespace and occasionally few declarations from other namespaces:
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<bean id="..." class="...">
<property name="isolation">
<util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
</property>
</bean>
</beans>
But sometimes you'll notice you are using way more nodes from different namespaces compared to default beans namespace. A good example is Spring Security configuration file:
<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.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<security:http auto-config='true'>
<security:intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/**" access="ROLE_USER" />
<security:form-login login-page='/login.jsp'/>
<security:session-management>
<security:concurrency-control max-sessions="1" />
</security:session-management>
<security:openid-login>
<security:attribute-exchange>
<security:openid-attribute name="email" type="http://axschema.org/contact/email" required="true" />
<security:openid-attribute name="name" type="http://axschema.org/namePerson" />
</security:attribute-exchange>
</security:openid-login>
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref='myUserDetailsService'/>
</security:authentication-manager>
<bean id="myUserDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
You see how clumsy it is because beans default namespace is used so rarely but the whole file must be cluttered with security: prefix? What about this (notice how xmlns namespace declarations have changed):
<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.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<http auto-config='true'>
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page='/login.jsp'/>
<session-management>
<concurrency-control max-sessions="1" />
</session-management>
<openid-login>
<attribute-exchange>
<openid-attribute name="email" type="http://axschema.org/contact/email" required="true" />
<openid-attribute name="name" type="http://axschema.org/namePerson" />
</attribute-exchange>
</openid-login>
</http>
<authentication-manager>
<authentication-provider user-service-ref='myUserDetailsService'/>
</authentication-manager>
<beans:bean id="myUserDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
</beans:beans>
These two configuration files are semantically equivalent (it is just a different way to encode the same information). But the latter is much more readable. Just use default namespace whichever namespace is used the most.
It depends on XML namespace configuration and is not Spring feature, makes no difference for the code, make some difference to coder, and in fact but effect of defining xmlns attribute on root xml element. Read more about <beans:bean> in this chapter of Spring Security (which isn't in fact the same as Spring Framework, but is very common and uses its own namespace in XML files). You can write:
<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.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="expressionHandler"/>
</global-method-security>
<beans:bean id="expressionHandler" class=
"org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<beans:property name="permissionEvaluator" ref="myPermissionEvaluator"/>
</beans:bean>
</beans:beans>
which is equivalent to:
<beans xmlns="http://www.springframework.org/schema/beans" <!-- see the difference here? -->
xmlns:security="http://www.springframework.org/schema/security"
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.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>
<bean id="expressionHandler" class=
"org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator" ref="myPermissionEvaluator"/>
</bean>
</beans>
Personally I always use beans as main namespace, but it's matter of habit.

Spring security UsernamePasswordAuthenticationFilter with remember-me

I want to add the remember-me into my login page, by reading here , it needs a UserDetailsService. But my UserDetailsService is not getting called, can anyone point where i am wrong? Thanks.
The 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"
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/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- configure Spring-Security
auto-config is false.
use-expressions is true: see http://static.springsource.org/spring-security/site/docs/3.1.x/reference/el-access.html
access-denied-page: which page is redirected when login is denied
entry-point-ref: This attribute allows this behaviour to be overridden by defining a customized
AuthenticationEntryPoint bean which will start the authentication process
-->
<security:http auto-config="false" use-expressions="true" entry-point-ref="authenticationEntryPoint" >
<!-- define how to handle the url /auth/login, primitAll is used since we defined use-expressions=true -->
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/search" access="hasRole('ROLE_USER')"/>
<!-- The logout element adds support for logging out by navigating to a particular URL.
The default logout URL is /j_spring_security_logout,
but you can set it to something else using the logout-url attribute -->
<security:logout
invalidate-session="true"
logout-success-url="/login" />
<security:custom-filter ref="blacklistFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
</security:http>
<!-- Custom filter to deny unwanted users even though registered -->
<bean id="blacklistFilter" class="com.myapp.filter.BlacklistFilter" />
<!-- Custom filter for username and password. we need to create another 4 beans -->
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:rememberMeServices-ref="rememberMeServices"
p:authenticationManager-ref="customAuthenticationManager"
p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler" />
<!-- Bean 1: Custom authentication manager. -->
<bean id="customAuthenticationManager" class="com.myapp.manager.CustomAuthenticationManager" />
<!-- bean 2: set the default failure url here -->
<bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
p:defaultFailureUrl="/login?error=true" />
<!-- bean 3: set the default target url here -->
<bean id="customAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler"
p:defaultTargetUrl="/search" />
<!-- bean 4: remember me -->
<bean id="rememberMeServices"
class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="key" value="myapp"/>
</bean>
<bean id="userDetailsService" class="com.myapp.service.UserDetailsServiceImpl" />
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login"/>
<security:authentication-manager/></beans>
Thanks, Ralph
I added the filter, but UserDetailsServiceImpl is still not called, there is a stop point.
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {
logger.info("User details service is called");
return null;
}
and now the configuration 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:security="http://www.springframework.org/schema/security"
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/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- configure Spring-Security
auto-config is false.
use-expressions is true: see http://static.springsource.org/spring-security/site/docs/3.1.x/reference/el-access.html
access-denied-page: which page is redirected when login is denied
entry-point-ref: This attribute allows this behaviour to be overridden by defining a customized
AuthenticationEntryPoint bean which will start the authentication process
-->
<security:http auto-config="false" use-expressions="true" entry-point-ref="authenticationEntryPoint" >
<!-- define how to handle the url /auth/login, primitAll is used since we defined use-expressions=true -->
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/search" access="hasRole('ROLE_USER')"/>
<!-- The logout element adds support for logging out by navigating to a particular URL.
The default logout URL is /j_spring_security_logout,
but you can set it to something else using the logout-url attribute -->
<security:logout
invalidate-session="true"
logout-success-url="/login" />
<security:custom-filter ref="blacklistFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
<security:custom-filter ref="rememberMeFilter" position="REMEMBER_ME_FILTER"/>
</security:http>
<!-- Custom filter to deny unwanted users even though registered -->
<bean id="blacklistFilter" class="com.myapp.filter.BlacklistFilter" />
<!-- Custom filter for username and password. we need to create another 4 beans -->
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:rememberMeServices-ref="rememberMeServices"
p:authenticationManager-ref="customAuthenticationManager"
p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler" />
<!-- Bean 1: Custom authentication manager. -->
<bean id="customAuthenticationManager" class="com.myapp.manager.CustomAuthenticationManager" />
<!-- bean 2: set the default failure url here -->
<bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
p:defaultFailureUrl="/login?error=true" />
<!-- bean 3: set the default target url here -->
<bean id="customAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler"
p:defaultTargetUrl="/search" />
<!-- bean 4: remember me -->
<bean id="rememberMeServices"
class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="key" value="myapp"/>
</bean>
<bean id="userDetailsService" class="com.myapp.service.UserDetailsServiceImpl" />
<bean id="rememberMeFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="customAuthenticationManager" />
</bean>
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login"/>
<security:authentication-manager alias="theAuthenticationManager"/></beans>
It looks like you forgot to add the RememberMeAuthenticationFilter. -- Have a look at the example in the doc you mentioned, you will see what I mean.
Try to reduce your configuration first, to the very default configuration like in http://www.i-develop.be/blog/2010/02/04/spring-security-remember-me/

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