Java 通过过滤器在Servlet中设置身份验证头
前言Java 通过过滤器在Servlet中设置身份验证头,java,servlets,servlet-filters,Java,Servlets,Servlet Filters,前言 package xxx.xxx.xxx.xxx.filters; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import jav
package xxx.xxx.xxx.xxx.filters;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import xxx.xxx.xxx.ConfigFile;
import xxx.xxx.xxx.Console;
import xxx.xxx.xxx.FalseException;
import xxx.xxx.activity.EncryptUtil;
public class AuthenticationFilter implements Filter {
public ConfigFile config;
public void init(FilterConfig arg0) throws ServletException {
config = new ConfigFile("C:/config.properties");
}
public void doFilter(ServletRequest sRequest, ServletResponse sResponse, FilterChain filterChain) throws IOException, ServletException {
Console.debug("AuthenticationFilter.doFilter() triggered.");
ServletRequestWrapper request = new ServletRequestWrapper((HttpServletRequest) sRequest);
HttpServletResponse response = (HttpServletResponse) sResponse;
HttpSession session = request.getSession();
Cookie cookie = null;
try {
if (request.getParameter("logout") != null) {
session.invalidate();
throw new FalseException("Logout recieved");
}
String auth = request.getHeader("Authorization");
if (auth == null) {
Console.debug("Authorization Header not found.");
// get cookie --COOKIE NAME--
Cookie[] cookies = request.getCookies();
if (cookies == null) {
throw new FalseException("Cookies not set.");
}
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].getName().equals(config.getProperty("authentication.cookie.name"))) {
cookie = cookies[i];
}
}
if (cookie == null) {
throw new FalseException("Cannot find Cookie (" + config.getProperty("authentication.cookie.name") + ") on Client");
}
Console.debug("Cookie (" + config.getProperty("authentication.cookie.name") + ") found on Client. value="+cookie.getValue());
String decToken = decryptToken(cookie.getValue());
Console.debug("Decrypted Token: "+decToken);
Console.debug("Setting Authorization Header...");
request.setAttribute("Authorization", decToken);
request.addHeader("Authorization", decryptToken(cookie.getValue()));
Console.debug("Authorization Header set.");
Console.debug("Validating Authorization Header value: "+request.getHeader("Authorization"));
}
}catch (FalseException e) {
Console.msg(e.getMessage() + ", giving the boot.");
response.sendRedirect(config.getProperty("application.login.url"));
} catch (Exception e) {
Console.error(e);
}
Console.debug("AuthenticationFilter.doFilter() finished.");
filterChain.doFilter(request, response);
}
public void destroy() {
}
private String decryptToken(String encToken) {
String token = null;
token = EncryptUtil.decryptFromString(encToken);
return token;
}
}
<web-app>
<filter>
<filter-name>AuthenticationFilter</filter-name>
<display-name>AuthenticationFilter</display-name>
<description></description>
<filter-class>com.xxx.xxx.xxx.filters.AuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>
package com.xxx.xxx.xxx.filters;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
public class ServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {
public ServletRequestWrapper(HttpServletRequest request) {
super(request);
headerMap = new HashMap();
}
private Map headerMap;
public void addHeader(String name, String value) {
headerMap.put(name, new String(value));
}
public Enumeration getHeaderNames() {
HttpServletRequest request = (HttpServletRequest) getRequest();
List list = new ArrayList();
for (Enumeration e = request.getHeaderNames(); e.hasMoreElements();) {
list.add(e.nextElement().toString());
}
for (Iterator i = headerMap.keySet().iterator(); i.hasNext();) {
list.add(i.next());
}
return Collections.enumeration(list);
}
public String getHeader(String name) {
Object value;
if ((value = headerMap.get("" + name)) != null)
return value.toString();
else
return ((HttpServletRequest) getRequest()).getHeader(name);
}
}
这是我第一次尝试过滤器,温柔一点
项目说明
我正试图为我们的几个应用程序完成SSO的构建,我似乎遇到了麻烦。我尝试连接到的webapp使用“Authentication”头来确定应用程序中的用户凭据。我已经构建了一个过滤器,希望在将标题传递到webapp之前设置它
问题
代码通过eclipse验证、编译、加载到Tomcat并传递到webapp。唯一缺少的是身份验证标头
我错过了什么/做错了什么
AuthenticationFilter源
package xxx.xxx.xxx.xxx.filters;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import xxx.xxx.xxx.ConfigFile;
import xxx.xxx.xxx.Console;
import xxx.xxx.xxx.FalseException;
import xxx.xxx.activity.EncryptUtil;
public class AuthenticationFilter implements Filter {
public ConfigFile config;
public void init(FilterConfig arg0) throws ServletException {
config = new ConfigFile("C:/config.properties");
}
public void doFilter(ServletRequest sRequest, ServletResponse sResponse, FilterChain filterChain) throws IOException, ServletException {
Console.debug("AuthenticationFilter.doFilter() triggered.");
ServletRequestWrapper request = new ServletRequestWrapper((HttpServletRequest) sRequest);
HttpServletResponse response = (HttpServletResponse) sResponse;
HttpSession session = request.getSession();
Cookie cookie = null;
try {
if (request.getParameter("logout") != null) {
session.invalidate();
throw new FalseException("Logout recieved");
}
String auth = request.getHeader("Authorization");
if (auth == null) {
Console.debug("Authorization Header not found.");
// get cookie --COOKIE NAME--
Cookie[] cookies = request.getCookies();
if (cookies == null) {
throw new FalseException("Cookies not set.");
}
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].getName().equals(config.getProperty("authentication.cookie.name"))) {
cookie = cookies[i];
}
}
if (cookie == null) {
throw new FalseException("Cannot find Cookie (" + config.getProperty("authentication.cookie.name") + ") on Client");
}
Console.debug("Cookie (" + config.getProperty("authentication.cookie.name") + ") found on Client. value="+cookie.getValue());
String decToken = decryptToken(cookie.getValue());
Console.debug("Decrypted Token: "+decToken);
Console.debug("Setting Authorization Header...");
request.setAttribute("Authorization", decToken);
request.addHeader("Authorization", decryptToken(cookie.getValue()));
Console.debug("Authorization Header set.");
Console.debug("Validating Authorization Header value: "+request.getHeader("Authorization"));
}
}catch (FalseException e) {
Console.msg(e.getMessage() + ", giving the boot.");
response.sendRedirect(config.getProperty("application.login.url"));
} catch (Exception e) {
Console.error(e);
}
Console.debug("AuthenticationFilter.doFilter() finished.");
filterChain.doFilter(request, response);
}
public void destroy() {
}
private String decryptToken(String encToken) {
String token = null;
token = EncryptUtil.decryptFromString(encToken);
return token;
}
}
<web-app>
<filter>
<filter-name>AuthenticationFilter</filter-name>
<display-name>AuthenticationFilter</display-name>
<description></description>
<filter-class>com.xxx.xxx.xxx.filters.AuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>
package com.xxx.xxx.xxx.filters;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
public class ServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {
public ServletRequestWrapper(HttpServletRequest request) {
super(request);
headerMap = new HashMap();
}
private Map headerMap;
public void addHeader(String name, String value) {
headerMap.put(name, new String(value));
}
public Enumeration getHeaderNames() {
HttpServletRequest request = (HttpServletRequest) getRequest();
List list = new ArrayList();
for (Enumeration e = request.getHeaderNames(); e.hasMoreElements();) {
list.add(e.nextElement().toString());
}
for (Iterator i = headerMap.keySet().iterator(); i.hasNext();) {
list.add(i.next());
}
return Collections.enumeration(list);
}
public String getHeader(String name) {
Object value;
if ((value = headerMap.get("" + name)) != null)
return value.toString();
else
return ((HttpServletRequest) getRequest()).getHeader(name);
}
}
调试日志
LoginServlet.doGet() triggered.
[DEBUG] : Authenticate.isClientLoggedIn() triggered.
xxx url : https://xxx.xxx.xxx/xxx/home.action
[DEBUG] : Authenticate.isClientLoggedIn() status code: 401
Unauthorized User.
Client IS NOT logged in.
-- Fill out Login Form, submit --
LoginServlet.doPost() triggered.
[DEBUG] : Authenticate.isClientLoggedIn() triggered.
xxx url : https://xxx.xxx.xxx./xxx/home.action
[DEBUG] : Authenticate.isClientLoggedIn() status code: 401
Unauthorized User.
Client IS NOT logged in.
Client (--USERID--) attempting basic authentication with password(--PASSWORD--).
[DEBUG] : BasicAuthentication.touch(http://localhost:PORT/vu/loginCheck.html, --USERID--, --PASSWORD--) triggered.
[DEBUG] : BasicAuthentication.touch() response code: 200
Client (--USERID--) has been logged IN.
Client (--USERID--) basic authentication finished, Client is logged in.
Client (--USERID--) logged in successfully.
[DEBUG] : Cookie (xxx_token) Set: 1e426f19ebdfef05dec6544307addc75401ecdc908a3c7e6df5336c744--SECRET--
[DEBUG] : Redirecting client to https://xxx.xxx.xxx/xxx/home.action
-- Redirected to webapp, filter recieves --
[DEBUG] : AuthenticationFilter.doFilter() triggered.
[DEBUG] : Authorization Header not found. << Initical check to see if user is already logged in to site
[DEBUG] : Cookie (xxx_token) found on Client. value=1e426f19ebdfef05dec6544307addc75401ecdc908a3c7e6df5336c744--SECRET--
[DEBUG] : Decrypted Token: Basic --SECRET--
[DEBUG] : Setting Authorization Header...
[DEBUG] : Authorization Header set.
[DEBUG] : Validating Authorization Header value: Basic --SECRET-- << Value matches Decrypted Token
[DEBUG] : AuthenticationFilter.doFilter() finished.
-- Web Application errors out, unable to find Authorization header
LoginServlet.doGet()已触发。
[调试]:已触发Authenticate.isClientLoggedIn()。
xxx网址:https://xxx.xxx.xxx/xxx/home.action
[调试]:Authenticate.isClientLoggedIn()状态代码:401
未经授权的用户。
客户端未登录。
--填写登录表,提交--
LoginServlet.doPost()已触发。
[调试]:已触发Authenticate.isClientLoggedIn()。
xxx网址:https://xxx.xxx.xxx./xxx/home.action
[调试]:Authenticate.isClientLoggedIn()状态代码:401
未经授权的用户。
客户端未登录。
客户端(--USERID--)正在尝试使用密码(--password--)进行基本身份验证。
[调试]:BasicAuthentication.touch(http://localhost:PORT/vu/loginCheck.html已触发、--USERID--、--PASSWORD--)。
[调试]:BasicAuthentication.touch()响应代码:200
客户端(--USERID--)已登录。
客户端(--USERID--)基本身份验证已完成,客户端已登录。
客户端(--USERID--)已成功登录。
[调试]:Cookie(xxx_令牌)集:1E426F19EBDFEF05DEC654307ADDC75401EDC908A3C7E6DF5336C744--机密--
[调试]:将客户端重定向到https://xxx.xxx.xxx/xxx/home.action
--已重定向到webapp,筛选器接收--
[调试]:已触发AuthenticationFilter.doFilter()。
[调试]:未找到授权标头 首先,最基本的问题(有点像“这是插入式的”问题),我假设您的cookie都植根于同一个域,并且您没有试图在这里获得跨域行为。因为饼干做不到
除了cookie测试之外,这看起来还不错。但这一切都取决于cookie测试
如果您想测试授权头,那么您可以简单地短路cookie测试(即它总是通过),并用一些有效值填充授权头。这将在短期内测试您的整个授权方案
完成/修复后,您可以专注于cookie设置和交付
我还假设您没有使用基于JavaEE容器的身份验证,而Tomcat会为您进行此检查。在这种情况下,过滤器只是“太晚了”。在调用过滤器之前,容器已经做出了决定
如果您使用的是基于容器的身份验证,并且您的应用程序位于同一个容器上,我可以想象Tomcat(或其他人)在容器级别有一个SSO选项。我知道玻璃鱼会为你做这件事的。如果是这样的话,修改Tomcat工件(即不是可移植的Java EE/Servlet机制)来实现这一点应该很简单。我添加了一个新的答案,因为它完全不同
我在我的系统上做了一个测试。我复制了您的代码,转储了cookie测试,并编写了一个简单的Servlet来为我转储内容
除了一个警告,它工作得很好
我不知道你的应用程序是如何使用它的。但是您的ServletRequestWrapper
实现了getHeaderNames
和getHeader
,但它没有实现getHeaders
。我在使用getHeaders
尝试转储请求时遇到了这个问题,当然,授权丢失了
因此,您可能希望更仔细地查看代码,看看它是否确实没有使用getHeaders
。如果是,它将“正常工作”,但完全跳过您所做的工作,从而错过您的授权标题
这是我的实现,它为我工作
@Override
public Enumeration getHeaders(String name) {
Enumeration e = super.getHeaders(name);
if (e != null && e.hasMoreElements()) {
return e;
} else {
List l = new ArrayList();
if (headerMap.get(name) != null) {
l.add(headerMap.get(name));
}
return Collections.enumeration(l);
}
}
只是一些建议;请不要抛出Throwable,这是为大量错误保留的,通常只有JVM在万不得已的情况下才能捕捉到这些错误,并且永远不会在过滤器中捕捉Throwable或异常,而不重新抛出它们。@Andrew White,我会记住这一点。我试图将我的过程和错误分开,而不使用一堆丑陋的if语句。这并不能原谅我的行为,只是解释一下。你说这是编译的,但我在ServletRequestWrapper上找不到addHeader()方法。@HighlyAffinated:很抱歉,我在帖子中没有包含自定义包装类。我现在将添加它。好的,您是如何得出缺少标题的结论的?您是否在感兴趣的webapp中调试了request.getHeader()
?是Web应用程序正在接收的ServletRequestWrapper
实例的请求吗?答案很好。我还想指出,明智的做法是使用应用服务器提供的用于身份验证和SSO的设施。它们可能会得到更好的测试和更广泛的审查。我特别担心提到的EncryptionUtils。通常我会同意你们的观点,但项目要求规定登录必须支持现有的用户管理,这恰好是基本的身份验证。应用程序本身使用标题中的信息来填充站点上的信息。我不是建议您更改为基于容器的身份验证。但是,需要明确的是,容器确实支持基本身份验证。但是您必须更改应用程序以使用Servlet安全接口(isUserInRole、getPrincipal等)。这就是筛选的原因,因为我无法(轻松)更改应用程序。最终,该项目将移动到哈希表,但在此同时