I am developing a web application with spring mvc framework and java. I am using active directory user authentication in the application.
Now i want to assign role to these users from a sql database. So the whole authentication process will be done by taking information from two sources: [ user id & password from active directory & roles of user from sql database ].
I have searched on internet to get any tutorial/samples to do so but I'm unable to get any useful stuff. So any help on the issue will be appreciated.
Here is the current code of my spring security file,
<bean id="adAuthenticationProvider"
class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<constructor-arg value="abbl.org"/>
<constructor-arg value="LDAP://abbl.org"/>
<property name="convertSubErrorCodesToExceptions" value="true"/>
<property name="useAuthenticationRequestCredentials" value="true"/>
</bean>
<bean id="customAuthenticationProvider"
class="org.abbl.exhbp.templates.CustomAuthorityProvider">
<constructor-arg ref="adAuthenticationProvider"/>
</bean>
<security:authentication-manager>
<security:authentication-provider ref="customAuthenticationProvider"/>
</security:authentication-manager>
my authenticate function in AuthenticationProvider class,
public Authentication authenticate(Authentication authentication) {
final Authentication a = delegate.authenticate(authentication);
// Load additional authorities and create an Authentication object
final List<GrantedAuthority> authorities = getGrantedAuthorities(a.getName());
return new AbstractAuthenticationToken(authorities) {
#Override
public Object getCredentials() {
//return null; //To change body of implemented methods use File | Settings | File Templates.
throw new UnsupportedOperationException();
}
#Override
public Object getPrincipal() {
//return null; //To change body of implemented methods use File | Settings | File Templates.
return a.getPrincipal();
}
};
}
After login , i am getting this exception,
java.lang.UnsupportedOperationException
org.abbl.exhbp.templates.CustomAuthorityProvider$1.getCredentials(CustomAuthorityProvider.java:41)
org.springframework.security.authentication.AbstractAuthenticationToken.eraseCredentials(AbstractAuthenticationToken.java:108)
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:186)
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94)
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:195)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
What modification or implementation I need to do here to achieve what i mentioned above?
Finally i achieved what i aimed for by using UsernamePasswordAuthenticationToken instead of AbstractAuthenticationToken. Solution is given below,
1) spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http auto-config="true" use-expressions="true">
<security:anonymous enabled="false"/>
<security:form-login login-page="/login" default-target-url="/home"
login-processing-url="/j_spring_security_check"
authentication-failure-url="/loginfailed"/>
<security:logout logout-success-url="/logout"/>
</security:http>
<bean id="adAuthenticationProvider"
class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<constructor-arg value="DOMAIN"/>
<constructor-arg value="URL"/>
<property name="convertSubErrorCodesToExceptions" value="true"/>
<property name="useAuthenticationRequestCredentials" value="true"/>
</bean>
<bean id="customAuthenticationProvider"
class="org.abbl.exhbp.templates.CustomAuthorityProvider">
<constructor-arg ref="adAuthenticationProvider"/>
</bean>
<security:authentication-manager>
<security:authentication-provider ref="customAuthenticationProvider"/>
</security:authentication-manager>
2) CustomAuthorityProvider.class
public class CustomAuthorityProvider implements AuthenticationProvider {
private AuthenticationProvider delegate;
public CustomAuthorityProvider(AuthenticationProvider delegate) {
this.delegate = delegate;
}
public Authentication authenticate(Authentication authentication) {
final Authentication a = delegate.authenticate(authentication);
// Load additional authorities and create an Authentication object
final List<GrantedAuthority> authorities = getGrantedAuthorities(a.getName());
return new UsernamePasswordAuthenticationToken(a.getPrincipal(),a.getCredentials(),authorities);
}
#Override
public boolean supports(Class<?> authentication) {
return delegate.supports(authentication);
}
List<GrantedAuthority> getGrantedAuthorities(String username) {
JdbcTemplateDataSource ds = new JdbcTemplateDataSource();
List<GrantedAuthority> roles = ds.getJdbcTemplate().query("select r.Role from Users u join UserRole ur on u.UserId = "
+ "ur.UserId join Roles r on r.RoleId = ur.RoleId where Username = ?",
new String[]{username},
new RowMapper<GrantedAuthority>() {
public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException {
return new SimpleGrantedAuthority(rs.getString(1));
}
});
return roles;
}
}
Related
Stacktrace
Why i have java.lang.NullPointerException in UserDetailsServiceImplementation when i try call method loadUserByUsername in line AdminUser user = (AdminUser) jpaUserDao.findUserByEmail(username);
`2016-02-13 15:08:52 ERROR UsernamePasswordAuthenticationFilter:226 - An internal error occurred while trying to authenticate the user.
Caused by: java.lang.NullPointerException
at com.softserveinc.ita.redplatform.business.service.UserDetailsServiceImplementation.loadUserByUsername(UserDetailsServiceImplementation.java:39)
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:114)
... 41 more`
spring-security.xml
<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.xsd">
<!-- enable use-expressions -->
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/adm/**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/red/**" access="hasAnyRole('ROLE_ADMIN', 'ROLE_REDADMIN')" />
<intercept-url pattern="/**" access="permitAll" />
<!-- access denied page -->
<access-denied-handler error-page="/index" />
<form-login login-page="/login" default-target-url="/index"
login-processing-url="/j_spring_security_check"
authentication-failure-url="/login?error"
username-parameter="email"
password-parameter="password" />
<logout logout-success-url="/login?logout" />
</http>
<beans:bean id="UserDetailsServiceImplementation"
class="com.softserveinc.ita.redplatform.business.service.UserDetailsServiceImplementation">
</beans:bean>
<authentication-manager>
<authentication-provider user-service-ref="UserDetailsServiceImplementation">
</authentication-provider>
</authentication-manager>
</beans:beans>
persistance.xml
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">
<context:component-scan base- package="com.softserveinc.ita.redplatform.persistence.dao.impl" />
<bean id="entityManagerFactoryBean"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="JPAUnit" />
<property name="packagesToScan">
<list>
<value>com.softserveinc.ita.redplatform.common.entity</value>
</list>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</props>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoryBean" />
</bean>
<tx:annotation-driven />
</beans>
JPAAdminUserDao find user by email
#Repository
#Transactional
public class JPAAdminUserDao extends JPAGenericDao<AdminUser, Long>
implements AdminUserDao {
#PersistenceContext
private EntityManager entityManagerFactoryBean;
public final AdminUser findUserByEmail(final String email) {
List<AdminUser> users = new ArrayList<AdminUser>();
users = (List<AdminUser>) entityManagerFactoryBean
.createQuery("from " + AdminUser.class.getName()
+ " as user where user.email=:email")
.setParameter("email", email).getResultList();
if (users.size() > 0) {
return users.get(0);
} else {
return null;
}
}
public static void main(String[] args) {
JPAAdminUserDao dao = new JPAAdminUserDao();
dao.findUserByEmail("rom23");
}
}
UserDetailsServiceImplementation
public class UserDetailsServiceImplementation implements UserDetailsService {
private AdminUserDao jpaUserDao;
#Override
public final UserDetails loadUserByUsername(final String username)
throws UsernameNotFoundException {
HashSet<String> set = new HashSet<String>();
AdminUser user = (AdminUser) jpaUserDao.findUserByEmail(username);
if (user instanceof AdminUser) {
set.add(new String("ROLE_USER"));
set.add(new String("ROLE_ADMIN"));
set.add(new String("ROLE_REDADMIN"));
} else {
set.add(new String("ROLE_USER"));
}
List<GrantedAuthority> authorities = buildUserAuthority(set);
return buildUserForAuthentication(user, authorities);
}
private User buildUserForAuthentication(final AdminUser user,
final List<GrantedAuthority> authorities) {
return new User(user.getEmail(), user.getPassword(), true,
true, true, true, authorities);
}
private List<GrantedAuthority> buildUserAuthority(final
Set<String> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (String userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole));
}
List<GrantedAuthority> result = new
ArrayList<GrantedAuthority>(setAuths);
return result;
}
public final AdminUserDao getJpaUserDao() {
return jpaUserDao;
}
public final void setAdminUserDao(final AdminUserDao newJpaUserDao) {
this.jpaUserDao = newJpaUserDao;
}
}
the issue is with this line of code:
AdminUser user = (AdminUser) jpaUserDao.findUserByEmail(username);
Simply jpaUserDao is not injected.
you have to set jpaUserDao in bean declaration, like:
<beans:bean id="UserDetailsServiceImplementation"
class="com.softserveinc.ita.redplatform.business.service.UserDetailsServiceImplementation">
<property name="adminUserDao" ref="jpaUserDao" />
</beans:bean>
and make sure that jpaUserDao is also declared
i'm learning spring framework, and i created a login app according to
http://o7planning.org/web/fe/default/en/document/29799/simple-login-web-application-using-spring-mvc-spring-security-and-spring-jdbc
but when i try to login using the username & password at database, it redirects me to error, here is the xml
spring-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<http use-expressions="true">
<intercept-url pattern="/" access="isAnonymous()" />
<intercept-url pattern="/welcome" access="isAnonymous()" />
<intercept-url pattern="/login" access="isAnonymous()" />
<intercept-url pattern="/logout" access="isAnonymous()" />
<intercept-url pattern="/userInfo"
access="hasAnyRole('ROLE_USER', 'ROLE_ADMIN')" />
<intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/other/**" access="isAuthenticated()" />
<access-denied-handler error-page="/403" />
<form-login login-page='/login' login-processing-url="/j_spring_security_check"
default-target-url="/userInfo" always-use-default-target="false"
authentication-failure-url="/login?error=true" username-parameter="username"
password-parameter="password" />
<logout logout-url="/logout" logout-success-url="/logoutSuccessful"
delete-cookies="JSESSIONID" invalidate-session="true" />
</http>
<authentication-manager>
<!-- authentication from database -->
<authentication-provider>
<jdbc-user-service data-source-ref="myDataSource"
users-by-username-query="select username,password, enabled from users where username=?"
authorities-by-username-query="select username, role from users where username=?" />
</authentication-provider>
</authentication-manager>
</beans:beans>
data-source-cfg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<bean id="myDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/testdb_o7" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
</beans>
i use mysql, how can i check the app is connected to DB and retrieved the right username & password sucessfully
<beans:bean id="customUserDetailsService" class="com.myclass.dao.SimpleUserDetailsService">
</beans:bean>
<authentication-manager>
<authentication-provider user-service-ref="customUserDetailsService">
<password-encoder hash="md5"> </password-encoder>
<!-- [plaintext, sha, sha-256, md5, md4, {sha}, {ssha}] -->
</authentication-provider>
</authentication-manager>
<beans:bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<beans:property name="exceptionMappings">
<beans:props>
<beans:prop key="org.springframework.security.core.userdetails.UsernameNotFoundException">/login.action?error=1</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
In SimpleUserDetailService for example you will test and check user from DB
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import UserBean;
#Repository
#Service
#Transactional(readOnly=true)
public class SimpleUserDetailsService implements UserDetailsService {
#Autowired
private UserDao userDAO;
private UserBean domainUser;
public UserDetails loadUserByUsername(String login)
throws UsernameNotFoundException {
domainUser = userDAO.getUser(login);
boolean enabled = true;
boolean disabled = false;
boolean accountNonExpired = true;
boolean accountExpired = false;
boolean credentialsNonExpired = true;
boolean credentialsExpired = false;
boolean accountNonLocked = true;
boolean accountLocked = false;
if (null != domainUser){
return new User(
domainUser.getUsername(), //getLogin(),
domainUser.getPassword(),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(domainUser.getIduser() ) );
} else {
return new User(
"**20**",
"**//**",
disabled,
accountExpired,
credentialsExpired,
accountLocked,
getAuthorities(-20 ) ) ;
}
}
public Collection<? extends GrantedAuthority> getAuthorities(Integer userid) {
List<GrantedAuthority> authList = getGrantedAuthorities(getRoles(userid));
return authList;
}
public List<String> getRoles(Integer userid) {
List<String> roles = new ArrayList<String>();
roles = userDAO.getUserRoles(userid);
return roles;
}
public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
}
in userDAO.getUser(login) you select user from table_user where user_name=:login or email = :login
well i try to redirect user base on role but my custom class do nothing i do some system out but nothing happen.
i follow this small tutorial http://oajamfibia.wordpress.com/2011/07/07/role-based-login-redirect/#comment-12
but i change my extention class for SavedRequestAwareAuthenticationSuccessHandler i also try the one in the tutorial nothing happen don't know what i missing. Any help will be appreciate.
this is my class
#Component
public class RoleBaseAuthentification extends SavedRequestAwareAuthenticationSuccessHandler{
private Map<String, String> roleMap;
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
System.out.println(request.getUserPrincipal());
if(authentication.getPrincipal() instanceof UserDetails){
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
System.out.println(userDetails);
String role = userDetails.getAuthorities().isEmpty() ? null : userDetails.getAuthorities().toArray()[0].toString();
System.out.println(role);
response.sendRedirect(request.getContextPath() + roleMap.get(role));
}
super.onAuthenticationSuccess(request, response, authentication);
}
public Map<String, String> getRoleMap() {
return roleMap;
}
public void setRoleMap(Map<String, String> roleMap) {
this.roleMap = roleMap;
}
}
and here is my security-context.xml
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username,password,enabled from users where username = ?"
authorities-by-username-query="select u.username, ur.authority from users u, user_roles ur where u.user_id = ur.user_id and u.username = ?" />
</security:authentication-provider>
</security:authentication-manager>
<security:http use-expressions="true">
<security:intercept-url pattern="/management" access="hasRole('ROLE_ADMIN')"/>
<security:form-login login-page="/login" authentication-success-handler-ref="redirectByRole"/>
</security:http>
<bean id="redirectByRole" class="com.freelogic.spring.web.service.RoleBaseAuthentification">
<property name="roleMap">
<map>
<entry key="ROLE_ADMIN" value="/management.jsp" />
<entry key="ROLE_USER" value="/home.jsp" />
</map>
</property>
</bean>
The problem
Your success handler extends SavedRequestAwareAuthenticationSuccessHandler so calling
super.onAuthenticationSuccess(request, response, authentication)
causes SavedRequestAwareAuthenticationSuccessHandler or one of it's super classes to use their redirect strategies and override yours.
The (ergghh) solution
You can prevent the redirect by calling super.onAuthenticationSuccess() before your reponse.sendRedirect(). It will override the redirecting attempts previously made. While not a good solution it will work. See below why I think it's not a good solution.
On a side note
I'm not sure why you are extending SavedRequestAwareAuthenticationSuccessHandler and not simply implementing AuthenticationSuccessHandler. The former allows a authenticated user to redirect to it's previous visited page. Your RoleBaseAuthentification only redirects by role with no condition to return to a previous url. So your choice to extend SavedRequestAwareAuthenticationSuccessHandler does not make sense to me.
Can't get Spring Security to work with DB authentication provider.
In-memory authentication provider works OK.
Step to reproduce:
when I logged with credentials sb,sb,login() method of AuthenticationService returned false.
There are no related log in Tomcat.
applicationContext.xml:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/chirokDB?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
service layer:
#Service("authenticationService")
public class AuthenticationServiceImpl implements AuthenticationService {
#Resource(name = "authenticationManager")
private AuthenticationManager authenticationManager;
public boolean login(String username, String password) {
try {
Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
username, password));
if (authenticate.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authenticate);
return true;
}
} catch (AuthenticationException e) {
}
return false;
}
managed bean level:
public String doLogin() {
boolean isLoggedIn = authenticationService.login(name, password);
if (isLoggedIn) {
return "index";
}
FacesContext.getCurrentInstance().addMessage("login failure", new FacesMessage());
return "failureLogin";
}
applicationContext-security.xml:
<global-method-security pre-post-annotations="enabled"/>
<http auto-config="true">
<form-login login-page="/login.xhtml" default-target-url="/index.xhtml"/>
<intercept-url pattern="/contacts.xhtml" access="ROLE_ANONYMOUS,ROLE_USER"/>
<intercept-url pattern="/delivery.xhtml" access="ROLE_USER"/>
<logout invalidate-session="true"/>
<session-management>
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
</session-management>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"/>
</authentication-provider>
</authentication-manager>
persistence level:
MySql DB has following standard tables(required by Spring):
1. users
2. authorities
users table has record with username='sb' and password='sb'
authorities table has record with username='sb' and authority='ROLE_USER'
note
with user-in memory all works OK with following config:
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="sb" password="sb" authorities="ROLE_USER"/>
</user-service>
</authentication-provider>
</authentication-manager>
assumption:
dataSource injected into org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl
As far Hibernate ORM used, perhaps some other than JdbcDaoImpl should be used?
Check if you're getting an Exception in your empty catch block (which always is a bad idea).
may i know possible to use spring security to limit max number of users able to login to website at the same time?
definately, not concurrent-session-control parameter. what i want is for instance, i want to limit maximum only allow 1000 users login same time. if more than that forward to notice page stating maximum users exceeded
You can use Spring Security's concurrent session control by accessing the SessionRegistry to find out how many users are currently logged in. In Spring Security 3, the ConcurrentSessionControlStrategy is responsible for controlling whether the user is allowed to create a session after logging in. You can extend this class and add an extra check based on the number of users:
public class MySessionAuthenticationStrategy extends ConcurrentSessionControlStrategy {
int MAX_USERS = 1000; // Whatever
SessionRegistry sr;
public MySessionAuthenticationStrategy(SessionRegistry sr) {
super(sr);
this.sr = sr;
}
#Override
public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
if (sr.getAllPrincipals().size() > MAX_USERS) {
throw new SessionAuthenticationException("Maximum number of users exceeded");
}
super.onAuthentication(authentication, request, response);
}
}
You would then inject this into the security namespace as described in the Spring Security reference manual.
In Spring Security 2.0, the concurrent session control is implemented slightly differently and you would customize the ConcurrentSessionController instead.
I do not have enough reputation to add a comment. But getAllPrincipals returns all principals including ones from expired sessions. Use some method like below to getAllActiveSessions.
private List<SessionInformation> getActiveSessions(SessionRegistry sessionRegistry) {
final List<Object> principals = sessionRegistry.getAllPrincipals();
if (principals != null) {
List<SessionInformation> sessions = new ArrayList<>();
for (Object principal : principals) {
sessions.addAll(sessionRegistry.getAllSessions(principal, false));
}
return sessions;
}
return Collections.emptyList();
}
this post is a bit old but I have had the same problem in spring security 4.1 and I have solved it like that.
session-management
<security:http disable-url-rewriting="true" use-expressions="true" auto-config="true">
<security:session-management invalid-session-url="/app/login" session-authentication-strategy-ref="sessionAuthenticationStrategy">
</security:session-management>
</security:http>
session-authentication-strategy-ref
<bean id="sessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
<constructor-arg>
<list>
<bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
<constructor-arg ref="sessionRegistry"/>
<property name="maximumSessions" value="1" />
<property name="exceptionIfMaximumExceeded" value="true" />
</bean>
<bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy">
</bean>
<bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
<constructor-arg ref="sessionRegistry"/>
</bean>
</list>
</constructor-arg>
</bean>
SessionRegistry
#Autowired
private SessionRegistry sessionRegistry;
Authentication
List<SessionInformation> sessions = new ArrayList<>();
for (Object principal : sessionRegistry.getAllPrincipals()) {
sessions.addAll(sessionRegistry.getAllSessions(principal, false));
}
LOGGER.info("Sessiones Activas: " + sessions.size());
// filtro para limite de sessiones
if (sessions.size() < max_sessions) {
//authentication
} else {
throw new SessionAuthenticationException("Maximo numero de Usuarios exedido.");
}
in this way because I am authenticating based on security: custom-filter