Custom Authentication Provider get calls with every request - spring

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;
}

Related

Multiple authentication with spring security

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.

BCryptPasswordEncoder in Spring Security with JSF 2.0 custom login page - Login with encrypted password saved in DB

I have an Application with JSF 2.0 and Spring 3.1.X with Spring Security.
This is what I have done so far, what I want is to create a Custom Spring Authentication with an encrypted password save in the mysql database. I wanted to use BCryptPasswordEncoder to encrypt my password.
This is what I have in my web.xml
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
This is what I have in my spring Security configuration file which is security-context.xml
<sec:global-method-security pre-post-annotations="enabled" />
<sec:http auto-config="true" use-expressions="true" access-denied-page="/pages/unsecure/accessDenied.xhtml">
<sec:intercept-url pattern="/pages/secure/**" access="hasRole('ROLE_USER')" />
<sec:intercept-url pattern="/ccrxhtml/**" access="hasRole('ROLE_USER')" />
<sec:intercept-url pattern="/pages/unsecure" access="permitAll"/>
<sec:intercept-url pattern="/resources/bootstrap/js/plugins/ckeditor/plugins/link/images/hidpi" access="hasRole('ROLE_MODERATOR')"/>
<sec:intercept-url pattern="/javax.faces.resource/**" access="permitAll"/>
<sec:form-login login-page="/pages/unsecure/loginForm.xhtml"
authentication-failure-url="/pages/unsecure/loginForm.xhtml?login_error=t"/>
<sec:logout invalidate-session="true" delete-cookies="true" logout-success-url="/"></sec:logout>
</sec:http>
<beans:bean id="customUserDetailsService" class="com.xgen.ccr.service.impl.CustomUserDetailsService"/>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="customUserDetailsService">
<sec:password-encoder ref="encoder"></sec:password-encoder>
</sec:authentication-provider>
</sec:authentication-manager>
<beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="10" />
</beans:bean>
<beans:bean id="authProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="customUserDetailsService" />
<beans:property name="passwordEncoder" ref="encoder" />
</beans:bean>
This is the login form, which is loginForm.xhtml
<h:form id="loginForm">
<div class="login_field">
<h:graphicImage library="images" name="connect121-1.jpg" styleClass="login_image" />
<p:growl id="msg" showDetail="true" life="3000" />
<p:panel header="Login" style="width: 100%;">
<h:panelGrid id="loginPanel" columns="2">
<h:outputText value="Username" />
<p:inputText id="j_username" value="#{loginMgmtBean.userName}"
required="true" requiredMessage="Please enter a UserName"></p:inputText>
<p:spacer></p:spacer>
<p:message for="j_username" ></p:message>
<h:outputText value="Password" />
<p:password id="j_password" value="#{loginMgmtBean.password}" feedback="false"
required="true" requiredMessage="Please enter a password"></p:password>
<p:spacer></p:spacer>
<p:message for="j_password"></p:message>
</h:panelGrid>
<h:commandButton value="Sign Me In"
action="#{loginMgmtBean.login}"
update="loginForm"
styleClass="button_login"
ajax="true"/>
This is my LoginMgtManageBean.java the login mathod which implements UserDetailsService interface.
public String login()
{
try
{
boolean userExists = userService.login(userName, password);
if (userExists)
{
user = userService.findByUserName(userName);
getCurrentHttpSession().setAttribute("userName", userName);
}
Authentication request = new UsernamePasswordAuthenticationToken(this.getUserName(), this.getPassword());
//authenticationManager is defined in line number 46 to 50
Authentication result = authenticationManager.authenticate(request);//Will return a validated authentication object
SecurityContextHolder.getContext().setAuthentication(result);
}
catch (AuthenticationServiceException e)
{
FacesContext.getCurrentInstance().addMessage("test",new FacesMessage(FacesMessage.SEVERITY_FATAL,
"Invalid Login - User not found!","Please Try Again!"));
return "incorrect";
}
return "correct";
}
This is the CustomUserDetails which I am using,
#Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
com.xgen.ccr.entity.User domainUser = userService.findByUserName(userName);
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
if (domainUser == null) {
return null;
}
User userToReturn = new User(domainUser.getUserName(), domainUser.getPassword(), enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked, getAuthorities(domainUser.getRole().getId()));
return userToReturn;
}
Just want to know the what I am missing ? Can any direct me to a accurate guide for this
Well, the problem I have is I inserted a user in to my mysql database with an encrypted password with the expectation that I will be able to login with that user
User user = new User();
Role roleById = roleService.findRoleById(1l);
user.setFirstName("Ajith");
user.setUserName("ranga");
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String hashedPassword = passwordEncoder.encode("333333");
user.setPassword(hashedPassword);
user.setRole(roleById);
userService.saveUser(user);
, yet the application simply does not authenticate the user with username/password ranga/333333
These are my tables
User(userid,username,password,firstName)
Role(roleid,role_name,description)
user_role(roleid,userid) composite primary key
I have the three tables alltogether, User, Role, and user_role which have the userid and the roldid init. This is my role table
CREATE TABLE `USER_ROLES` (
`role_id` bigint(20) NOT NULL DEFAULT '0',
`user_id` bigint(20) NOT NULL,
PRIMARY KEY (`role_id`,`user_id`),
UNIQUE KEY `UK_690fxih6hbwaxrgl1rlfs37ko` (`user_id`),
KEY `FK_2pe93gfsv15g0fdrcfpv4hapu` (`role_id`),
KEY `FK_690fxih6hbwaxrgl1rlfs37ko` (`user_id`),
CONSTRAINT `FK_2pe93gfsv15g0fdrcfpv4hapu` FOREIGN KEY (`role_id`) REFERENCES `Roles` (`id`),
CONSTRAINT `FK_690fxih6hbwaxrgl1rlfs37ko` FOREIGN KEY (`user_id`) REFERENCES `Users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$
This was fixed using the following encryption mechanism in Spring security
In Spring-security.xml
<beans:bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="12"/>
</beans:bean>
<!-- Set customUserDetailsService class as the authentication Manager for Spring Security-->
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="customUserDetailsService" >
<sec:password-encoder ref="bCryptPasswordEncoder"></sec:password-encoder>
</sec:authentication-provider>
</sec:authentication-manager>
Also in the CustomUserDetailsService.java you just need to override the loadUserByuserName
#Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
com.xgen.ccr.entity.User domainUser = userService.findByUserName(userName);
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
if (domainUser == null) {
return null;
}
User userToReturn = new User(domainUser.getUserName(), domainUser.getPassword(), enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked, getAuthorities(domainUser.getRole().getId()));
return userToReturn;
}
Also when you are creating a user please use the same strength
User user = new User();
Role roleById = roleService.findRoleById(1l);
user.setFirstName("chandana1");
user.setUserName("chandana1");
user.setEnabled(Boolean.TRUE);
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(12);
String hashedPassword = passwordEncoder.encode("333333");
boolean matches = passwordEncoder.matches("333333", hashedPassword);
System.out.println(matches);
user.setPassword(hashedPassword);
finally set the prependId to false in the login form
<h:form id="loginForm" prependId="false">
This will not append the form id to the j_username and j_password.
Further, do the necessary navigation as above.
Hope this would help someone who wants to use
1. Custom JSF login page
2. Use spring security with password encryption/hashing using BcryptPasswordEncoder
3. Want to load the password from the database(Hibernate as the ORM)

How redirect based on role after authentication with spring security

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;
}
}

check if user subscription for trial period is expire or not using spring MVC

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>

Spring security - Get all logged in principals

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,

Resources