I can't seem to get a cookie of spring that remembers the session, nor is the persistent_logins table of the dataSource getting populated. Why isn't the cookie received by the client?
Application context xml file :
<?xml version="1.0" encoding="UTF-8"?>
<bean:beans>
<http>
...
<remember-me data-source-ref="dataSource"
user-service-ref="userService" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userService">
<password-encoder hash="md5" ref="passwordEncoder">
<salt-source ref="saltSource" />
</password-encoder>
</authentication-provider>
</authentication-manager>
...
</bean:beans>
login method:
#Service
public class AServiceImpl extends RemoteServiceServlet implements AService {
#Override
public boolean login(String username, String password, boolean remember) {
Collection<GrantedAuthority> auths = userDetailsService.getGrantedAuthorities(user);
auth = new UsernamePasswordAuthenticationToken(username, password, auths);
Authentication result = authenticationManager.authenticate(auth);
SecurityContextHolder.getContext().setAuthentication(result);
getThreadLocalRequest().getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,SecurityContextHolder.getContext());
rememberMeServices.loginSuccess(getThreadLocalRequest(),getThreadLocalResponse(), auth);
}
}
Related
I have an application that contains two fields : admin and candidate.
i have implemented spring security in the section candidate and it works but when i want to implement another security in the authentification for the admin, it doesn't work.
i have two authentification pages
there is my security code :
<security:http use-expressions="true" auto-config="true"
access-denied-page="/404.xhtml" >
<security:intercept-url access="hasAnyRole('candidat')"
pattern="/candidat.xhtml" />
<security:intercept-url access="hasAnyRole('candidat')"
pattern="/aproposdemoi.xhtml" />
<security:intercept-url access="hasAnyRole('candidat')"
pattern="/chargermoncv.xhtml" />
<security:intercept-url access="hasAnyRole('candidat')"
pattern="/completermonprofil.xhtml" />
<security:intercept-url access="hasAnyRole('candidat')"
pattern="/maphotodeprofil.xhtml" />
<security:intercept-url access="hasAnyRole('candidat')"
pattern="/mescompetences.xhtml" />
<security:intercept-url access="hasAnyRole('candidat')"
pattern="/mesexperiences.xhtml" />
<security:intercept-url access="hasAnyRole('candidat')"
pattern="/meslangues.xhtml" />
<security:intercept-url access="hasAnyRole('candidat')"
pattern="/moncompte.xhtml" />
<security:intercept-url access="hasAnyRole('candidat')"
pattern="/supprimercompte.xhtml" />
<security:form-login login-processing-url="/j_spring_security_check"
login-page="/carrieres?login_error=1" always-use-default-target="true"
default-target-url="/candidat.xhtml" />
<security:logout logout-success-url="/carrieres.xhtml" />
<security:remember-me key="uniqueAndSecret"/>
<security:form-login login-processing-url="/j_spring_security_check"
login-page="/adminzone?login_error=1" always-use-default-target="true"
default-target-url="/tableaudebord.xhtml" />
<security:logout logout-success-url="/adminzone.xhtml" />
<security:remember-me key="uniqueAndSecret"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:password-encoder hash="md5" />
<security:jdbc-user-service id="jdbcMemoryAP"
data-source-ref="dataSource"
users-by-username-query="
select email_candidat,mot_de_passe_candidat,enabled
from candidat where email_candidat=?"
authorities-by-username-query="
select u.email_candidat, ur.autorite from candidat u, role_candidat ur
where u.id_candidat = ur.candidat and u.email_candidat =? " />
</security:authentication-provider>
</security:authentication-manager>
<security:authentication-manager >
<security:authentication-provider>
<security:password-encoder hash="md5" />
<security:jdbc-user-service id="jdbcMemoryAP"
data-source-ref="dataSource"
users-by-username-query="
select email_admin,mot_de_passe,enabled
from administrateur where email_admin=?"
authorities-by-username-query="
select u.email_admin, ur.autorite from administrateur u, role_administrateur ur
where u.idAdmin = ur.administrateur and u.email_admin =? " />
</security:authentication-provider>
</security:authentication-manager>
So, Any time you have to login via two tables, both the Model classes of the tables should implement UserDetails. Per table, you will need one LoginService.
Security-applicationContext.xml :
// The 2 providers mentioned below are each for different datbase tables. Please //note, tables will be checked sequentially.
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="daoAuthenticationProvider"/>
<security:authentication-provider ref="hostAuthenticationProvider"/>
</security:authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="strengthInInteger you want, default 6" />
</beans:bean>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
//Below is the first LoginServiceImpl, a java bean I have declared in //applicationContext.xml, not here.
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
<beans:bean id="hostAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
//Below is the second Login implementation, a java bean I have declared in //applicationContext.xml, not here.
<beans:property name="userDetailsService" ref="HostLoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
Now,
// Notice that first LoginServiceImpl implements UserDetailsService. This file is responsible to check if there is any student with the given username in db. If yes, then we build a User object which Spring-security understands, for which I will paste code after this file.
#Transactional
#Service("userDetailsService")
public class LoginServiceImpl implements UserDetailsService{
#Autowired private StudentDAO studentDAO;
#Autowired private Assembler assembler;
private static final GrantedAuthority USER_AUTH = new SimpleGrantedAuthority("ROLE_STUDENT");
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,DataAccessException {
Student student = this.studentDAO.findStudentByUsername(username);
if(student == null) { throw new UsernameNotFoundException("Wrong username or password");}
return assembler.buildUserFromUserEntity(student);
}
}
Now, when user is found, then only we call this.
#Service("assembler")
public class Assembler {
#Transactional(readOnly = true)
User buildUserFromUserEntity(Student userEntity){
String username = userEntity.getUsername();
String password = userEntity.getPassword();
// Long id = userEntity.getId();
// boolean enabled = userEntity.isActive();
boolean enabled = true;
boolean accountNonExpired = userEntity.isAccountNonExpired();
boolean credentialsNonExpired = userEntity.isCredentialsNonExpired();
boolean accountNonLocked = userEntity.isAccountNonLocked();
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_STUDENT"));
User user = new User(username,password,enabled,accountNonExpired,credentialsNonExpired,accountNonLocked,authorities);
return user;
}
}
In a similar fashion, I have 2nd i.e HostLoginService :
#Transactional
#Service("hostuserDetailsService")
public class HostLoginService implements UserDetailsService{
#Autowired
private HostDAO hostDAO;
#Autowired
private HostAssembler assembler;
private static final GrantedAuthority USER_AUTH = new SimpleGrantedAuthority("ROLE_HOST");
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,DataAccessException {
Host host = this.hostDAO.findHostByUsername(username);
if(host == null) { throw new UsernameNotFoundException("Wrong username or password");}
return assembler.buildUserFromUserEntity(host);
}
}
For brevity, I am avoiding the assembler for it.
Now you can see the roles are different. For proper redirection, you have to crearte a controller method for the default-target-url of Spring-Security, check who is currently authenticated, admin or candidate by querying Spring-security. Then you can redirect.
If any doubt, let me know.
I am creating a custom authentication provider that authenticates user using a third party system. Username and password are being sent to server in json format. To implement that I have created a custom filter - UsernamePasswordAuthenticationFilter which is called at position FORM_LOGIN_FILTER. After this I created a custom authentication provider to authenticate user using a third party system. But, this authentication filter is being called with every request, which causes third party system to be called with every request. What I am doing wrong?
CustomUsernamePasswordAuthenticationFilter:
#Override
public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response)
{
//Get username password from request
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken( username, password);
setDetails(request, token);
return this.getAuthenticationManager().authenticate(token);
}
Custom Authentication Provider:
#Override
public Authentication authenticate(Authentication authentication) {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
boolean flag = //use the credentials to try to authenticate against the third party system
if(flag) {
return new UsernamePasswordAuthenticationToken(username, password);
}
else
throw new BadCredentialsException("Bad Credentials");
}
#Override
public boolean supports(Class<?> authentication) {
return true;
}
security-context.xml
<http pattern="/resources/**" security="none"/>
<http auto-config="false" use-expressions="true" access-denied-page="/welcome"
create-session="always" disable-url-rewriting="true" entry-point-ref="customEntryPoint">
<intercept-url pattern="/" access='permitAll'/>
<custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER" />
<intercept-url pattern="/**" access="isAuthenticated()" />
<logout logout-success-url="/" delete-cookies="JSESSIONID" logout-url="/logout" invalidate-session="true" />
</http>
<bean id="loginFilter" class="org.temp.secure.CustomUsernamePasswordAuthenticationFilter">
<beans:property name="requiresAuthenticationRequestMatcher" ref="loginRequestUrlHandler" />
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="usernameParameter" value="username" />
<beans:property name="passwordParameter" value="password" />
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="customAuthenticationProvider" />
</authentication-manager>
<bean id="loginRequestUrlHandler" class="org.springframework.security.web.util.matcher.RegexRequestMatcher">
<constructor-arg index="0" value="/login" />
<constructor-arg index="1" value="POST" />
<constructor-arg index="2" value="false" />
</bean>
<bean id="customEntryPoint" class="org.temp.secure.CustomEntryPoint" />
<bean id="customAuthenticationProvider" class="org.temp.secure.MyAuthenticationProvider"/>
Never mind, got it, problem was that I was not setting any roles, so it was showing authentication as false. After setting roles in UsernamePasswordAuthenticationToken, it does not call custom authentication provider any more..
#Override
public Authentication authenticate(Authentication authentication) {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
boolean flag = //use the credentials to try to authenticate against the third party system
if(flag) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_ONE"));
authorities.add(new SimpleGrantedAuthority("ROLE_TWO"));
return new UsernamePasswordAuthenticationToken(username, password, authorities);
}
else
throw new BadCredentialsException("Bad Credentials");
}
#Override
public boolean supports(Class<?> authentication) {
return true;
}
I am working with spring-security-oauth2 and I faced with such exception:
Caused by: java.io.NotSerializableException: com.samsung.diary.security.UserDetailsServiceImpl
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1180)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1528)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1493)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
In my config file i have:
<beans:bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<beans:constructor-arg ref="clientDetails" />
</beans:bean>
<authentication-manager id="clientAuthenticationManager" >
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailsService">
</authentication-provider>
</authentication-manager>
If I change it to:
<beans:bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<beans:constructor-arg ref="clientDetails" />
</beans:bean>
<authentication-manager id="clientAuthenticationManager" >
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="2" password="2" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
everething works perfect.
My UserDetailsService implementation:
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
#Qualifier("usersDAO")
private UsersDAO dao;
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
final com.samsung.diary.domain.User userEntity = dao.findByLogin(username);
if (userEntity == null)
throw new UsernameNotFoundException("user not found");
String login = userEntity.getLogin();
String password = userEntity.getPassword();
boolean enabled = userEntity.getEnabled().equals("true");
boolean accountNonExpired = enabled;
boolean credentialsNonExpired = enabled;
boolean accountNonLocked = enabled;
GrantedAuthority auth = new GrantedAuthority() {
private static final long serialVersionUID = 1L;
public String getAuthority() {
return userEntity.getAuthority();
}
};
Set<GrantedAuthority> set = new HashSet<GrantedAuthority>();
set.add(auth);
UserDetails details = new User(login, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked,
set);
return details;
}
}
As I understand there is something connected with Serializable interface but I can't figure out how to solve it.
EDIT: I made UserDetailsServiceImpl and UsersDAO Serializable and now it works. But I still don't understand where my UserDetailsServiceImpl is being written in a stream.
If someone give me a hint I would be very appreciated.
I'm using Spring 3.2.0 and the same version of Spring security. On successful login, a user is redirected to one of the protected pages as follows.
public final class LoginSuccessHandler implements AuthenticationSuccessHandler
{
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException
{
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_ADMIN"))
{
response.sendRedirect("admin_side/Home.htm");
return;
}
}
}
I'm using Hibernate. How can I update the login date-time (Last Login) in the database on successful login? I have a submit button on the login page whose POST request doesn't seem to map to a method in its corresponding login controller. The login form's action is actually mapped to the Servlet - j_spring_security_check.
The entire spring-security.xml file is as follows, if it is required.
<?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.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<http pattern="/Login.htm*" security="none"></http>
<http auto-config='true'>
<!--<remember-me key="myAppKey"/>-->
<session-management session-fixation-protection="newSession">
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
<intercept-url pattern="/admin_side/**" access="ROLE_ADMIN" requires-channel="any"/>
<form-login login-page="/" default-target-url="/admin_side/Home.htm" authentication-failure-url="/LoginFailed.htm" authentication-success-handler-ref="loginSuccessHandler"/>
<logout logout-success-url="/Login.htm" invalidate-session="true" delete-cookies="JSESSIONID"/>
</http>
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select email_id, password, enabled from user_table where lower(email_id)=lower(?)"
authorities-by-username-query="select ut.email_id, ur.authority from user_table ut, user_roles ur where ut.user_id=ur.user_id and lower(ut.email_id)=lower(?)"/>
</authentication-provider>
</authentication-manager>
<beans:bean id="loginSuccessHandler" class="loginsuccesshandler.LoginSuccessHandler"/>
<global-method-security>
<protect-pointcut expression="execution(* dao.*.*(..))" access="ROLE_ADMIN"/>
</global-method-security>
<!--<global-method-security secured-annotations="enabled" />-->
</beans:beans>
An other way is to register an handler for the AuthenticationSuccessEvent.
#Service
public class UserService implements
ApplicationListener<AuthenticationSuccessEvent> {
#Override
public void onApplicationEvent(AuthenticationSuccessEvent event) {
String userName = ((UserDetails) event.getAuthentication().
getPrincipal()).getUsername();
User user = this.userDao.findByLogin(userName);
user.setLastLoginDate(new Date());
}
}
Why do not do it in authentication success handler directly?
public final class LoginSuccessHandler implements AuthenticationSuccessHandler
{
#Autowired
private UserService userService;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException
{
String userName = authentication.getPrincipal().getName();
this.userService.updateLastLoginDateForUserByName(userName);
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_ADMIN"))
{
response.sendRedirect("admin_side/Home.htm");
return;
}
}
}
You could also subclass the spring AuthenticationProvider interface and inject it into the <authentication-manager /> element.
The class would be something like
public class AuthenticationProvider extends DaoAuthenticationProvider {
// inject whatever tou want
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
return super.authenticate(authentication);
// do what ever you want here
}
}
(supposing you're using DaoAuthenticationProvider)
Then you just need to register beans
<bean class="x.y.z.AuthenticationProvider" id="myAuthProvider" scope="singleton" />
<authentication-manager>
<authentication-provider ref="myAuthProvider">
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select email_id, password, enabled from user_table where lower(email_id)=lower(?)"
authorities-by-username-query="select ut.email_id, ur.authority from user_table ut, user_roles ur where ut.user_id=ur.user_id and lower(ut.email_id)=lower(?)"/>
</authentication-provider>
</authentication-manager>
(Don't trust code correctness, I wrote it on the fly. It's just meant to show my idea.)
Stefano
I am using spring MVC and want to check if user's trial period has expired.
I am getting user detail using spring security using the following method
public User getUserDetail() {
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
Object principal = auth.getPrincipal();
if(principal instanceof User){
User user = (User) principal;
return user;
}
return null;
}
User object contains the date when he logged in first.
I am checking the user subscription using following code
UserBean userLoggedIn = (UserBean) userService.getUserDetail();
Date dt = userLoggedIn.getUserCreationDate();
DateTime userCreated = new DateTime(dt).plusDays(TRIAL_PERIOD);
DateTime currentDateTime = new DateTime();
if(currentDateTime.compareTo(userCreated) > 0 && userLoggedIn.getPackageType() == 0){
return new ModelAndView("pricing","user",userLoggedIn);
}
Now my problem is I don't want to write the above code repeatedly in each controller. So is there any common place where I can check the user trial period expire or not and redirect him to pricing page.
I have CustomUserDetail class where I am accessing user details from database and put it in spring security session. So I think this should be the best place to check if users trial period is expire or not but I don't know how I can redirect user from this class to pricing page.
My CustomUserDetail class is
#Service
#Transactional(readOnly = true)
public class CustomUserDetailsService implements UserDetailsService {
static final Logger logger = Logger.getLogger(CustomUserDetailsService.class);
#Resource(name="userService")
private UserService userService;
/* (non-Javadoc)
* #see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
*/
#Override
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException, DataAccessException {
try {
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
UserBean domainUser = userService.getUserByName(email);
domainUser.isEnabled();
domainUser.isAccountNonExpired();
domainUser.isCredentialsNonExpired();
domainUser.isAccountNonLocked();
//Collection<? extends GrantedAuthority> roles = getAuthorities((long) domainUser.getRoleId());
return domainUser;
} catch (Exception e) {
logger.error("Invalid Login.",e);
throw new RuntimeException(e);
}
}
---updated---
My spring-security.xml is
<form-login login-page="/login.htm"
authentication-failure-url="/loginfailed.htm"
authentication-failure-handler-ref="exceptionMapper"
default-target-url="/index.htm"
always-use-default-target="true"/>
<access-denied-handler error-page="/logout.htm"/>
<logout invalidate-session="true"
logout-url="/logout.htm"
success-handler-ref="userController"/>
<remember-me user-service-ref="customUserDetailsService" key="89dqj219dn910lsAc12" use-secure-cookie="true" token-validity-seconds="466560000"/>
<session-management session-authentication-strategy-ref="sas"/>
</http>
<authentication-manager>
<authentication-provider user-service-ref="customUserDetailsService">
<password-encoder ref="customEnocdePassword" >
<salt-source user-property="email"/>
</password-encoder>
</authentication-provider>
</authentication-manager>
<beans:bean id="customEnocdePassword" class="com.mycom.myproj.utility.CustomEnocdePassword" />
<beans:bean id="exceptionMapper" class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler" >
<beans:property name="exceptionMappings">
<beans:map>
<beans:entry key="your.package.TrialPeriodExpiredException" value="/pricing"/>
</beans:map>
</beans:property>
</beans:bean>
<beans:bean id="sas"
class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="maximumSessions" value="3" />
---update----
Now what I did is
<beans:bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="customUserDetailsService"/>
<beans:property name="passwordEncoder" ref="customEnocdePassword"/>
<beans:property name="preAuthenticationChecks" ref="expirationChecker"/>
</beans:bean>
<authentication-manager>
<authentication-provider user-service-ref="authenticationProvider">
<password-encoder ref="customEnocdePassword" >
<salt-source user-property="email"/>
</password-encoder>
</authentication-provider>
</authentication-manager>
<!-- <authentication-manager>
<authentication-provider user-service-ref="customUserDetailsService">
<password-encoder ref="customEnocdePassword" >
<salt-source user-property="email"/>
</password-encoder>
</authentication-provider>
</authentication-manager> -->
<beans:bean id="expirationChecker" class="com.mycom.myproj.utility.UserTrialPeriodExpirationChecker" />
<beans:bean id="customEnocdePassword" class="com.mycom.myproj.utility.CustomEnocdePassword" />
now I am getting below error
"Cannot convert value of type [org.springframework.security.authentication.dao.DaoAuthenticationProvider]
to required type [org.springframework.security.core.userdetails.UserDetailsService]
for property 'userDetailsService': no matching editors or conversion strategy found"
You could set a custom UserDetailsChecker on the DaoAuthenticationProvider that verifies the expiration date before authenticating the user.
The <authentication-provider> element in your config generates a DaoAuthenticationProvider, but there is no attribute on that element that would allow you to set its preAuthenticationChecks property. In order to work around this limitation of the namespace configuration, you will have to fall back to defining that provider as a normal bean:
<bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="customUserDetailsService"/>
<property name="passwordEncoder" ref="customEnocdePassword"/>
<property name="preAuthenticationChecks" ref="expirationChecker"/>
</bean>
and refer to it by the id in the <authentication-manager> config:
<security:authentication-manager>
<security:authentication-provider ref="authenticationProvider"/>
</security:authentication-manager>
The above referenced expirationChecker bean must implement UserDetailsChecker which is a call-back interface receiving the UserDetails object, where you could throw a specific exception if the user's trial period has expired:
public class UserTrialPeriodExpirationChecker implements UserDetailsChecker {
#Override
public void check(UserDetails user) {
if( /* whatever way you check expiration */ ) {
throw new TrialPeriodExpiredException();
}
if (!user.isAccountNonLocked()) {
throw new LockedException("User account is locked");
}
if (!user.isEnabled()) {
throw new DisabledException("User is disabled");
}
if (!user.isAccountNonExpired()) {
throw new AccountExpiredException("User account has expired");
}
}
}
Note that the last three checks are not related to the expiration checking, but you have to have them here, as the default implementation (which is AbstractUserDetailsAuthenticationProvider.DefaultPreAuthenticationChecks) is now overridden by this class. Since the default implementation is a private inner class, you cannot simply extend it, but need to copy the code from there to prevent locked/disabled/etc. users from logging in.
Once you have all that in place, configure an ExceptionMappingAuthenticationFailureHandler that maps your TrialPeriodExpiredException to the URL of the pricing page, where the user should land.
<form-login authentication-failure-handler-ref="exceptionMapper" ... />
...
<bean id="exceptionMapper" class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler" >
<property name="exceptionMappings">
<map>
<entry key="your.package.TrialPeriodExpiredException" value="/pricing"/>
</map>
</property>
</bean>