|
@@ -0,0 +1,254 @@
|
|
|
|
+package com.jeeplus.security.config;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * @author: 王强
|
|
|
|
+ * @create: 2023-04-25 16:19
|
|
|
|
+ **/
|
|
|
|
+import java.util.Arrays;
|
|
|
|
+import java.util.Collections;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.zip.Inflater;
|
|
|
|
+import javax.servlet.ServletException;
|
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
|
+import javax.servlet.http.HttpSession;
|
|
|
|
+import javax.xml.bind.DatatypeConverter;
|
|
|
|
+import org.jasig.cas.client.Protocol;
|
|
|
|
+import org.jasig.cas.client.configuration.ConfigurationKeys;
|
|
|
|
+import org.jasig.cas.client.session.HashMapBackedSessionMappingStorage;
|
|
|
|
+import org.jasig.cas.client.session.SessionMappingStorage;
|
|
|
|
+import org.jasig.cas.client.util.CommonUtils;
|
|
|
|
+import org.jasig.cas.client.util.XmlUtils;
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
+
|
|
|
|
+public final class SingleSignOutHandler {
|
|
|
|
+ private static final int DECOMPRESSION_FACTOR = 10;
|
|
|
|
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
|
|
|
+ private SessionMappingStorage sessionMappingStorage = new HashMapBackedSessionMappingStorage();
|
|
|
|
+ private String artifactParameterName;
|
|
|
|
+ private String logoutParameterName;
|
|
|
|
+ private String relayStateParameterName;
|
|
|
|
+ private String casServerUrlPrefix;
|
|
|
|
+ private String logoutCallbackPath;
|
|
|
|
+ private boolean artifactParameterOverPost;
|
|
|
|
+ private boolean eagerlyCreateSessions;
|
|
|
|
+ private List<String> safeParameters;
|
|
|
|
+ private final LogoutStrategy logoutStrategy;
|
|
|
|
+
|
|
|
|
+ public SingleSignOutHandler() {
|
|
|
|
+ this.artifactParameterName = Protocol.CAS2.getArtifactParameterName();
|
|
|
|
+ this.logoutParameterName = (String)ConfigurationKeys.LOGOUT_PARAMETER_NAME.getDefaultValue();
|
|
|
|
+ this.relayStateParameterName = (String)ConfigurationKeys.RELAY_STATE_PARAMETER_NAME.getDefaultValue();
|
|
|
|
+ this.casServerUrlPrefix = "";
|
|
|
|
+ this.artifactParameterOverPost = false;
|
|
|
|
+ this.eagerlyCreateSessions = true;
|
|
|
|
+ this.logoutStrategy = (LogoutStrategy)(isServlet30() ? new Servlet30LogoutStrategy() : new Servlet25LogoutStrategy());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void setSessionMappingStorage(SessionMappingStorage storage) {
|
|
|
|
+ this.sessionMappingStorage = storage;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void setArtifactParameterOverPost(boolean artifactParameterOverPost) {
|
|
|
|
+ this.artifactParameterOverPost = artifactParameterOverPost;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public SessionMappingStorage getSessionMappingStorage() {
|
|
|
|
+ return this.sessionMappingStorage;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void setArtifactParameterName(String name) {
|
|
|
|
+ this.artifactParameterName = name;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void setLogoutParameterName(String name) {
|
|
|
|
+ this.logoutParameterName = name;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void setCasServerUrlPrefix(String casServerUrlPrefix) {
|
|
|
|
+ this.casServerUrlPrefix = casServerUrlPrefix;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void setLogoutCallbackPath(String logoutCallbackPath) {
|
|
|
|
+ this.logoutCallbackPath = logoutCallbackPath;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void setRelayStateParameterName(String name) {
|
|
|
|
+ this.relayStateParameterName = name;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void setEagerlyCreateSessions(boolean eagerlyCreateSessions) {
|
|
|
|
+ this.eagerlyCreateSessions = eagerlyCreateSessions;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public synchronized void init() {
|
|
|
|
+ if (this.safeParameters == null) {
|
|
|
|
+ CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
|
|
|
|
+ CommonUtils.assertNotNull(this.logoutParameterName, "logoutParameterName cannot be null.");
|
|
|
|
+ CommonUtils.assertNotNull(this.sessionMappingStorage, "sessionMappingStorage cannot be null.");
|
|
|
|
+ CommonUtils.assertNotNull(this.relayStateParameterName, "relayStateParameterName cannot be null.");
|
|
|
|
+ CommonUtils.assertNotNull(this.casServerUrlPrefix, "casServerUrlPrefix cannot be null.");
|
|
|
|
+ if (CommonUtils.isBlank(this.casServerUrlPrefix)) {
|
|
|
|
+ this.logger.warn("Front Channel single sign out redirects are disabled when the 'casServerUrlPrefix' value is not set.");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (this.artifactParameterOverPost) {
|
|
|
|
+ this.safeParameters = Arrays.asList(this.logoutParameterName, this.artifactParameterName);
|
|
|
|
+ } else {
|
|
|
|
+ this.safeParameters = Collections.singletonList(this.logoutParameterName);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private boolean isTokenRequest(HttpServletRequest request) {
|
|
|
|
+ return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.artifactParameterName, this.safeParameters));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private boolean isLogoutRequest(HttpServletRequest request) {
|
|
|
|
+ if (!"POST".equalsIgnoreCase(request.getMethod())) {
|
|
|
|
+ return "GET".equalsIgnoreCase(request.getMethod()) ? CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters)) : false;
|
|
|
|
+ } else {
|
|
|
|
+ return !this.isMultipartRequest(request) && this.pathEligibleForLogout(request) && CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private boolean pathEligibleForLogout(HttpServletRequest request) {
|
|
|
|
+ return this.logoutCallbackPath == null || this.logoutCallbackPath.equals(this.getPath(request));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private String getPath(HttpServletRequest request) {
|
|
|
|
+ return request.getServletPath() + CommonUtils.nullToEmpty(request.getPathInfo());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public boolean process(HttpServletRequest request, HttpServletResponse response) {
|
|
|
|
+ if (this.isTokenRequest(request)) {
|
|
|
|
+ this.logger.trace("Received a token request");
|
|
|
|
+ this.recordSession(request);
|
|
|
|
+ return true;
|
|
|
|
+ } else if (this.isLogoutRequest(request)) {
|
|
|
|
+ //第二种情况, 其他客户端退出
|
|
|
|
+ TestCas.name = "logout";
|
|
|
|
+ TestCas.is = true;
|
|
|
|
+
|
|
|
|
+ this.logger.trace("Received a logout request");
|
|
|
|
+ this.destroySession(request);
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+ } else {
|
|
|
|
+ this.logger.trace("Ignoring URI for logout: {}", request.getRequestURI());
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void recordSession(HttpServletRequest request) {
|
|
|
|
+ HttpSession session = request.getSession(this.eagerlyCreateSessions);
|
|
|
|
+ if (session == null) {
|
|
|
|
+ this.logger.debug("No session currently exists (and none created). Cannot record session information for single sign out.");
|
|
|
|
+ } else {
|
|
|
|
+ String token = CommonUtils.safeGetParameter(request, this.artifactParameterName, this.safeParameters);
|
|
|
|
+ this.logger.debug("Recording session for token {}", token);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ this.sessionMappingStorage.removeBySessionById(session.getId());
|
|
|
|
+ } catch (Exception var5) {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.sessionMappingStorage.addSessionById(token, session);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private String uncompressLogoutMessage(String originalMessage) {
|
|
|
|
+ byte[] binaryMessage = DatatypeConverter.parseBase64Binary(originalMessage);
|
|
|
|
+ Inflater decompresser = null;
|
|
|
|
+
|
|
|
|
+ String var6;
|
|
|
|
+ try {
|
|
|
|
+ decompresser = new Inflater();
|
|
|
|
+ decompresser.setInput(binaryMessage);
|
|
|
|
+ byte[] result = new byte[binaryMessage.length * 10];
|
|
|
|
+ int resultLength = decompresser.inflate(result);
|
|
|
|
+ var6 = new String(result, 0, resultLength, "UTF-8");
|
|
|
|
+ } catch (Exception var10) {
|
|
|
|
+ this.logger.error("Unable to decompress logout message", var10);
|
|
|
|
+ throw new RuntimeException(var10);
|
|
|
|
+ } finally {
|
|
|
|
+ if (decompresser != null) {
|
|
|
|
+ decompresser.end();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return var6;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void destroySession(HttpServletRequest request) {
|
|
|
|
+ String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters);
|
|
|
|
+ if (CommonUtils.isBlank(logoutMessage)) {
|
|
|
|
+ this.logger.error("Could not locate logout message of the request from {}", this.logoutParameterName);
|
|
|
|
+ } else {
|
|
|
|
+ if (!logoutMessage.contains("SessionIndex")) {
|
|
|
|
+ logoutMessage = this.uncompressLogoutMessage(logoutMessage);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.logger.trace("Logout request:\n{}", logoutMessage);
|
|
|
|
+ String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");
|
|
|
|
+ if (CommonUtils.isNotBlank(token)) {
|
|
|
|
+ HttpSession session = this.sessionMappingStorage.removeSessionByMappingId(token);
|
|
|
|
+ if (session != null) {
|
|
|
|
+ String sessionID = session.getId();
|
|
|
|
+ this.logger.debug("Invalidating session [{}] for token [{}]", sessionID, token);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ session.invalidate();
|
|
|
|
+ } catch (IllegalStateException var7) {
|
|
|
|
+ this.logger.debug("Error invalidating session.", var7);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.logoutStrategy.logout(request);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private boolean isMultipartRequest(HttpServletRequest request) {
|
|
|
|
+ return request.getContentType() != null && request.getContentType().toLowerCase().startsWith("multipart");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static boolean isServlet30() {
|
|
|
|
+ try {
|
|
|
|
+ return HttpServletRequest.class.getMethod("logout") != null;
|
|
|
|
+ } catch (NoSuchMethodException var1) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private class Servlet30LogoutStrategy implements LogoutStrategy {
|
|
|
|
+ private Servlet30LogoutStrategy() {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void logout(HttpServletRequest request) {
|
|
|
|
+ try {
|
|
|
|
+ request.logout();
|
|
|
|
+ } catch (ServletException var3) {
|
|
|
|
+ logger.debug("Error performing request.logout.");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private class Servlet25LogoutStrategy implements LogoutStrategy {
|
|
|
|
+ private Servlet25LogoutStrategy() {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void logout(HttpServletRequest request) {
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private interface LogoutStrategy {
|
|
|
|
+ void logout(HttpServletRequest var1);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|