Authentication
HTTP Basic Authentication in Rails
Many services you will access are going to require authentication of some sort. We can quickly add basic authentication to our rails application from Serve It Up by making app/controllers/application_controller.rb look like this:
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
protect_from_forgery # See ActionController::RequestForgeryProtection for details
before_filter :authenticate
private
def authenticate
authenticate_or_request_with_http_basic do |id, password|
id == "Ace" && password == "newenglandclamchowder"
end
end
end
Of course you would never hard code user id's and passwords in a real application, and you would always salt and hash your passwords before storing them in your database right? If you thought about breakfast when I said salt and hash then I encourage you to do a little research. There's a decent article on Password cracking on Wikipedia to get you started. If you decide to use MD5 to hash your passwords, you might as well just post them on facebook.
There are lots of plugins and stuff for rails that make this easier. Did I mention that only idiots store plaintext passwords? Please use a strong hash for password storage, the best one out there is bcrypt, designed by Niels Provos and David Mazieres of the OpenBSD team. It's a special purpose user password scheme implemented by Smart People. There is of course a ruby implementation of bcrypt. In Rails 3.1, you get password encryption baked into ActiveModel (using bcrypt of course).
Of course you would also never use HTTP Basic authentication without also using TLS or SSL, because HTTP Basic sends the username and password in the clear.
Security rant done.
HTTP Basic Authentication in jactiveresource
With these changes made to the rails service, jactiveresource will now start throwing UnauthorizedAccess exceptions when it tries to access it. So let's provide some credentials to jactiveresource.
ResourceConnection c = new ResourceConnection("http://localhost:3000");
c.setUsername("Ace");
c.setPassword("newenglandclamchowder");
You might be tempted to do something like:
ResourceConnection c = new ResourceConnection("http://Ace:newenglandclamchowder@localhost:3000");
The problem with embedding these into the URI is that if your password is "h/f9#:b2" then your in barney. So don't do it: if you do, the credentials will be ignored.
Other kinds of authentication in jactiveresource
Say your rails app has a mechanism for authentication that relies on HTTP Cookies. For example, your Rails app might expect a cookie to be set with a particular value, and it then grants access to the resource instead of using HTTP Basic authentication. Once you have acquired the cookie (jactiveresource doesn't help with that), you need a way to get your ResourceConnection object to use it. To do so, you need to create your own HttpClientFactory.
A HttpClientFactory is the thing that a ResourceConnection calls to get HttpClient objects, which it then uses to talk to your rails app. You can either create a new class that implements the AbstractHttpClientFactory interface, or you can subclass the default factory. For this example, we will choose the subclass method. So we create a new class:
import java.net.URL;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
public class CookieFactory extends DefaultHttpClientFactory {
private BasicCookieStore bcs;
public void setCookieStore(BasicCookieStore bcs) {
this.bcs = bcs;
}
public BasicCookieStore getCookieStore() {
return this.bcs;
}
@Override
public DefaultHttpClient getHttpClient(URL site) {
DefaultHttpClient c = super.getHttpClient(site);
c.setCookieStore(bcs);
return c;
}
}
And magically get the cookie (you figure that part out), and tell the resource connection to use the cookie factory to make HttpClient? objects:
BasicCookieStore myCookieStore = new BasicCookieStore();
// here invoke magic to get the cookie and add it to the cookie store
CookieFactory f = new CookieFactory();
f.setCookieStore(myCookieStore);
ResourceConnection c = new ResourceConnection("http://localhost:3000");
c.setHttpClientFactory(f);
