Ich arbeite an einer unordentlichen Struts 1-Anwendung, die eine benutzerdefinierte Kontextklasse verwendet, um Werte in der gesamten Anwendung zu speichern. Im Grunde wird es nur zum Speichern von Sitzungsbereichsvariablen verwendet. Ich vermute, der Grund, dass diese benutzerdefinierte Klasse verwendet wird, ist, dass andere Klassen, die keinen Zugriff auf die HTTP-Sitzung haben, die Sitzungsvariablen abrufen und festlegen können.ThreadLocale-Wert vermischt sich in Servlet-Filter
Wie auch immer, das funktioniert in den meisten Fällen gut. Der benutzerdefinierte Kontext wird in allen Aktionen und Serviceklassen verwendet, um Variablen problemlos zu teilen. Wie auch immer, ich habe gerade herausgefunden, dass es mit diesem benutzerdefinierten Kontext in einem HTTP-Filter nicht so gut funktioniert! Es scheint, dass es zufällig den Wert von einer anderen Sitzung zieht. Und nach der Sitzung meine ich eigentlich thread, da dieser benutzerdefinierte Kontext ThreadLocale verwendet, um seine schmutzige Arbeit zu erledigen.
Werfen Sie einen Blick
package com.zero.alpha.common;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
public final class CustomContext implements Serializable {
private static final long serialVersionUID = 400312938676062620L;
private static ThreadLocal<CustomContext> local = new ThreadLocal() {
protected CustomContext initialValue() {
return new CustomContext("0", "0", Locale.getDefault());
}
};
private String dscId;
private String sessionId;
private Locale locale;
private Map<String, Serializable> generalArea;
public CustomContext(String dscId, String sessionId, Locale locale) {
this.dscId = dscId;
this.sessionId = sessionId;
if (locale != null) {
this.locale = locale;
} else {
this.locale = Locale.getDefault();
}
this.generalArea = new Hashtable();
}
public static CustomContext get() {
return ((CustomContext) local.get());
}
public static void set(CustomContext context) {
local.set(context);
}
public String getDscId() {
return this.dscId;
}
public String getSessionId() {
return this.sessionId;
}
public Locale getLocale() {
return this.locale;
}
public Serializable getGeneralArea(String key) {
return ((Serializable) this.generalArea.get(key));
}
public Serializable putGeneralArea(String key, Serializable value) {
return ((Serializable) this.generalArea.put(key, value));
}
public void clearGeneralArea() {
this.generalArea.clear();
}
public Serializable removeGeneralArea(String key) {
return ((Serializable) this.generalArea.remove(key));
}
}
Wieder scheint dies nur schön und gut innerhalb jeder anderen Klasse anders als ein Filter zu arbeiten. Lass mich dir den Filter zeigen, wo er versagt.
package com.zero.alpha.myapp.common.filter;
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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.zero.alpha.common.CustomContext;
import com.zero.alpha.myapp.utility.CommonConstants;
import com.zero.alpha.myapp.utility.CommonHelpers;
import com.zero.alpha.myapp.UserDomain;
public class LoginFilter implements Filter {
public LoginFilter() {
}
public void init(FilterConfig config) throws ServletException {}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// Don't use the login filter during a login or logout request
if (req.getServletPath().equals("/login.do")
|| req.getServletPath().equals("/login-submit.do")
|| req.getServletPath().equals("/logout.do")) {
chain.doFilter(request, response);
} else {
doFilter(req, (HttpServletResponse) response, chain);
}
}
protected void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpSession session = request.getSession(false);
// This is the problem right here. Sometimes this will grab the value of a different user currently logged in
UserDomain user = (UserDomain) CustomContext.get()
.getGeneralArea(CommonConstants.ContextKey.USER_SESSION);
if (session == null || user == null) {
// Unauthorized
response.sendRedirect(loginPage);
} else {
// Authorized
session.setAttribute("userInfo", CommonHelpers.getUserDisplay(user));
chain.doFilter(request, response);
}
}
}
Wenn der benutzerdefinierte Kontext den Benutzer in dem doFilter Verfahren zu greifen verwendet wird, es wird das Benutzerobjekt von einem anderen angemeldeten Benutzern zufällig greifen. Offensichtlich keine gute Situation!
Die einzige Zeit, die dies passiert, ist nach einigen Aktivitäten von einem anderen angemeldeten Benutzer. Ich könnte den ganzen Tag dort sitzen und die Sitzung von Benutzer A auffrischen und es würde kein Problem geben. Nachdem Sie jedoch eine Aktion als Benutzer B ausgeführt und dann die Sitzung von Benutzer A erneut aktualisiert haben, wird sie normalerweise ausgetauscht. Aber wenn ich die Sitzung von Benutzer A erneut aktualisiere, sind die Dinge wieder normal.
Ich habe bemerkt, dass dies extrem häufiger passiert, wenn die Anwendung tatsächlich auf einem Remote-Entwicklungs-Tomcat-Server bereitgestellt wird. Es passiert immer noch lokal, aber nicht annähernd so oft wie bei der Remote-Bereitstellung. Es passiert fast 100% der Zeit aus der Ferne.
Ich habe die Sitzungsvariable innerhalb des Filters untersucht, und es scheint kein Problem mit der Sitzung selbst zu sein. Ich habe bestätigt, dass die Sitzungs-ID auch dann noch korrekt ist, wenn der falsche Benutzer aus der ThreadLocale-Variable gezogen wird.
Kann mir jemand helfen? Vielen Dank.
Sind Sie sicher, dass Aktionen von Benutzer b nicht auf die benutzerdefinierte Kontextinstanz zugreifen? – efekctive
@efekctive Die Aktionen von Benutzer B verwenden den gleichen benutzerdefinierten Kontext wie Benutzer A auf genau dieselbe Weise. – zero01alpha