First of all! Thank you for reading my question.
I have a problem with retreiving all my principal objects. I use Spring version 3.2.1.RELEASE and spring security 3.1.3.RELEASE.
I did my research on the net and I found how to retrieve the principals, but after inserting my own authentication code it doesnt work anymore. Methode to retrieve all principals objects:
#RequestMapping("/loggedinusers")
public String viewAllLoggedInUsers(Model model) {
List<Object> principals = sessionRegistry.getAllPrincipals();
model.addAttribute("size", principals.size());
List<Integer> listOfUserIds = new ArrayList<Integer>();
for (Object principal : principals) {
if (principal instanceof Principal) {
listOfUserIds.add(((Principal) principal).getId());
}
}
return "/logged_in_users";
}
The above code was working before I changed some security configuration. Here is all my configuration:
<!-- bean namespave -->
<security:global-method-security jsr250-annotations="enabled" pre-post-annotations="enabled" secured-annotations="enabled" />
<security:http use-expressions="true" entry-point-ref="loginEntryPoint">
<security:intercept-url pattern="/login" access="permitAll()" />
<!-- ******* Filters ******* -->
<security:custom-filter ref="ipFormLoginFilter" position="FORM_LOGIN_FILTER"/>
<security:logout
delete-cookies="JSESSIONID"
logout-url="/logout"
logout-success-url="/login"
/>
<security:session-management session-fixation-protection="newSession">
<security:concurrency-control session-registry-alias="sessionRegistry" max-sessions="5" error-if-maximum-exceeded="false" />
</security:session-management>
</security:http>
<bean id="loginEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg value="/login"/>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="customUserAuthenticationProvider" />
</security:authentication-manager>
<bean id="ipFormLoginFilter" class="nl.irp.vadp.security.CustomIpUsernamePasswordAuthenticationFilter">
<property name="filterProcessesUrl" value="/authlogin"/>
<property name="authenticationManager" ref="authenticationManager"/>
<property name="usernameParameter" value="username"/>
<property name="passwordParameter" value="password"/>
<property name="authenticationSuccessHandler">
<bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/"/>
</bean>
</property>
<property name="authenticationFailureHandler">
<bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login?login_error=true"/>
</bean>
</property>
</bean>
<bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
<constructor-arg value="512" />
</bean>
</beans>
Code::
Filter class
public final class CustomIpUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (request.getMethod().equals("POST")) {
String username = obtainUsername(request);
String password = obtainPassword(request);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
}
Code:: Custom Authentication class
#Component
public class CustomUserAuthenticationProvider implements AuthenticationProvider {
#Autowired
UserService userService;
#Autowired
ShaPasswordEncoder shaPasswordEncoder;
public CustomUserAuthenticationProvider() {
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final String BAD_CREDENTIALS = "test";
final String BAD_IP_ADDRESS = "test";
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
String email = token.getName();
User user = null;
if (email != null) {
user = userService.findUserByEmail(email);
}
if (user == null) {
throw new UsernameNotFoundException(BAD_CREDENTIALS + "no user found");
}
String password = user.getPassword();
String salt = user.getName();
if (!shaPasswordEncoder.isPasswordValid(password, (String) token.getCredentials(), salt)) {
throw new BadCredentialsException(BAD_CREDENTIALS + "bad password");
}
if (!user.hasIpaddress(request.getRemoteAddr())) {
throw new BadCredentialsException(BAD_IP_ADDRESS + "bad ip adress");
}
authorities.add(new SimpleGrantedAuthority("ROLE_" + user.getRole().getName().toUpperCase()));
Principal principal = new Principal(user.getEmail(), user.getPassword(), authorities, user.getId());
return new UsernamePasswordAuthenticationToken(principal, user.getPassword());
}
#Override
public boolean supports(Class<?> authentication) {
return CustomIpUsernamePasswordAuthenticationToken.class.equals(authentication);
}
}
The following listeners are added:
<!-- Listeners -->
<listener><!-- Starts up the webapp project -->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener><!-- spring security listener -->
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<!-- extra toegevoegd voor die ip ... -->
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
As the above code describes, I made my own AuthenticationProvider with an authenticate methode which authenticates the inserted data. This works perfectly (component scan is also done). Authorities in jsp ( for example) seems to work also. I seem not to understand why I cant get the registered principals.
edit:
I removed the "auto-config=true" en the tag before inserting additional information.
Hope someone can help me out.
EDIT 2:
I found out where the problem was. In my own custom filter, there is a property called:sessionAuthenticationStrategy. This field needs to be set.
I inserted the following in my filter and it works:
<property name="sessionAuthenticationStrategy" ref="sessionFixationProtectionStrategy" />
<bean id="sessionFixationProtectionStrategy" class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy">
Gtrz,
Related
For Spring security setup in Spring Boot. The LDAP Authentication provider is configured by default to use BindAuthenticator class.
This Class contains method
/**
* Allows subclasses to inspect the exception thrown by an attempt to bind with a
* particular DN. The default implementation just reports the failure to the debug
* logger.
*/
protected void handleBindException(String userDn, String username, Throwable cause) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to bind as " + userDn + ": " + cause);
}
}
This Method is to handle the authentication related Exceptions like invalid credentials.
I want to over-ride this method so i can handle this issue and return proper error message on the basis of error codes returned by LDAP. like invalid password or the account is locked.
Current LDAP implementation always returns "Bad Credentials" that does not give the right picture that why my credentials are invalid. i want to cover the cases
where the account is Locked
password is expired so i can redirect to change password
account locked due to number of invalid password retries
Please help
The issue i fixed by defining the LDAP context instead of using the Spring Boot LDAPAuthenticationProviderConfigurer.
Then created the FilterBasedLdapUserSearch and Over-written the BindAuthentication with my ConnectBindAuthenticator.
i created a separate LDAPConfiguration class for spring boot configuration and registered all these custom objects as Beans.
From the above Objects i created LDAPAuthenticationProvider by passing my Custom Objects to constructor
The Config is as below
#Bean
public DefaultSpringSecurityContextSource contextSource() {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(env.getProperty("ldap.url"));
contextSource.setBase(env.getProperty("ldap.base"));
contextSource.setUserDn(env.getProperty("ldap.managerDn"));
contextSource.setPassword(env.getProperty("ldap.managerPassword"));
return contextSource;
}
#Bean
public ConnectBindAuthenticator bindAuthenticator() {
ConnectBindAuthenticator connectBindAuthenticator = new ConnectBindAuthenticator(contextSource());
connectBindAuthenticator.setUserSearch(ldapUserSearch());
connectBindAuthenticator.setUserDnPatterns(new String[]{env.getProperty("ldap.managerDn")});
return connectBindAuthenticator;
}
#Bean
public LdapUserSearch ldapUserSearch() {
return new FilterBasedLdapUserSearch("", env.getProperty("ldap.userSearchFilter"), contextSource());
}
You have to change your spring security configuration to add your extension of BindAuthenticator:
CustomBindAuthenticator.java
public class CustomBindAuthenticator extends BindAuthenticator {
public CustomBindAuthenticator(BaseLdapPathContextSource contextSource) {
super(contextSource);
}
#Override
protected void handleBindException(String userDn, String username, Throwable cause) {
// TODO: Include here the logic of your custom BindAuthenticator
if (somethingHappens()) {
throw new MyCustomException("Custom error message");
}
super.handleBindException(userDn, username, cause);
}
}
spring-security.xml
<beans:bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg value="LDAP_URL" />
<beans:property name="userDn" value="USER_DN" />
<beans:property name="password" value="PASSWORD" />
</beans:bean>
<beans:bean id="userSearch"
class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<beans:constructor-arg index="0" value="USER_SEARCH_BASE" />
<beans:constructor-arg index="1" value="USER_SEARCH_FILTER" />
<beans:constructor-arg index="2" ref="contextSource" />
</beans:bean>
<beans:bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<beans:constructor-arg>
<beans:bean class="com.your.project.CustomBindAuthenticator">
<beans:constructor-arg ref="contextSource" />
<beans:property name="userSearch" ref="userSearch" />
</beans:bean>
</beans:constructor-arg>
</beans:bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="ldapAuthProvider" />
</security:authentication-manager>
Hope it's helpful.
I am trying to to authenticate user with spring-security with SHA hashing and salting. I have added extra field in users table for extra salt and customize UserDetails with this salt but whenever I try to login it is throwing bad credential exception.
My CustomJdbcDaoImpl class is :
public class CustomJdbcDaoImpl extends JdbcDaoImpl implements IChangePassword {
private Logger logger = LoggerFactory.getLogger(CustomJdbcDaoImpl.class);
#Override
protected UserDetails createUserDetails(String username,UserDetails userFromUserQuery,
List<GrantedAuthority> combinedAuthorities){
String returnUsername = userFromUserQuery.getUsername();
if(!isUsernameBasedPrimaryKey()){
returnUsername = username;
}
logger.info("inside #class CustomJdbcDaoImpl #method createUserDetails USER DETAILS ARE: "+userFromUserQuery.getPassword()+"authritieds: "+combinedAuthorities);
return new SaltedUser(returnUsername,
userFromUserQuery.getPassword(),
userFromUserQuery.isEnabled(),
true,
true,
true,
combinedAuthorities,
((SaltedUser)userFromUserQuery).getSalt());
}
#Override
protected List<UserDetails> loadUsersByUsername(String username) {
return getJdbcTemplate()
.query(getUsersByUsernameQuery(),
new String[] {username},
new RowMapper<UserDetails>() {
public SaltedUser mapRow(ResultSet rs, int rowNum) throws SQLException {
String username = rs.getString(1);
String password = rs.getString(2);
boolean enabled = rs.getBoolean(3);
String salt = rs.getString(4);
SaltedUser saltedUser = new SaltedUser(username, password, enabled,
true,
true,
true,
AuthorityUtils.NO_AUTHORITIES,
salt);
logger.info("inside #class #method loadUsersByUsername salted password is: "+saltedUser.getPassword());
return saltedUser;
}
});
}
#Override
public void changePassword(String username, String password) {
getJdbcTemplate().
update("update users set password = ? where username = ?",password,username);
}
}
for changing password each time with salt my DatabasePasswordSecurerBean class is
public class DatabasePasswordSecurerBean extends JdbcDaoSupport {
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private SaltSource saltSource;
#Autowired
private UserDetailsService userDetailsService;
private Logger logger = LoggerFactory.getLogger(DatabasePasswordSecurerBean.class);
public void secureDatabase(){
logger.info("inside #class DatabasePasswordSecurerBean #method secureDatabase entry...");
getJdbcTemplate().query("select username,password from users",new RowCallbackHandler(){
#Override
public void processRow(ResultSet rs) throws SQLException {
String username = rs.getString(1);
String password = rs.getString(2);
UserDetails user = userDetailsService.loadUserByUsername(username);
String encodedPassword = passwordEncoder.encodePassword(password,saltSource.getSalt(user));
getJdbcTemplate().update("update users set password = ? where username = ?",
encodedPassword,username);
logger.info("#class DatabasePasswordSecurerBean #method secureDatabase updating password for user: "+username + "to: "+encodedPassword);
}
});
}
}
security.xml configurations are
<http auto-config="true">
<intercept-url pattern="/*" access="ROLE_USER" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="jdbcUserService">
<password-encoder ref="passwordEncoder" >
<salt-source ref="saltSource" />
</password-encoder>
</authentication-provider>
</authentication-manager>
</beans:beans>
and my application.xml is
<!-- Simple implementation of the standard JDBC DataSource interface,
configuring the plain old JDBC DriverManager via bean properties -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${db.driverClassName}" />
<property name="url" value="${db.connection.url}" />
<property name="username" value="${db.connection.username}" />
<property name="password" value="${db.connection.password}" />
</bean>
<bean id="jdbcUserService" class="com.petCart.springsecurity.security.CustomJdbcDaoImpl">
<property name="dataSource" ref="dataSource" />
<property name="enableGroups" value="true"></property>
<property name="enableAuthorities" value="false"></property>
<property name="usersByUsernameQuery">
<value>
select username,password,enabled,salt from users where username = ?
</value>
</property>
<property name="groupAuthoritiesByUsernameQuery">
<value>
select r.roleid,r.role_name,p.permissionname from roles r
join userrole ur on ur.roleid = r.roleid
join users u on u.id = ur.userid
join rolepermission rp on r.roleid = rp.roleid
join permissions p on p.permissionid = rp.permissionid
where u.username = ?
</value>
</property>
</bean>
<!-- password encoder -->
<bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" id="passwordEncoder"/>
<bean class="com.petCart.springsecurity.security.DatabasePasswordSecurerBean" init-method="secureDatabase" depends-on="dataSource">
<property name="dataSource" ref="dataSource" />
</bean>
<bean class="org.springframework.security.authentication.dao.ReflectionSaltSource" id="saltSource">
<property name="userPropertyToUse" value="username" />
</bean>
Try to change from
<bean class="org.springframework.security.authentication.dao.ReflectionSaltSource" id="saltSource">
<property name="userPropertyToUse" value="username" />
</bean>
to
<bean class="org.springframework.security.authentication.dao.ReflectionSaltSource" id="saltSource">
<property name="userPropertyToUse" value="salt" />
</bean>
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 use spring security, spring, hibernate and jsf
authentication work correctly but it always redirects me to the page home.jsf
I want to manage the access of users after authentication
I want to manage the access of users after authentication
if authority = ROLE_ADMIN redirect ves homeadmin.jsf
if authority = ROLE_RH redirect ves homerh.jsf
if authority = ROLE_EXCUTIVE redirect ves homeex.jsf
if authority = ROLE_MANAGER redirect ves homem.jsf
if authority = ROLE_GP redirect ves homegp.jsf
The autority field in the Collaborateur table
the Colaborateur Class is
private Integer idColaborateur;
private Rolecol rolecol;
private String matriculeColaborateur;
private String nomColaborateur;
private String prenomColaborateur;
private String mailColaborateur;
private String pwdColaboratuer;
private String loginColaborateur;
private String adresseColaborateur;
private Boolean flgSuspendu;
private Set<HistoriqueNoteObjctif> historiqueNoteObjctifs = new HashSet<HistoriqueNoteObjctif>(
0);
private Set<Note> notes = new HashSet<Note>(0);
private Set<NoteObjectifs> noteObjectifses = new HashSet<NoteObjectifs>(0);
private Set<CompagneDevaluation> compagneDevaluations = new HashSet<CompagneDevaluation>(
0);
private Set<ColaborateurHierarchique> colaborateurHierarchiques = new HashSet<ColaborateurHierarchique>(
0);
private String authority;
//getter and seter
Datasource configuration is in the file applicationContext.xml
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root" />
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/modulevsql" />
<property name="password" value="root" />
<property name="maxStatementsPerConnection" value="0" />
<property name="maxAdministrativeTaskTime" value="0" />
<property name="maxConnectionAge" value="0" />
<property name="maxIdleTime" value="0" />
<property name="maxIdleTimeExcessConnections" value="0" />
<property name="maxPoolSize" value="0" />
<property name="maxStatements" value="0" />
</bean>
the User Class is
public class User implements UserDetails {
private static final long serialVersionUID = 1L;
private String name;
private String password;
private Colaborateur user;
public void setUser(Colaborateur user) {
this.user = user;
}
public User(String name) {
FacesContext fc=FacesContext.getCurrentInstance();
UserBean userBean=(UserBean) fc.getApplication().createValueBinding("#{UserBean}").getValue(fc);
userBean.chargerUtilisateur(name);
user = userBean.getUtilisateur();
System.err.println("USERS >>> "+user);
PasswordSupport pswdSupport = new PasswordSupport();
if (user!=null){
System.out.println("User.getLogin() :"+user.getLoginColaborateur());
System.out.println("user.getPwd() :"+user.getPwdColaboratuer());
this.name=user.getMatriculeColaborateur();
this.password=user.getPwdColaboratuer();
System.err.println(pswdSupport.getMD5Hash("1"));
}
}
public Collection<GrantedAuthority> getAuthorities() {
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
System.out.println("GrantedAuthorityImpl 1");
System.out.println("GrantedAuthorityImpl 2");
System.out.println("GrantedAuthorityImpl 3");
System.out.println("GrantedAuthorityImpl 4");
grantedAuthorities.add(new GrantedAuthorityImpl("ROLE_VISITEUR"));
return grantedAuthorities;
}
//getter and setter
and this is applicationContext-security.xml file
<?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.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<global-method-security secured-annotations="enabled">
</global-method-security>
<http pattern="/modules/members/**" access-denied-page="/modules/members/accessDenied.jsf" authentication-manager-ref="MembersAuthenticationManager">
<intercept-url pattern="/modules/members/secure/**" access="ROLE_VISITEUR" />
<intercept-url pattern="/modules/members/secure/homeadmin.jsf" access="ROLE_ADMIN" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<form-login login-page="/modules/members/login.jsf"
default-target-url="/modules/members/secure/home.jsf"
login-processing-url="/modules/members/j_spring_security_check"
authentication-failure-url="/modules/members/login.jsf" />
<logout logout-url="/modules/members/secure/logout"
logout-success-url="/modules/members/login.jsf" delete-cookies="true" />
</http>
<authentication-manager alias="MembersAuthenticationManager">
<authentication-provider user-service-ref="securityManager">
<password-encoder hash="md5" />
</authentication-provider>
</authentication-manager>
<beans:bean id="securityManager" class="tn.com.security.SecurityManager" />
</beans:beans>
Implement an AuthenticationSuccessHandler and redirect based on the collection of GrantedAuthority objects that's contained within the Authentication that you get passed in.
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException {
/* Redirect on the successful authentication of the user */
logger.info("Hit the AuthSuccessHandler");
String redirectAddress = null;
Collection<? extends GrantedAuthority> auths = authResult.getAuthorities();
if(auths.contains("ROLE_ADMIN"){
response.sendRedirect(response.encodeURL("homeadmin.jsf");
}
etc etc etc.
You could even add your roles to an Enum and write a switch statement to determine the redirect location.
Make sure you declare your AuthenticationSuccessHandler in your Security Config
<beans:bean id="customAuthenticationSuccessHandler" class="foo.bar.CustomAuthenticationSuccessHandler" />
<form-login login-page="/LoginView"
authentication-success-handler-ref="customAuthenticationSuccessHandler"
authentication-failure-url="/FailedLogin" />
The answer given by JamesENL is correct but with one mention:
You need to iterate over the collection of GrantedAuthority and only afterwards check for the ROLE:
Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();
for (GrantedAuthority grantedAuthority : authorities) {
if (grantedAuthority.getAuthority().equals("ROLE_USER")) {
response.sendRedirect("/userHome);
return;
} else if (grantedAuthority.getAuthority().equals("ROLE_ADMIN")) {
response.sendRedirect("/adminHome);
return;
}
}
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>