Results 1 to 6 of 6

Thread: passing authentication information to datasource

  1. #1
    Join Date
    Nov 2011
    Posts
    7

    Default passing authentication information to datasource

    What is the right approach for passing authentication information to a datasource? This is needed in order to obtain a database connection using the user authentication information. The database connection is expected to be obtained for each call to getConnection() method of the DataSource. Currently, I am using a SecurityContext to get the AuthenticationToken, however, using a ThreadLocal was suggested on http://stackoverflow.com/questions/8...urce-in-spring

    Here is the code for using the security context:

    Code:
    public class CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    	public Authentication attemptAuthentication(){
    
    		CustomAuthenticationToken result = new CustomAuthenticationToken(
    				username, password, database, authorities);
    		
    		return result;
    	}
    }
    
    public class CustomDataSource extends DriverManagerDataSource{
    	
    	protected Connection getConnectionFromDriverManager(String url,
    			Properties props) throws SQLException {
    		
    		// option 1 using the security context
    		SecurityContext securityContext = SecurityContextHolder.getContext();
    		Authentication authentication = securityContext.getAuthentication();
    		if (authentication != null){
    			Object principal = authentication.getPrincipal();
    			Object credentials = authentication.getCredentials();
    			Connection conn = getConn(principal, credentials);
    			return conn;
    		}
    		return null;
    	}
    }
    and here is the code for using a ThreadLocal:

    Code:
    public interface WebUtils{
        public static final ThreadLocal<AuthInfo> authInfo = new ThreadLocal<AuthInfo>();
    }
    
    public class CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    	public Authentication attemptAuthentication(){
    
    		CustomAuthenticationToken result = new CustomAuthenticationToken(
    				username, "", database, authorities);
    		
    		AuthInfo info = new AuthInfo();
    		info.setName(username);
    		info.setPass(password);
    		
    		WebAttributes.authInfo.set(info);
    		
    		return result;
    	}
    }
    
    public class CustomDataSource extends DriverManagerDataSource{
    	
    	protected Connection getConnectionFromDriverManager(String url,
    			Properties props) throws SQLException {
    		
    		// option 1 using ThreadLocal
    		AuthInfo info = WebAttributes.authInfo.get();
    		Connection conn = getConnFromAuthInfo(info);
    		return conn;
    
    	}
    }

  2. #2
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,629

    Default

    I suggest a forum search and a read of the javadocs of spring... Spring already has support for this, the only thing you need to do is write a Filter which sets the right username/password for the current request (and that code/answer is here on the forum multiple times).

    Take a look at the UserCredentialsDataSourceAdapter (that wraps around any datasource). Register it in the context, create a filter which calls the setCredentialsForCurrentThread method and when processing is over calls removeCredentialsForCurrentThread. No reason to implement your own, just a filter. This filter needs to be after the spring security filter that sets the security context.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  3. #3
    Join Date
    Nov 2011
    Posts
    7

    Default

    Thanks Marten for your help

    I tried using the setCredentialsForCurrentThread before but it did not work. I had to use setUsername and setPassword methods in order for the Properties parameter passed to getConnection() to be populated.

    Code:
    	public Authentication attemptAuthentication(){
    		// setCredentialsForCurrentThread does not work
    		dataSourceAdapter.setCredentialsForCurrentThread(username, password);
    		// had to use setter methods
    		dataSourceAdapter.setUsername(username);
    		dataSourceAdapter.setPassword(password);
    	}
    If a new filter is defined, this mean the credentials need to be obtained from the SecurityContext for each request, right?
    Also, how and when should the credentials be reset?

  4. #4
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,629

    Default

    Don't use setUsername and/or setPassword!!!! That sets the global username/password which is used by EVERYONE unless you want everyone to use the same credentials (or swap credentials in the middle of a transaction!).

    Also as I stated you should obtain the username/password based on the security context, unless you also need to have the username/password available for authentication. However it will only work BEFORE you get a connection so make sure that you use the setCredentialsForCurrentThread BEFORE any attempt is made to get a connection.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  5. #5
    Join Date
    Nov 2011
    Posts
    7

    Default

    Thanks Marten, setCredentialsForCurrentThread works now

    I defined a new filter with the data source as a property

    Code:
    	<http entry-point-ref="authenticationEntryPoint" auto-config="false">
    		<custom-filter position="FORM_LOGIN_FILTER" ref="customAuthenticationFilter"/>
    		<custom-filter after="FORM_LOGIN_FILTER" ref="customSecurityFilter"/>
    	</http>
    
    	<b:bean id="customDataSourceAdapter" class="org.springframework.jdbc.datasource.UserCredentialsDataSourceAdapter">
            	<b:property name="targetDataSource" ref="targetDataSource" />
    	</b:bean>
    
    	<b:bean id="customSecurityFilter" class="com.package.security.CustomSecurityFilter">
    		<b:property name="customDataSourceAdapter" ref="customDataSourceAdapter" />
    	</b:bean>
    The new filter obtains the username/password based on the security context

    Code:
    public class CustomSecurityFilter implements Filter {
    
    	private UserCredentialsDataSourceAdapter customDataSourceAdapter;
    
    	@Override
    	public void doFilter(){
    
    		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    
    		if (authentication != null) {
    			customDataSourceAdapter.setCredentialsForCurrentThread(
    					authentication.getPrincipal().toString(),
    					authentication.getCredentials().toString());
    		}
    
    		chain.doFilter(request, response);
    
    		customDataSourceAdapter.removeCredentialsFromCurrentThread();
    	}
    Is there a simple way to pass additional properties such as the database URL? Would extending the UserCredentialsDataSourceAdapter be the right way?

  6. #6
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,629

    Default

    If you also want to use a url and other properties your only option is to create a new datasource each time you need a connection. The url is a property on a datasource and is not passed to a connection. So not sure if that is something (smart?) to do as creating a datasource might be a cumbersome operation.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •