there is a way to hide/encrypt password in xml spring config file?
I read that is possible with a "custom" subclass of DataSource, but the solutions keep key in same config file as plain text...so is a bit useless.
There is a way to use KeyStore for this?
For example read the value from a keystore.
Thanks all.
What is the purpose of hiding the password? I suggest you configure the datasource in the container (Tomcat, JBoss or whatever you use) and inject the datasource into your application using jndi:
<jee:jndi-lookup id="thedatasource"
jndi-name="java:comp/env/jdbc/thedatasource"
lookup-on-startup="false"
expected-type="javax.sql.DataSource"/>
This way you have not to expose and password in your application but only in the servlet container.
Yes, you can do that. You will have to create a wrapper bean around the data source class. Here is an example of how I have done it before. Hope this helps!
<beans>
<bean id="someDao" class="com.dao.SomeDAOImpl">
<property name="datasource">
<ref local="secureDataSource"/>
</property>
</bean>
<bean id="secureDataSource" class="com.ds.SecureDataSource">
<property name="driverClassName">
<value><your driver></value>
</property>
<property name="url">
<value><your url></value>
</property>
<property name="username">
<value><your user id></value>
</property>
<property name="password">
<value><encrypted_pwd></value>
</property>
</bean>
</beans>
Then inside the SecureDataSource class you will need to decrypt the password.
import java.sql.Connection;
import java.sql.SQLException;
public class SecureDataSource extends DriverManagerDataSource{
private String url;
private String username;
private String password;
/**
* #param url the url to set
*/
public void setUrl(String url) {
this.url = url;
}
/**
* #param username the username to set
*/
public void setUsername(String username) {
this.username = username;
}
/**
* #param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}
protected Connection getConnectionFromDriverManager() throws SQLException {
String decryptedPassword = null;
//decrypt the password here
return getConnectionFromDriverManager(url,username,decryptedPassword);
}
}
Good options have been given, another obvious answer is to use the PropertyPlaceholderConfigurer:
<context:property-placeholder
system-properties-mode="OVERRIDE"
location="classpath:database.properties" />
<bean id="dataSource" class="com.whatever.datasource.you.Use">
<property name="password" value="${database.password}" />
</bean>
Now you can keep your password either as a property in a properties file (which you might create during deployment if you don't want to have it in the SCM) or as a System Property (which will hopefully also be beyond reach of other developers).
Clarification: create during deployment is somewhat vague. I guess you will have to write an installer that generates the properties file dynamically on the end user's machine, probably coupled with a sign up / log in mechanism.
EDIT: I still haven't figured out who you are hiding the information from. Two theories:
a) People who have access to your source code
b) Your customers
If it's a), then go my way. All other ways can easily be breached by the other developer just starting your application with a debugger (and suddenly he's inside the datasource object and sees the password).
If it's b), then you have no chance, basically. The customer has tons of possibilities to get at your password: debuggers, agents, bytecode manipulation, loadtime weaving etc. Even if he doesn't do any of that, he will just have to attach a port sniffer to get at the password in clear text. The only safe thing to do is have a username / password per customer (never store a global password at your customer's machine).
I had the same question recently. I wanted to store a hashed version of the password in a .properties file.
I did the trick thanks to the previous options: I extended the DelegatingDataSource and overrided the getConnection([...]) methods.
public class UnhashingDataSource extends DelegatingDataSource {
private static final Logger LOGGER = Logger.getLogger(UnhashingDataSource.class);
private static final int HEX_RADIX = 16;
private static final String DB_PASS = "a_sample_password";
#Override
public Connection getConnection() throws SQLException {
DriverManagerDataSource dataSource = (DriverManagerDataSource) getTargetDataSource();
return getConnection(dataSource.getUsername(), dataSource.getPassword());
}
#Override
public Connection getConnection(String username, String password) throws SQLException {
try {
DataSource datasource = getTargetDataSource();
if (datasource == null) {
throw new RuntimeException("targetDataSource is null");
}
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.reset();
md.update(DB_PASS.getBytes());
if (password.equals(getHexString(md.digest()))) {
return datasource.getConnection(username, DB_PASS);
} else {
throw new RuntimeException("Unable to connect to DB");
}
} catch (NoSuchAlgorithmException e) {
LOGGER.error("Unknown algorithm");
}
return null;
}
private String getHexString(final byte[] messageDigest) {
BigInteger bigInt = new BigInteger(1, messageDigest);
return bigInt.toString(HEX_RADIX);
}
}
Then, here is how I used it in my applicationContext.xml:
# Using the unhashing datasource
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="unhashingDataSource" />
# ...
</bean>
<bean id="hashedDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${datasource.driverClassName}" />
<property name="url" value="${datasource.url}" />
<property name="username" value="${datasource.username}" />
<property name="password" value="${datasource.hash}" />
</bean>
<bean id="unhashingDataSource"
class="my.package.UnhashingDataSource">
<property name="targetDataSource" ref="hashedDataSource" />
</bean>
Where datasource.hash is a property (from a .properties file) stored like:
datasource.hash = 2e54b0667ef542e3398c55a08a4e04e69b9769e8
The plain password is still in bytecode but not directly in a .properties file anymore.
Thanks for all your post and queries.
Hope for visitors its clear the technical way to encrypt password by reading this page. One important thing I would like to add here, if you are dealing with production then definitely will suggest you to use any "Secure Hash Algorithm" like SHA-256 with salt. You can consider secure hash algorithm using salt as industry standard.
Related
I have a mail sender set up in config file: spring/servlet-context.xml
which has username and password set.
I've come up with a specific use-case where I need to send mail from a different email account.
Is it possible to set this up in this same config file.
At first, I thought this would mean simply to add another bean id having the other email account's username and password set within, but then that didn't make sense to me how is the JavaMailSender going to tell which sender I want each time?!
My code:
In servlet-context.xml:
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="smtp.gmail.com" />
<property name="port" value="587" />
<property name="username" value="default_email#gmail.com" />
<property name="password" value="***1***" />
<property name="javaMailProperties">
<props>
<prop key="mail.transport.protocol">smtp</prop>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.starttls.enable">true</prop>
<prop key="mail.debug">false</prop>
<prop key="mail.smtp.sendpartial">true</prop>
</props>
</property>
</bean>
[ I thought to add here:
<bean id="anotherMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
...
<property name="username" value="another_email#gmail.com" />
<property name="password" value="***2***" />
...
</bean>
]
And a Java Class responsible for email sending:
public class MailService {
private static JavaMailSender mailSender;
#SuppressWarnings("static-access")
public void setMailSender(JavaMailSender mailSender) {
this.mailSender = mailSender;
}
public void sendMail(final String aSubject, final String aContent, final String toMail, final List<String> attachedFileUrls, String aFilename) {
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true,"UTF-8");
helper.setFrom(simpleMailMessage.getFrom());
helper.setTo(toMail);
helper.setSubject("A subject");
helper.setText("some content", true);
} catch (Exception e) {...}
Thread thread = new SendMail1(message);
thread.start();
}
class SendMail1 extends Thread {
MimeMessage message;
SendMail1(MimeMessage message) {
this.message = message;
}
public void run() {
mailSender.send(message);
}
}
}
(It doesn't help changing setFrom and setTo functions, because they only set the visual "to" and "from" in recipent's mail box)
At the moment mailSender "knows" somehow by the config settings above to send email to the email set in servlet-context.xml .
I would like to add sendMailFromSpecialSender function which will send email from other sender.
Is this possible?
If it is, then how?
UPDATE:
After posting this question I found a partial answer to my question by Bill Shannon:
The simple solution is to use a separate Session for each sender and send each message one at a time.
So...
1. How do I create a separate Session for my other sender case?
2. Does the configuration in servlet-context.xml enable having a separate session with other configuration, or can I leave that as it is?
Thank-you
Your question seems to be about Spring's dependency injection, and how to inject two beans of the same class.
One good way to do this is to use a #Qualifier:
public void setPrimaryMailSender(#Qualifier("primary") JavaMailSender mailSender) {
this.mailSender = mailSender;
}
public void setSecondaryMailSender(#Qualifier("secondary") JavaMailSender mailSender) {
this.secondaryMailSender = mailSender;
}
and then in your bean definitions:
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<qualifier value="primary"/>
...
</bean>
<bean id="anotherMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<qualifier value="secondary"/>
...
</bean>
In this way, you can easily inject two fields of the same type.
You just have to use another mailSender in your sendMail() method.
In that method you can have conditional check something like
if(condtion){
MimeMessage=mailSender.createMimeMessage();
else {
MimeMessage=otherSender.createMimeMessage()
}
Similar check in your inner class can help ypu to decide which sender to use.
After registration of a new user, generated hashed password in database don't match password from user input typed in order to authenticate. Raw passwords are the same but hashed versions are different. I wonder how to get these two match each other for proper authentication? I'm using Spring 4.3.2.RELEASE, and 4.2.0.RELEASE for Security.
Also I have a warning:
WARN SpringSecurityCoreVersion:78 - **** You are advised to use Spring 4.3.4.RELEASE or later with this version. You are running: 4.3.0.RELEASE
Maybe this is causing a problem in some way.
config.xml:
<bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
<security:password-encoder ref="encoder"/>
</security:authentication-provider>
</security:authentication-manager>
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userService" />
<property name="hideUserNotFoundExceptions" value="false" />
<property name="passwordEncoder" ref="encoder" />
</bean>
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<constructor-arg>
<ref bean="daoAuthenticationProvider" />
</constructor-arg>
</bean>
UserEntity.java:
public void setPassword(String password) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
this.password = passwordEncoder.encode(password);
}
UserAuthenticationProviderService.java:
public boolean processUserAuthentication(UserEntity user) {
try {
Authentication request = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
return true;
} catch(AuthenticationException e) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), "Catched Error!"));
return false;
}
}
EDIT: Solved.
As Shaun said problem was encoding in Entity class. After moving encoding to place of user creation everything works well, because encoding now appears only in user creation process. Thanks!
Yes, you noted right after 2 encoding of the same string by BCryptEncoder you will get different strings. But Spring Security doesn't use matching for equals. When you register you encoder, SPring Security would use boolean matches(CharSequence rawPassword, String encodedPassword) from PasswordEncoder(BCryptEncoder implements this interface).
If you are interesting about details, you can view implementation of BCrypt, its pretty simple:
static boolean equalsNoEarlyReturn(String a, String b) {
char[] caa = a.toCharArray();
char[] cab = b.toCharArray();
if (caa.length != cab.length) {
return false;
}
byte ret = 0;
for (int i = 0; i < caa.length; i++) {
ret |= caa[i] ^ cab[i];
}
return ret == 0;
}
Is it possible to generate multiple valid access tokens using the client_credentials or password grant type per request?
Generating a token using the above grant types only gives a new token when the current one expires per request.
I can use the password grant type to generate a refresh token and then generate multiple access tokens, but doing that will invalidate any previous access tokens.
Any idea how i could change to allow an access token to be generated per request to the /oauth/token endpoint and insure that any previous tokens are not invalidated?
Below is the XML configuration of my oauth server.
<!-- oauth2 config start-->
<sec:http pattern="/test/oauth/token" create-session="never"
authentication-manager-ref="authenticationManager" >
<sec:intercept-url pattern="/test/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<sec:anonymous enabled="false" />
<sec:http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
<sec:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
</sec:http>
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="clientDetailsUserService" />
</sec:authentication-manager>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<bean id="clientDetails" class="org.security.oauth2.ClientDetailsServiceImpl"></bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="springsec/client" />
<property name="typeName" value="Basic" />
</bean>
<bean id="oauthAccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>
<oauth:authorization-server
client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:authorization-code />
<oauth:implicit/>
<oauth:refresh-token/>
<oauth:client-credentials />
<oauth:password authentication-manager-ref="userAuthenticationManager"/>
</oauth:authorization-server>
<sec:authentication-manager id="userAuthenticationManager">
<sec:authentication-provider ref="customUserAuthenticationProvider">
</sec:authentication-provider>
</sec:authentication-manager>
<bean id="customUserAuthenticationProvider"
class="org.security.oauth2.CustomUserAuthenticationProvider">
</bean>
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="accessTokenValiditySeconds" value="300"></property>
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
<constructor-arg ref="jdbcTemplate" />
</bean>
<bean id="jdbcTemplate"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/oauthdb"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
</bean>
Updated on 21/11/2014
When I double check, I found that InMemoryTokenStore use a OAuth2Authentication's hash string as key of serveral Map. And when I use same username, client_id, scope.. and I got same key. So this may leading to some problem. So I think the old way are deprecated. The following is what I did to avoid the problem.
Create another AuthenticationKeyGenerator that can calculate unique key, called UniqueAuthenticationKeyGenerator
/*
* Copyright 2006-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
/**
* Basic key generator taking into account the client id, scope, resource ids and username (principal name) if they
* exist.
*
* #author Dave Syer
* #author thanh
*/
public class UniqueAuthenticationKeyGenerator implements AuthenticationKeyGenerator {
private static final String CLIENT_ID = "client_id";
private static final String SCOPE = "scope";
private static final String USERNAME = "username";
private static final String UUID_KEY = "uuid";
public String extractKey(OAuth2Authentication authentication) {
Map<String, String> values = new LinkedHashMap<String, String>();
OAuth2Request authorizationRequest = authentication.getOAuth2Request();
if (!authentication.isClientOnly()) {
values.put(USERNAME, authentication.getName());
}
values.put(CLIENT_ID, authorizationRequest.getClientId());
if (authorizationRequest.getScope() != null) {
values.put(SCOPE, OAuth2Utils.formatParameterList(authorizationRequest.getScope()));
}
Map<String, Serializable> extentions = authorizationRequest.getExtensions();
String uuid = null;
if (extentions == null) {
extentions = new HashMap<String, Serializable>(1);
uuid = UUID.randomUUID().toString();
extentions.put(UUID_KEY, uuid);
} else {
uuid = (String) extentions.get(UUID_KEY);
if (uuid == null) {
uuid = UUID.randomUUID().toString();
extentions.put(UUID_KEY, uuid);
}
}
values.put(UUID_KEY, uuid);
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
}
catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("MD5 algorithm not available. Fatal (should be in the JDK).");
}
try {
byte[] bytes = digest.digest(values.toString().getBytes("UTF-8"));
return String.format("%032x", new BigInteger(1, bytes));
}
catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 encoding not available. Fatal (should be in the JDK).");
}
}
}
Finally, wire them up
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
<constructor-arg ref="jdbcTemplate" />
<property name="authenticationKeyGenerator">
<bean class="your.package.UniqueAuthenticationKeyGenerator" />
</property>
</bean>
Below way may leading to some problem, see updated answer!!!
You are using DefaultTokenServices. Try this code and make sure to re-define your `tokenServices`
package com.thanh.backend.oauth2.core;
import java.util.Date;
import java.util.UUID;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
/**
* #author thanh
*/
public class SimpleTokenService extends DefaultTokenServices {
private TokenStore tokenStore;
private TokenEnhancer accessTokenEnhancer;
#Override
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
OAuth2RefreshToken refreshToken = createRefreshToken(authentication);;
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
tokenStore.storeRefreshToken(refreshToken, authentication);
return accessToken;
}
private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
if (validitySeconds > 0) {
token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
}
token.setRefreshToken(refreshToken);
token.setScope(authentication.getOAuth2Request().getScope());
return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
}
private ExpiringOAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
if (!isSupportRefreshToken(authentication.getOAuth2Request())) {
return null;
}
int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request());
ExpiringOAuth2RefreshToken refreshToken = new DefaultExpiringOAuth2RefreshToken(UUID.randomUUID().toString(),
new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
return refreshToken;
}
#Override
public void setTokenEnhancer(TokenEnhancer accessTokenEnhancer) {
super.setTokenEnhancer(accessTokenEnhancer);
this.accessTokenEnhancer = accessTokenEnhancer;
}
#Override
public void setTokenStore(TokenStore tokenStore) {
super.setTokenStore(tokenStore);
this.tokenStore = tokenStore;
}
}
Following #Thanh Nguyen Van approach:
I stumbled upon the same problem while developing my backend with Spring Boot and OAuth2. The problem I encountered was that, if multiple devices shared the same tokens, once one device refreshed the token, the other device would be clueless and, long story short, both devices entered in a token refresh frenzy. My solution was to replace the default AuthenticationKeyGenerator with a custom implementation which overrides DefaultAuthenticationKeyGenerator and adds a new parameter client_instance_id in the key generator mixture. My mobile clients would then send this parameter which has to be unique across app installs (iOS or Android). This is not a special requirement, since most mobile apps already track the application instance in some form.
public class EnhancedAuthenticationKeyGenerator extends DefaultAuthenticationKeyGenerator {
public static final String PARAM_CLIENT_INSTANCE_ID = "client_instance_id";
private static final String KEY_SUPER_KEY = "super_key";
private static final String KEY_CLIENT_INSTANCE_ID = PARAM_CLIENT_INSTANCE_ID;
#Override
public String extractKey(final OAuth2Authentication authentication) {
final String superKey = super.extractKey(authentication);
final OAuth2Request authorizationRequest = authentication.getOAuth2Request();
final Map<String, String> requestParameters = authorizationRequest.getRequestParameters();
final String clientInstanceId = requestParameters != null ? requestParameters.get(PARAM_CLIENT_INSTANCE_ID) : null;
if (clientInstanceId == null || clientInstanceId.length() == 0) {
return superKey;
}
final Map<String, String> values = new LinkedHashMap<>(2);
values.put(KEY_SUPER_KEY, superKey);
values.put(KEY_CLIENT_INSTANCE_ID, clientInstanceId);
return generateKey(values);
}
}
which you would then inject in a similar manner:
final JdbcTokenStore tokenStore = new JdbcTokenStore(mDataSource);
tokenStore.setAuthenticationKeyGenerator(new EnhancedAuthenticationKeyGenerator());
The HTTP request would then look something like this
POST /oauth/token HTTP/1.1
Host: {{host}}
Authorization: Basic {{auth_client_basic}}
Content-Type: application/x-www-form-urlencoded
grant_type=password&username={{username}}&password={{password}}&client_instance_id={{instance_id}}
The benefit of using this approach is that, if the client doesn't send a client_instance_id, the default key would be generated, and if an instance is provided, the same key is returned every time for the same instance. Also, the key is platform independent. The downside would be that the MD5 digest (used internally) is called two times.
Don't set any scope values at backend side keep empty and at the time of generating access token send sessionId or deviceId or any unique ID to scope , then Always you will get new Token For same client and user combination.
I need to convert dbcp2 java setup code into spring beans
The below code works as expected:
protected void setupDriver(String connectURI, String username, String password) throws ClassNotFoundException, SQLException{
//
// First, we'll create a ConnectionFactory that the
// pool will use to create Connections.
// We'll use the DriverManagerConnectionFactory,
// using the connect string passed in the command line
// arguments.
//
ConnectionFactory connectionFactory =
new DriverManagerConnectionFactory(connectURI, username, password);
//
// Next we'll create the PoolableConnectionFactory, which wraps
// the "real" Connections created by the ConnectionFactory with
// the classes that implement the pooling functionality.
//
poolableConnectionFactory =
new PoolableConnectionFactory(connectionFactory, null);
logger.info("poolableConnectionFactory created");
//
// Now we'll need a ObjectPool that serves as the
// actual pool of connections.
//
// We'll use a GenericObjectPool instance, although
// any ObjectPool implementation will suffice.
//
connectionPool =
new GenericObjectPool<PoolableConnection>(poolableConnectionFactory,getPoolConfig());
logger.info("connectionPool created");
// Set the factory's pool property to the owning pool
poolableConnectionFactory.setPool(connectionPool);
logger.info("connectionPool is set to poolableConnectionFactory");
//
// Finally, we create the PoolingDriver itself...
//
Class.forName("org.apache.commons.dbcp2.PoolingDriver");
driver = (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");
logger.info("dbcp2 driver is created");
//
// ...and register our pool with it.
//
driver.registerPool(poolName,connectionPool);
logger.info("driver is registered");
//
// Now, we create the PoolingDriver itself,
// passing in the object pool we created.
//
dataSource = new PoolingDataSource<PoolableConnection>(connectionPool);
logger.info("dataSource is created");
//
//Finally we create the JdbcTemplate for sql
//operations in DbDAO class
//
jdbcTemplate = new JdbcTemplate(dataSource);
logger.info("jdbcTemplate is setup");
logger.info("Finally dbcp2 driver setup is completed!");
}
//Pool initial setup values
private GenericObjectPoolConfig getPoolConfig(){
logger.info("Let's create the pool config values");
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(Integer.parseInt(config.getMaxtotal())); // set number of max connections i.e 25
poolConfig.setMaxWaitMillis(Long.parseLong(config.getMaxwaitmillis())); //ie. wait for a minute = 60000
poolConfig.setMaxIdle(Integer.parseInt(config.getMaxidle())); // set max number of idle connections
/*poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnReturn(true);*/
//poolConfig.setTestWhileIdle(true);
//poolConfig.setTimeBetweenEvictionRunsMillis(10000L);
//poolConfig.setNumTestsPerEvictionRun(5);
//poolConfig.setMinEvictableIdleTimeMillis(5000L);
return poolConfig;
}
And this is the beans.xml
<!-- ============ Trauma Database Connection Pooling Beans Settings ================== -->
<bean id="connectionFactory" class="org.apache.commons.dbcp2.DriverManagerConnectionFactory">
<constructor-arg index="0" value="${tir.jdbc.url}" />
<constructor-arg index="1" value="${tir.jdbc.username}" />
<constructor-arg index="2" value="${tir.jdbc.password}" />
</bean>
<!-- Connection Factory -->
<bean id="poolableConnectionFactory" class="org.apache.commons.dbcp2.PoolableConnectionFactory">
<constructor-arg index="0" ref="connectionFactory"/>
<constructor-arg index="1" > <null/> </constructor-arg>
</bean>
<!-- Pool Configs -->
<bean id="poolConfig" class="org.apache.commons.pool2.impl.GenericObjectPoolConfig">
<property name="maxTotal" value="${pool.maxtotal}"/>
<property name="maxIdle" value="${pool.maxidle}"/>
<property name="minIdle" value="${pool.minidle}"/>
<property name="maxWaitMillis" value="${pool.maxwaitmillis}"/>
</bean>
<!-- Connection Pool -->
<bean id="connectionPool" class="org.apache.commons.pool2.impl.GenericObjectPool">
<constructor-arg index="0" ref="poolableConnectionFactory"/>
<constructor-arg index="1" ref="poolConfig"/>
</bean>
<!-- Datasource gets connection pool -->
<bean id="dataSource" class="org.apache.commons.dbcp2.PoolingDataSource">
<constructor-arg ref="connectionPool"/>
</bean>
<!-- JdbcTemplate bean gets the datasource -->
<bean id="jdbcTemplateTIR" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>
<!-- Finally, we create our Database object bean -->
<bean id="dbdao" class="edu.uams.dao.impl.DBDAO">
<property name="jdbcTemplate" ref="jdbcTemplateTIR" />
</bean>
<!-- ============= END OF Trauma Database Connection Pooling Settings =========== -->
In both situations I can use the jDBCTemplate objects, however it gives the following warning:
"WARN 0[main] - org.apache.commons.dbcp2.PoolingDataSource.(PoolingDataSource.java:65) PoolableConnectionFactory not linked to pool. Calling setPool() to fix the configuration"
The reason is obvious: In my java code, I setup
poolableConnectionFactory.setPool(connectionPool);
How can I call setPool method from beans?
And how can I also setup this java code in my beans? The constructor of DriverManagerConnectionFactory does not get any constructor of DriverClassName?
// Finally, we create the PoolingDriver itself...
//
Class.forName("org.apache.commons.dbcp2.PoolingDriver");
driver = (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");
logger.info("dbcp2 driver is created");
//
// ...and register our pool with it.
//
driver.registerPool(poolName,connectionPool);
Wouldn't it be easier if you just used org.apache.commons.dbcp2.BasicDataSource? According to the documentation it provides a "one stop shopping" solution for basic connection pooling.
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${db.driver}"/>
<property name="url" value="${db.jdbcurl}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
<property name="initialSize" value="3"/>
</bean>
I was searching for a solution to exactly this problem given in the question with these specific versions of the libraries. I used the Spring configuration you gave and tweaked it a little bit, and I was able to get it working. Here is how I am using it...
<bean id="poolingDataSourceBean" class="org.apache.commons.dbcp2.PoolingDataSource">
<constructor-arg>
<bean id="genericObjectPoolBean" class="org.apache.commons.pool2.impl.GenericObjectPool">
<constructor-arg>
<bean id="poolableConnectionFactoryBean" class="org.apache.commons.dbcp2.PoolableConnectionFactory">
<constructor-arg index="0">
<bean id="dataSourceConnectionFactoryBean" class="org.apache.commons.dbcp2.DataSourceConnectionFactory">
<constructor-arg>
<bean id="dataSourceBean" class="net.sourceforge.jtds.jdbcx.JtdsDataSource">
<property name="serverName" value="${database.server}"/>
<property name="portNumber" value="${database.port}"/>
<property name="databaseName" value="${database.name}"/>
<property name="user" value="${database.user}"/>
<property name="password" value="${database.password}"/>
</bean>
</constructor-arg>
</bean>
</constructor-arg>
<constructor-arg index="1"><null/></constructor-arg>
</bean>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
You are getting PoolableConnectionFactory not linked to pool. Calling setPool() to fix the configuration" warning because PoolingDataSource has pooling defined while PoolableConnectionFactory does not. With spring you run into circular dependency: PoolableConnectionFactory needs GenericObjectPool that needs PoolableConnectionFactory in constructor.
One way to get around it is to use MethodInvokingFactoryBean (or MethodInvokingBean in spring 4.0+):
<bean id="connectionPoolSetter" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" >
<property name="targetObject" ref="poolableConnectionFactory" />
<property name="targetMethod" value="setPool"/>
<property name="arguments" ref="connectionPool" />
</bean>
then add depends-on attribute to dataSource to make sure connectionPoolSetter is initialized:
<bean id="dataSource" class="org.apache.commons.dbcp2.PoolingDataSource" depends-on="connectionPoolSetter">
<constructor-arg ref="connectionPool"/>
</bean>
Check Pooling Data Source example code:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/doc/PoolingDataSourceExample.java?view=markup
By the way, I also have a DBPoolManager which I can manage the pool itself, even printing the stats.. even I haven't find any solution yet, I hope this code will be useful for you..
import java.sql.Connection;
import java.sql.SQLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
public class DBPoolManager {
private static final Logger logger = LoggerFactory.getLogger(DBPoolManager.class);
private static DBPoolManager dbPoolManager = null;
private static final String POOLNAME = "dbpool";
private DBPoolDriver poolDriver = null;
private String url = null;
private String username = null;
private String password = null;
private static DriverStats stats = null;
protected DBPoolManager(){}
public void init(String url, String username, String password ){
this.url = url;
this.username = username;
this.password = password;
try{
this.poolDriver = new DBPoolDriver(POOLNAME);
}catch(Exception ex){
logger.error(ex.getMessage());
}
}
public static DBPoolManager getInstance(){
if(dbPoolManager == null){
synchronized(DBPoolManager.class){
if(dbPoolManager == null){
dbPoolManager = new DBPoolManager();
logger.info("Created a new singleton of DBPoolManager: "+dbPoolManager);
}
}
}
else{
logger.info("Created the same singleton of DBPoolManager: "+dbPoolManager);
}
return dbPoolManager;
}
public JdbcTemplate getJdbcTemplate(){
return poolDriver.getJdbcTemplate();
}
public Connection getConnection() throws Exception{
return poolDriver.getConnection();
}
public void setupPool() throws ClassNotFoundException, SQLException {
try{
poolDriver.setupDriver(url, username, password);
}
catch(Exception e){
logger.error(e.getMessage());
}
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMaxTotal() {
return Integer.parseInt(poolDriver.getConfig().getMaxtotal());
}
public void setMaxTotal(int maxTotal) {
poolDriver.getConfig().setMaxtotal(String.valueOf(maxTotal));
}
protected long getMaxWaitMillis() {
return Long.parseLong(poolDriver.getConfig().getMaxwaitmillis());
}
public void setMaxWaitMillis(long maxWaitMillis) {
poolDriver.getConfig().setMaxwaitmillis(String.valueOf(maxWaitMillis));
}
public int getMaxIdle() {
return Integer.parseInt(poolDriver.getConfig().getMaxidle());
}
public void setMaxIdle(int maxIdle) {
poolDriver.getConfig().setMaxidle(String.valueOf(maxIdle));
}
public int getNumActive() throws SQLException{
return poolDriver.getConnectionPool().getNumActive();
}
public int getNumIdle() throws SQLException{
return poolDriver.getConnectionPool().getNumIdle();
}
public void shutDownPool() throws SQLException{
poolDriver.close();
}
public void printDriverStats() throws Exception {
logger.info(Thread.currentThread().getName()+ " NumActive: " + dbPoolManager.getNumActive());
logger.info(Thread.currentThread().getName()+ " NumIdle: " + dbPoolManager.getNumIdle());
}
I am newbie to Spring and Shiro platforms.
I have two url sets /admin/-- and /vendor/--. Both client sets are authenticating with specific realms. I have extended ModularRealmAuthenticator class to choose correct realm for authenticating.
ModularRealmAuthenticator.java
#Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured();
MultiLoginAuthenticationToken mlat = null;
Realm loginRealm = null;
if (!(authenticationToken instanceof MultiLoginAuthenticationToken)) {
throw new AuthenticationException("Unrecognized token , not a typeof MultiLoginAuthenticationToken ");
} else {
mlat = (MultiLoginAuthenticationToken) authenticationToken;
logger.debug("realm name is : {}", mlat.getRealmName());
loginRealm = lookupRealm(mlat.getRealmName());
}
return doSingleRealmAuthentication(loginRealm, mlat);
}
protected Realm lookupRealm(String realmName) throws AuthenticationException {
Collection<Realm> realms = getRealms();
for (Realm realm : realms) {
if (realm.getName().equalsIgnoreCase(realmName)) {
logger.debug("look up realm name is : {}", realm.getName());
return realm;
}
}
throw new AuthenticationException("No realm configured for Client " + realmName);
}
But while I am assigning role and permissions from different set of datasource to both clients (Admin and vendor). It is iterating the realms in order which I have defined in applicationContext.xml file.
My ApplicationContext.xml
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="authenticator">
<bean class="com.yatra.mp.security.MultiLoginAuthenticator"/>
</property>
<!-- Single realm app (realm configured next, below). If you have multiple
realms, use the 'realms' property instead. -->
<property name="realms">
<util:list>
<ref bean="adminAuthRealm" />
<ref bean="vendorAuthRealm" />
</util:list>
</property>
<property name="cacheManager" ref="cacheManager" />
</bean>
In both of realms are extending AuthorizingRealm class and both have doGetAuthorizationInfo and doGetAuthenticationInfo method. In which I have defined my custom implementation.
Is it necessary to extend ModularRealmAuthorizer class? If yes, could you please tell me which method I have override?
What you can do is add the domain info to the PrincipalCollection you can wrap in the AuthenticationInfo. It is an added token in the principal collection that gets carried over in subsequent shiro calls. You can use that info in the authentication to skip if it doesn't match your realm. This is actually what we do in our custom realm:
public class OurRealmImpl extends AuthorizingRealm
...
#Override
public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
... //check if user exists and read passwordhash
Login ourLoginToken = ...
SimplePrincipalCollection principalCollection = new SimplePrincipalCollection(ourLoginToken, realmName);
return new SimpleAuthenticationInfo(principalCollection, passwordHash);
}
#Override
public AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Collection collection = principals.fromRealm(realmName);
if (collection.isEmpty()) {
return null;
}
Login login = (Login) collection.iterator().next();
... get the rights and return authorization
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permissionStrings);
return info;
}
i met that problem too. finally, i solved it. step as the follow:
1) let a new class to extend the ModularRealmAuthorizer.
public class OurModularRealmAuthorizer extend ModularRealmAuthorizer{
private map<string,OurAuthorizerRealm> mRealms;
private map<string,OurAuthorizerRealm> getMRealms(){return mRealms;}
private void setMRealms(map<string,OurAuthorizerRealm> mrealms){
this.mRealms = mrealms;
Collection<Realm> tmpRealms = new ArrayList<Realm>();
for (OurAuthorizerRealm value : mrealms.values()) {
Realm realm = (Realm) value;
tmpRealms.add(realm);
}
this.realms = tmpRealms;/*setting realms*/
}
}
2. spring-shiro.xml:
<bean id="ourModularRealmAuthorizer" class="xx.xxx.shiro.realm.ShiroModularRealmAuthorizer">
<property name="mRealms">
<map>
<entry key="ourAuthorizerRealm1" value-ref="ourAuthorizerRealm1" />
<entry key="ourAuthorizerRealm2" value-ref="ourAuthorizerRealm2" />
</map>
</property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="authenticator" ref="ourModularRealmAuthenticator"></property>
<property name="authorizer" ref="ouModularRealmAuthorizer"></property>
<property name="cacheManager" ref="shiroCacheManager"></property>
</bean>
all right.