我有一个在Tomcat7上运行的web应用程序。目前用户通过JDBCRealm使用密码登录。
在我的组织中有一个身份验证服务器。用户向该服务器发送HTTP请求,服务器以某种方式对其进行身份验证,然后使用自定义HTTP标头将请求转发到我的应用程序,该标头指定经过身份验证的用户名(无密码)。
我想使用这种机制对我的应用程序的用户进行身份验证,同时保留JDBC领域。当用户向/login
发送请求时,他们的请求将使用标头进行身份验证,并且他们将被重定向到主页,就像他们使用标准j_security_check
表单登录一样,但不必提供密码。
以下是我到目前为止的想法:
@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");
}
}
当我为JDBCRealm提供了正确的密码,但密码在过滤器中不可用时,这就起作用了。
更新:我最终用一个自定义阀门解决了这个问题:
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);
}
我从一个稍后调用的过滤器中进行重定向。
我还差三分就可以发表评论了,所以我必须根据给定的信息回答,用猜测填补一些空白。
您可以通过子类化javax.servlet.http.HttpServletRequestWrapper.来实现"身份验证"
重写getRemoteUser()和getUserPrincipal(),以便它们返回经过身份验证的用户的用户名,以及一个Principal对象,该对象基本上可以是您想要创建的任何对象。JMXPrincipal应该很方便使用。您可能还应该重写getAuthType()以返回HttpServlet请求.FORM_AUTH,从而使所有内容都很好地网格化。并根据Javadoc实现一个快速注销()方法,该方法表示"在请求中调用getUserPrincipal、getRemoteUser和getAuthType时,将null设置为返回的值。"
然后,在过滤器中,将传入的HttpServlet请求包装在包装器对象中,并将包装器对象传递到chain.doFilter().中
如果你想获得灵感,你可以在包装器上覆盖login()方法,它接受任何(或null)密码,并创建将由getRemoteUser()、getUserPrincipal()和getAuthType()返回的内部实例变量。