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
Related
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?
The following code in my DAO works perfectly fine.
public void insert(final Person person) {
transactionTemplate.execute(new TransactionCallback<Void>() {
public Void doInTransaction(TransactionStatus txStatus) {
try {
getJdbcTemplate().execute("insert into person(username, password) values ('" + person.getUsername() + "','" + person.getPassword() + "')");
} catch (RuntimeException e) {
txStatus.setRollbackOnly();
throw e;
}
return null;
}
});
}
Following is my spring config.
<bean id="derbyds" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="username" value="app" />
<property name="password" value="app" />
<property name="url" value="jdbc:derby:mytempdb" />
</bean>
<bean id="persondaojdbc" class="com.napp.dao.impl.PersonDaoJdbcImpl">
<property name="dataSource" ref="derbyds" />
<property name="transactionTemplate">
<bean class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="derbyds"/>
</bean>
What i wanted to know is how does the
<bean id="persondaojdbc" class="com.napp.dao.impl.PersonDaoJdbcImpl">
<property name="dataSource" ref="derbyds" />
<property name="transactionTemplate">
<bean class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="derbyds"/>
</bean>
Now, its imperative that both the TransactionManager and the code (in my case jdbc template) operate on the same connection. I am assuming both of them are getting the Connection objects from the DataSource. DataSource pools connections and its a chance when you call getConnection multiple times, you will get different Connection obejcts. How does spring make sure that the TransactionManager and JdbcTemplate end up getting the same connection objects. My understanding is that, if that doesn't happen, rollbacks, or commits wont work, correct? Could someone throw more light on this.
If you look at the code for JdbcTemplate (one of the execute(...) methods) you will see
Connection con = DataSourceUtils.getConnection(getDataSource());
Which tries to retrieve a Connection from a ConnectionHolder registered with a TransactionSynchronizationManager.
If there is no such object, it just gets a connection from the DataSource and registers it (if it is in a transactional environment, ie. you have a transaction manager). Otherwise, it immediately returns the registered object.
This is the code (stripped of logs and stuff)
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.
Connection con = dataSource.getConnection();
// flag set by the TransactionManager
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// Use same Connection for further JDBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
return con;
You'll notice that the JdbcTemplate tries to
finally {
DataSourceUtils.releaseConnection(con, getDataSource());
}
release the Connection, but this only happens if you're in a non-transactional environment, ie.
if it is not managed externally (that is, not bound to the thread).
Therefore, in a transactional world, the JdbcTemplate will be reusing the same Connection object.
I'm working on a web application where multiple applications authenticates through a CAS SSO Server. Howerver, each application should maintain their respective roles and these roles are stored in a database specific to the application. So, I need to have 2 realms, one for CAS (for authc) and another for DB (for authz).
This is my current shiro config. I'm getting the redirection to the CAS working properly, but the logged in user (Subject) doesn't seems to have the roles/permission loaded in it (e.g. SecurityUtil.isPermitted() not working as expected)
<bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
<property name="name" value="jdbcRealm" />
<property name="dataSource" ref="dataSource" />
<property name="authenticationQuery"
value="SELECT password FROM system_user_accounts WHERE username=? and status=10" />
<property name="userRolesQuery"
value="SELECT role_code FROM system_roles r, system_user_accounts u, system_user_roles ur WHERE u.user_id=ur.user_id AND r.role_id=ur.role_id AND u.username=?" />
<property name="permissionsQuery"
value="SELECT code FROM system_roles r, system_permissions p, system_role_permission rp WHERE r.role_id=rp.role_id AND p.permission_id=rp.permission_id AND r.role_code=?" />
<property name="permissionsLookupEnabled" value="true"></property>
<property name="cachingEnabled" value="true" />
<property name="credentialsMatcher" ref="passwordMatcher" />
</bean>
<!-- For CAS -->
<bean id="casRealm" class="org.apache.shiro.cas.CasRealm">
<property name="defaultRoles" value="ROLE_USER" />
<property name="casServerUrlPrefix" value="http://localhost:7080/auth" />
<property name="casService" value="http://localhost:8080/hawk-hck-web/shiro-cas" />
<property name="validationProtocol" value="SAML" />
<property name="cachingEnabled" value="true"></property>
</bean>
<bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory" />
<!-- Security Manager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realms">
<list>
<ref bean="casRealm" />
<ref bean="jdbcRealm" />
</list>
</property>
<property name="cacheManager" ref="cacheManager"/>
<property name="subjectFactory" ref="casSubjectFactory" />
</bean>
<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
<property name="failureUrl" value="/error"></property>
</bean>
<!-- Shiro filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="http://localhost:7080/auth/login?service=http://localhost:8080/hawk-hck-web/shiro-cas" />
<property name="successUrl" value="/home/index" />
<property name="unauthorizedUrl" value="/error" />
<property name="filters">
<util:map>
<entry key="casFilter" value-ref="casFilter" />
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
<!-- !!! Order matters !!! -->
/shiro-cas = casFilter
/login = anon
/logout = logout
/error = anon
/static/** = anon
/** = authc
</value>
</property>
</bean>
The way I register the realms with the securityManager should be in correct. I can't really find a good example of the setup.
I have 2 questions here:
What is correct setup/configuration to achieve above mentioned scenario?
What is the best practice to manage users and roles across different/seperate applications?
The problem you are running into has to do with the fact that both CasRealm and JdbcRealm extends both AuthorizingRealm (Authorizer) and AuthenticatingRealm. First step I would take is with the JdbcRealm. The JdbcRealm implementation inherits the AuthenticatingRealm#supports(AuthenticationToken token) method implementation. If you extend JdbcRealm and override the "supports" method to return "false" for all token types the JdbcRealm will no longer be used for authentication purposes.
#Override
public boolean supports (AuthenticationToken token) {
return false;
}
The CasRealm is a different story, there is no way (that I know of) to easily tell Shiro to not use a realm that implements Authorizer when checking permissions. I personally find it frustrating that the default implementation for most protocols assumes that both authorization and authentication are needed. I would prefer each to be split into two implementations (eg AuthenticatingCasRealm, AuthorizingCasRealm).
The logic behind checking permissions when multiple realms are in use is documented here. The specific text that references this behavior is:
Step 4: Each configured Realm is checked to see if it implements the
same Authorizer interface. If so, the Realm's own respective hasRole*,
checkRole*, isPermitted*, or checkPermission* method is called.
Based on this, you theoretically could override each of the named methods and all of their overloaded implementations to always return "false".
My solution to this problem is based on my prior comment about splitting each realm into two components, one for authentication and one for authorization. You end up with more duplicate code this way but it is explicit in what behaviors you are expecting from your implementation.
Here's how to go about it:
Create a new class "AuthenticatingCasRealm" that extends org.apache.shiro.realm.AuthenticatingRealm and implements org.apache.shiro.util.Initializable.
Copy and paste the contents of the existing CasRealm source into your new "AuthenticatingCasRealm" class. (I am aware that taking a copy-and-paste route of existing code is often frowned upon however in the described circumstsance I know of no other way of solving the problem.)
Strip out all methods that were implemented for org.apache.shiro.realm.AuthorizingRealm.
Update your Shrio configuration to reference your new AuthenticatingCasRealm implementation.
Based on these changes you should now have two custom implementations in your Shrio config; one of JdbcRealm overriding the "supports" method and one of CasRealm removing the authorization API methods.
There is one additional method based on explicitly declaring an Authorizer via Shiro's configuration that may be better suited to your situation.
Here is an explicit declaration of an Authorizer and Authenticator via a custom ShiroFilter extension. Both were implemented and registered to the provided JNDI names at startup.
public class CustomShiroFilter extends ShiroFilter {
#Override
public void init () throws Exception {
super.init();
DefaultWebSecurityManager dwsm = (DefaultWebSecurityManager) getSecurityManager();
dwsm.setAuthorizer((Authorizer)JndiUtil.get("realms/authorizerRealm"));
dwsm.setAuthenticator((Authenticator)JndiUtil.get("realms/authenticatorRealm"));
}
}
You need only one realm that extends AuthorizingRealm. It will provide
authc: method doGetAuthenticationInfo (CAS server)
authz: method doGetAuthorizationInfo (JDBC)
Hope this helps
We had a similar case where we use a LDAP Realm for authentication and used the standard shiro.ini file for the authorization for a simple use case.
To complement the answer of 'justin.hughey', I give the blueprint (could be spring as well) configuration in order to make your use case working:
<!-- Bean for Authentication -->
<bean id="rccadRealm" class="org.mydomain.myproject.security.shiro.ldap.realm.LdapRealm"
init-method="init">
<property name="searchBase" value="${realm.searchBase}" />
<property name="singleUserFilter" value="${realm.singleUserFilter}" />
<property name="timeout" value="30000" />
<property name="url" value="${contextFactory.url}" />
<property name="systemUsername" value="${contextFactory.systemUsername}" />
<property name="systemPassword" value="${contextFactory.systemPassword}" />
</bean>
<!-- Bean for Authorization -->
<bean id="iniRealm" class="org.mydomain.myproject.security.realm.AuthzOnlyIniRealm">
<argument value="file:$[config.base]/etc/shiro.ini"/>
<property name="authorizationCachingEnabled" value="true" />
</bean>
<bean id="myModularAuthenticator"
class="org.mydomain.myproject.security.service.MyModularRealmAuthenticator">
<property name="realms">
<list>
<ref component-id="ldapRealm" />
</list>
</property>
</bean>
<bean id="mySecurityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">
<property name="authenticator" ref="myModularAuthenticator" />
<property name="authorizer" ref="iniRealm" />
<property name="cacheManager" ref="cacheManager" />
</bean>
The key things is that we needed:
a modularRealmAuthenticator and let the default strategy (as there's only one realm) for the 'authenticator'
a special AuthzOnlyIniRealm which overrides the method supports returning false to prevent using it for authentication.
Our LdapRealm implementation is just an extension of the Shiro ActiveDirectoryRealm.
I have the below configurations in Spring , it is working fine but performance is too low (it takes 1 min for 20 messages). Can you please suggest me changes to increase the performance.
<bean id="jmsConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="transportType"><value>1</value></property>
<property name="queueManager"><value></value></property>
<property name="hostName"><value></value></property>
<property name="port"><value></value></property>
<property name="channel"><value></value></property>
<property name="clientId"><value></value></property>
</bean>
<bean id="SenderJMSTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory"><ref bean="jmsConnectionFactory" /> </property>
<property name="pubSubDomain"><value>false</value></property>
<property name="defaultDestination"><ref bean="senderQueue" /></property>
</bean>
<bean id="senderQueue" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="TEST" />
<property name="baseQueueManagerName"><value>tree.queue.manager</value></property>
<property name="baseQueueName"><value>ORANGE.QUEUE</value></property>
</bean>
<bean id="jmsSender" class="org.tree.jms.spring.JMSSender">
<property name="jmsTemplate"><ref bean="SenderJMSTemplate"/></property>
</bean>
I am calling from spring as
JMSSender obj = (JMSSender) context.getBean("jmsSender");
And My Sender program is :
#Cacheable("message")
public void sendMesage() {
jmsTemplate.send(new MessageCreator() {
public Message createMessage(Session session)throws JMSException {
message = (Message) session.createTextMessage(stringBuffer.toString());
return message;
}
});
}
}
A common problem when using JMSTemplate to send messages out of JavaEE containers is the it's extremly slow since it acquires a new connection for each message (and then closes it). You would probably need a pooled/cached connection to gain speed here.
Read this article, it's written for ActiveMQ, but applies in a similar way to WebSphere MQ: http://activemq.apache.org/jmstemplate-gotchas.html
You can setup a cached connection factory in spring using something like this:
<bean id="cachedConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory"
p:targetConnectionFactory-ref="jmsConnectionFactory"
p:sessionCacheSize="10" />
Then use it instead of the original one for JMS connections.
I am trying to inject the aspects in a service. For this service I am creating a proxied object using classic way.
I have written a bean- baseProxy of type (ProxyFactoryBean) which contains a list of all the required advices.
<bean id="baseProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>methodInvocationAdvice</value>
</list>
</property>
</bean>
I am creating a proxy for the service like this :
<bean id="singproxy" parent="baseProxy">
<property name="target" ref="singtarget" />
<property name="targetClass" value="com.spring.learning.SingingService"></property>
</bean>
Which doesn't work but when I revert these two properties and write like this :
<bean id="singproxy" parent="baseProxy">
<property name="targetClass" value="com.spring.learning.SingingService"></property>
<property name="target" ref="singtarget" />
</bean>
To my surprise it works fine. In spring does it matter on the order for bean ? Or its a special case with ProxyFactoryBean?
I tried with Spring 3.0 I am not sure same behavior exists with previous versions.
Concerning target and targetClass, It's one or the other, but not both. Here's the relevant source (from org.springframework.aop.framework.AdvisedSupport), a parent class of ProxyFactoryBean:
public void setTarget(Object target) {
setTargetSource(new SingletonTargetSource(target));
}
public void setTargetSource(TargetSource targetSource) {
this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE);
}
public void setTargetClass(Class targetClass) {
this.targetSource = EmptyTargetSource.forClass(targetClass);
}
As you can see, both setTarget() and setTargetClass() write to the same field, so the last assignment wins.