I am trying to do simple AD authentication using Spring LDAP. Below is my config xml
<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="ldap://ValidADhost:port" />
<property name="base" value="dc=ad,dc=XXX,dc=com"/>
<property name="userDn" value="ValidUserName" />
<property name="password" value="ValidPassword" />
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="contextSource" />
</bean>
Authentication code:
public boolean login(String username, String password) {
AndFilter filter = new AndFilter();
ldapTemplate.setIgnorePartialResultException(true);
filter.and(new EqualsFilter("sAMAccountName", username));
return ldapTemplate.authenticate("", filter.toString(), password);
}
With this code, Im getting the following Exception
HTTP Status 500 - Request processing failed; nested exception is org.springframework.ldap.CommunicationException:ValidADhost:port;nested exception is javax.naming.CommunicationException:ValidADhost:port[Root exception is java.net.UnknownHostException:ValidADhost:port]
I am able to get the user details from the same LDAP hostname:port in C# test program.
I appreciate any help/pointers/solutions.
Related
Migrating existing legacy application to java 11 and spring 5 and seems to be ServiceActivator method not invoking. I see publisher method invoking and printing log statement but no logs printing from ServiceActivator class
spring 5.3.14
spring-integration-* 5.5.7
XML Configuration:
<int:channel id="employeeServicesChannel">
<int:dispatcher task-executor="employeeServicesExecutor" />
</int:channel>
<bean id="employeeServicesExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10" />
<property name="maxPoolSize" value="40" />
<property name="queueCapacity" value="10" />
<property name="keepAliveSeconds" value="0"></property>
</bean>
public class EmployeeServicesPublisher {
#Publisher(channel = "employeeServicesChannel")
public EmployeeServicesDto publishEmployeeServicesRequest(EmployeeServicesDto requestDto) {
logger.info(" employee service request :" + requestDto);
return requestDto;
}
#MessageEndpoint
public class EmployeeServicesServerGateway {
#ServiceActivator(inputChannel = "employeeServicesChannel", outputChannel =
"esDynamicOutputChannel")
public EmployeeServicesDto processRequest(EmployeeServicesDto employeeServicesDto) {
logger.info("===================================");
logger.info(" request Id:" + employeeServicesDto.getRequestId());
return employeeServicesDto;
}
I am working on a Spring-MVC application with spring-security and I have 2 different types of users who can login, one is from a personal account, and one is the group account.
So basically I want 2 daoAuthenticationMethods.
For both I have implemented the UserDetails and userDetailsService interface. After referring to the post on this I am trying to implement that approach.
The error I am getting is conflicting userDetailsService in the Service layer. I know I cannot use 2 userDetailsService, but if I put something else in the xml's property tab, I get unknown property error. Kindly check the configuration and please tell me what I might be doing wrong.
Error log :
Offending resource: ServletContext resource [/WEB-INF/spring/appServlet/security-applicationContext.xml]; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from ServletContext resource [/WEB-INF/spring/appServlet/servlet-context.xml]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'userDetailsService' for bean class [com.journaldev.spring.service.GroupLoginServiceImpl] conflicts with existing, non-compatible bean definition of same name and class [com.journaldev.spring.service.LoginServiceImpl]
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:70)
at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:85)
Security-application-context.xml :
<!-- Global Security settings -->
<security:global-method-security pre-post-annotations="enabled" />
<security:http create-session="ifRequired" use-expressions="true" auto-config="true" disable-url-rewriting="true">
<security:form-login login-page="/" default-target-url="/canvas/list"
always-use-default-target="false" authentication-failure-url="/denied.jsp" />
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map path-type="ant">
<security:filter-chain pattern="/**" filters="authenticationProcessingFilterForPersonal, authenticationProcessingFilterForGroup"/>
</security:filter-chain-map>
</bean>
<bean id="authenticationProcessingFilterForPersonal"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManagerForPersonal"/>
<property name="filterProcessesUrl" value="/j_spring_security_check_for_person" />
</bean>
<bean id="authenticationProcessingFilterForGroup"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManagerForGroup"/>
<property name="filterProcessesUrl" value="/j_spring_security_check_for_group"/>
</bean>
<bean id="authenticationManagerForPersonal" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<bean class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService">
<ref bean="LoginServiceImpl"/>
</property>
<property name="passwordEncoder" ref="encoder"/>
</bean>
</list>
</property>
</bean>
<bean id="authenticationManagerForGroup" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<bean class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService">
<ref bean="GroupLoginServiceImpl"/>
</property>
<property name="passwordEncoder" ref="encoder"/>
</bean>
</list>
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="authenticationManagerForPersonal"/>
<security:authentication-provider ref="authenticationManagerForGroup"/>
</security:authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11" />
</beans:bean>
LoginServiceImpl :
// This method is for the personalAccount
#Transactional
#Service("userDetailsService")
public class LoginServiceImpl implements UserDetailsService{
#Autowired private PersonDAO personDAO;
#Autowired private Assembler assembler;
private static final GrantedAuthority USER_AUTH = new SimpleGrantedAuthority("ROLE_USER");
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,DataAccessException {
Person person = personDAO.findPersonByUsername(username.toLowerCase());
if(person == null) { throw new UsernameNotFoundException("Wrong username or password");}
return assembler.buildUserFromUserEntity(person);
}
}
GroupLoginServiceImpl :
#Transactional
#Service("userDetailsService") // I cannot change this, it throws me error when I change this or remove this
public class GroupLoginServiceImpl implements UserDetailsService {
#Autowired
private GroupMembersDAO groupMembersDAO;
#Autowired
private GroupAssembler groupAssembler;
private static final GrantedAuthority USER_AUTH = new SimpleGrantedAuthority("ROLE_GROUP");
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,DataAccessException {
GroupMembers groupMembers = groupMembersDAO.findMemberByUsername(username.toLowerCase());
if(groupMembers == null) { throw new UsernameNotFoundException("Wrong username or password");}
return groupAssembler.buildUserFromUserEntity(groupMembers);
}
}
I can post any other methods too if necessary. kindly let me know what to do. Any pointers are welcome. Thank you.
I think you have misunderstood how to write the XML. The first instance should be something like:
<property name="userDetailsService" ref="userDetailsService">
And the second:
<property name="userDetailsService" ref="groupDetailsService">
I'm developing a web application using Spring, Vaadin and Apache Shiro for authentication and authorization. I have two realms, since some users log in through a database, others authenticate against LDAP. JDBC realm works perfectly but somehow LDAP realm lets everybody through - no matter what username/password combination is provided.
Here is my Spring configuration:
<!-- Apache Shiro -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realms">
<list>
<ref bean="jdbcRealm" />
<ref bean="ldapRealm" />
</list>
</property>
<property name="authenticator.authenticationStrategy">
<bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy" />
</property>
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<bean id="ldapContextFactory" class="org.apache.shiro.realm.ldap.JndiLdapContextFactory">
<property name="url" value="ldap://localhost:389" />
</bean>
<bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="ldapRealm" class="org.apache.shiro.realm.ldap.JndiLdapRealm">
<property name="contextFactory" ref="ldapContextFactory" />
<property name="userDnTemplate" value="uid={0},ou=people,dc=maxcrc,dc=com" />
</bean>
Logging in is rather typical:
try {
// Obtain user reference
Subject currentUser = SecurityUtils.getSubject();
// Create token using provided username and password
UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
// Remember user
if(rememberMe.getValue())
token.setRememberMe(true);
// Login
currentUser.login(token);
// If we are here, no exception was raised and the user was logged in, so redirect
UI.getCurrent().getNavigator().navigateTo("main" + "/" + "main-page");
// Fire CustomEvent
fireEvent(new CustomEvent(ErasmusLoginForm.this));
} catch ( UnknownAccountException e ) {
Notification.show("No such user...");
} catch ( IncorrectCredentialsException e ) {
Notification.show("Invalid creditentials...");
} catch ( LockedAccountException e ) {
Notification.show("Locked account...");
} catch ( AuthenticationException e ) {
e.printStackTrace();
Notification.show("Some other exception...");
} catch (Exception e) {
// Password encryption exception
}
I read almost everywhere with no luck.
This post (Shiro Authenticates Non-existent User in LDAP) also wasn't helpful to me - both the DN template and the URL are correct and the server (LDAP server) is running. Why does it let everybody through?
If I turn Ldap realm off, JDBC authentication works perfectly. But with both of them on, everybody gets through since I'm using FirstSuccessfulStrategy.
EDIT: Additional note: if I provide an empty password, AuthenticationException is raised. But any non-empty password works fine.
Any ideas?
I've been struggling with authenticationStrategy settings with shiro 1.2.1 in a spring based web application. I have 2 realms. One authenticates against database and one against ldap. both realms are working fine just that i wanted a FirstSuccessfulStrategy but it seems both realms are still being called. here is my security-application-context:
<bean id="passwordService" class="org.apache.shiro.authc.credential.DefaultPasswordService">
<property name="hashService" ref="hashService" />
</bean>
<bean id="hashService" class="org.apache.shiro.crypto.hash.DefaultHashService">
<property name="hashAlgorithmName" value="SHA-512" />
<property name="hashIterations" value="500000" />
</bean>
<bean id="SaltedSha512JPARealm" class="bla.bla.webapp.security.SaltedSha512JPARealm">
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.PasswordMatcher">
<property name="passwordService" ref="passwordService"/>
</bean>
</property>
</bean>
<bean id="ldapContextFactory" class="org.apache.shiro.realm.ldap.JndiLdapContextFactory">
<property name="url" value="${user.ldap.connection.url}"/>
<property name="authenticationMechanism" value="${user.ldap.connection.auth_mecanism}"/>
</bean>
<bean id="ldapRealm" class="bla.bla.webapp.security.LDAPRealm">
<property name="userDnTemplate" value="${user.ldap.connection.userDnTemplate}"/>
<property name="contextFactory" ref="ldapContextFactory" />
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" depends-on="roleRepository,roleRightRepository,rightRepository,userRepository">
<property name="realms">
<list>
<ref local="ldapRealm"/>
<ref local="SaltedSha512JPARealm"/>
</list>
</property>
<property name="authenticator.authenticationStrategy">
<bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"/>
</property>
</bean>
is there anything there that i am not doing well?
FirstSuccessfulStrategy means that your authenticator will try all your realms to authenticate user until the first successful. Your realms was configured in order: ldapRealm, SaltedSha512JPARealm. So if lapRealm will fail authenticator will try second one. To solve this you can try to configure the most successful or the quickest realm to be first, e.g. you can change your realms order to be SaltedSha512JPARealm, ldapRealm:
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" depends-on="roleRepository,roleRightRepository,rightRepository,userRepository">
<property name="realms">
<list>
<ref local="SaltedSha512JPARealm"/>
<ref local="ldapRealm"/>
</list>
</property>
<property name="authenticator.authenticationStrategy">
<bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"/>
</property>
</bean>
But you should understand that for this configuration if SaltedSha512JPARealm will fail, authenticator will try ldapRealm.
Or you can try to use different token classes for this realms. But it will work only if you have different authentication entry points for each of them.
UPD
It seems that ModularRealmAuthenticator is designed so that it will always try to authenticate user by all realms. FirstSuccessfulStrategy can affect only on authentication result. It will return first successful AuthenticationInfo. To achieve your goal you need to override ModularRealmAuthenticator#doMultiRealmAuthentication method. It can look like this:
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) {
AuthenticationStrategy strategy = getAuthenticationStrategy();
AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);
if (log.isTraceEnabled()) {
log.trace("Iterating through {} realms for PAM authentication", realms.size());
}
for (Realm realm : realms) {
aggregate = strategy.beforeAttempt(realm, token, aggregate);
if (realm.supports(token)) {
log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);
AuthenticationInfo info = null;
Throwable t = null;
try {
info = realm.getAuthenticationInfo(token);
} catch (Throwable throwable) {
t = throwable;
if (log.isDebugEnabled()) {
String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";
log.debug(msg, t);
}
}
aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);
// dirty dirty hack
if (aggregate != null && !CollectionUtils.isEmpty(aggregate.getPrincipals())) {
return aggregate;
}
// end dirty dirty hack
} else {
log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token);
}
}
aggregate = strategy.afterAllAttempts(token, aggregate);
return aggregate;
}
<property name="authenticator.authenticationStrategy">
<bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"/>
</property>
the above definition is wrong. Define it as follows
<property name="authenticator.authenticationStrategy" ref="authcStrategy"/>
And define the below bean definition separately
<bean id="authcStrategy" class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"/>
Then it will work as expected
We have an application where we have used spring for IOC. We have the dataSource bean configured in applicationContext.xml and that is referenced in other bean definations.
The dataSource bean defination looks like:
<bean id="dbDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url"
value="jdbc:oracle:oci:#TESTDB" />
<property name="username" value="TESTUSER" />
<property name="password" value="TESTPWD" />
<property name="initialSize" value="50" />
<property name="maxActive" value="40" />
<property name="maxIdle" value="10" />
<property name="minIdle" value="10" />
<property name="maxWait" value="-1" />
</bean>
<bean id="serviceDAO" class="com.test.impl.ServiceDAOImpl">
<property name="dataSource" ref="dbDataSource" />
</bean>
ServiceDAOImpl looks as follows:
public class ServiceDAOImpl implements ServiceDAO {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
#SuppressWarnings({ "rawtypes", "unchecked" })
public ValueObj readValue(String key) {
String query = "SELECT * FROM SERVICE_LOOKUP WHERE KEY=?";
/**
* Implement the RowMapper callback interface
*/
return (ValueObj) jdbcTemplate.queryForObject(query,
new Object[] { key }, new RowMapper() {
public Object mapRow(ResultSet resultSet, int rowNum)
throws SQLException {
return new ValueObj(resultSet.getString("KEY"),
resultSet.getString("VALUE"));
}
});
}
public ServiceDAOImpl() {
}
}
Now, at the server start up injection is happening fine and when we use the dataSource in serviceDAOImpl the connection is happening fine. But the very first time the database call is made it takes around 3 mins to get the response back. I think this is because the pool creation is done during the first call and we have set the parameter "initialSize" = 50 in applicationConext.xml.
So, to avoid this we need a way in which the pool can be created during the application startup itself and can be used directly.
Please suggest. Let me know if any clarification required.
Regards
Saroj
There's a work-around for this .You could force jdbcTemplate to use the
DB connection at startup. See the link here for detailed explanation .
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg index="0" ref="dataSource"/>
<constructor-arg index="1" value="false"/>
</bean>
The second constructor-arg is the lazy Init flag.
Aravind A's solution is the preffered one, but just in case you don't want to define an extra bean you can point spring to your DAO's init method:
<bean id="serviceDAO" class="com.test.impl.ServiceDAOImpl" init-method="init">
<property name="dataSource" ref="dbDataSource" />
</bean>
and then define ServiceDAOImpl.init() which calls some sql like SELECT 1 FROM SERVICE_LOOKUP LIMIT 1 or even better some noop like SELECT 1:
public class ServiceDAOImpl implements ServiceDAO {
public void init() {
String query = "SELECT 1 FROM SERVICE_LOOKUP LIMIT 1";
int i = jdbcTemplate.queryForInt(query);
}
}