Announcement Announcement Module
Collapse
No announcement yet.
passing authentication information to datasource Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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
    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.

    Comment


    • #3
      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?

      Comment


      • #4
        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.

        Comment


        • #5
          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?

          Comment


          • #6
            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.

            Comment

            Working...
            X