I have a web app running on Tomcat 7. Currently users login with passwords through JDBCRealm.
In my organization there's an authentication server. Users send HTTP requests to that server, the server authenticates them somehow, and then forwards the request to my app with a custom HTTP header that specifies the authenticated username (no password).
I'd like to use this mechanism for authenticating users to my app, while keeping the JDBC realm. When users send a request to /login, their request will be authenticated using the headers, and they'll be redirected to the main page as if they logged in using the standard j_security_check form, but without having to provide a password.
Here's what I came up with so far:
@WebFilter("/login/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) req;
HttpServletResponse httpResponse = (HttpServletResponse) res;
String username = ... // Extract username from httpRequest, not an issue
httpRequest.login(username, "???"); // Need to provide a password!
httpResponse.sendRedirect(httpRequest.getContextPath() + "/pages/home.xhtml");
}
}
This works when I provide the right password for JDBCRealm, but the password is unavailable in the filter.
Update: I ended up solving this with a custom valve:
public class LoginValve extends ValveBase {
@Override
public void invoke(Request req, Response res) throws IOException, ServletException {
if (!req.getRequestURI().equals(req.getContextPath() + "/login")) {
getNext().invoke(req, res);
return;
}
Session session = req.getSessionInternal();
if (session.getPrincipal() == null) {
String username = ... // From req
List<String> roles = ... // From req
session.setPrincipal(new GenericPrincipal(username, null, roles));
}
req.setUserPrincipal(session.getPrincipal());
getNext().invoke(req, res);
}
And I do the redirect from a filter that's called later.
I'm three reputation points shy of being able to comment, so I'll have to answer based on the given information, filling in some of the blanks with guesses.
You can achieve the "authentication" by subclassing javax.servlet.http.HttpServletRequestWrapper.
Override the getRemoteUser() and getUserPrincipal() so they return the authenticated user's username, and a Principal object that can be basically anything you want to make it. A JMXPrincipal should be convenient to use. You should probably also override getAuthType() to return HttpServletRequest.FORM_AUTH to make everything mesh nicely. And implement a quick logout() method per the Javadoc, which says "Establish null as the value returned when getUserPrincipal, getRemoteUser, and getAuthType is called on the request."
Then, in your filter, wrap the incoming HttpServletRequest in your wrapper object, and pass the wrapper object into chain.doFilter().
If you want to get fancy you can override the login() method also on the wrapper, where it accepts any (or a null) password and creates the internal instance variables that will be returned by getRemoteUser(), getUserPrincipal() and getAuthType().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With