From a9323e4f9b2622da5e64e9fdf5c3211a3434cdd1 Mon Sep 17 00:00:00 2001 From: wangyu <727842003@qq.com> Date: Mon, 21 Oct 2024 11:44:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=EF=BC=8C=E8=A7=A3?= =?UTF-8?q?=E8=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 5 + .../flyfish/boot/cas/config/CASConfig.java | 99 ----- .../boot/cas/config/annotation/CASUser.java | 9 - .../resolver/CASUserArgumentResolver.java | 27 -- .../config/session/WebSessionDecorator.java | 194 -------- .../config/session/WebSessionListener.java | 21 - .../flyfish/boot/cas/context/CASContext.java | 131 ------ .../boot/cas/context/CASContextInit.java | 13 - .../flyfish/boot/cas/context/CASReceipt.java | 111 ----- .../cas/context/SessionMappingStorage.java | 65 --- .../exception/CASAuthenticationException.java | 11 - .../flyfish/boot/cas/filter/CASFilter.java | 420 ------------------ .../boot/cas/filter/CASLoginFilter.java | 23 - .../flyfish/boot/cas/filter/CASParameter.java | 88 ---- .../cas/validator/ProxyTicketValidator.java | 86 ---- .../flyfish/boot/cas/validator/SecureURL.java | 44 -- .../cas/validator/ServiceTicketValidator.java | 192 -------- .../flyfish/boot/cas/validator/XmlUtils.java | 111 ----- 18 files changed, 5 insertions(+), 1645 deletions(-) delete mode 100644 src/main/java/dev/flyfish/boot/cas/config/CASConfig.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/config/annotation/CASUser.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/config/resolver/CASUserArgumentResolver.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/config/session/WebSessionDecorator.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/config/session/WebSessionListener.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/context/CASContext.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/context/CASContextInit.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/context/CASReceipt.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/context/SessionMappingStorage.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/exception/CASAuthenticationException.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/filter/CASFilter.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/filter/CASLoginFilter.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/filter/CASParameter.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/validator/ProxyTicketValidator.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/validator/SecureURL.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/validator/ServiceTicketValidator.java delete mode 100644 src/main/java/dev/flyfish/boot/cas/validator/XmlUtils.java diff --git a/pom.xml b/pom.xml index 1034063..6b202ff 100644 --- a/pom.xml +++ b/pom.xml @@ -45,6 +45,11 @@ lombok true + + dev.flyfish.boot + spring-webflux-cas-client-starter + 0.0.1 + org.springframework.boot spring-boot-starter-test diff --git a/src/main/java/dev/flyfish/boot/cas/config/CASConfig.java b/src/main/java/dev/flyfish/boot/cas/config/CASConfig.java deleted file mode 100644 index f3b5664..0000000 --- a/src/main/java/dev/flyfish/boot/cas/config/CASConfig.java +++ /dev/null @@ -1,99 +0,0 @@ -package dev.flyfish.boot.cas.config; - -import dev.flyfish.boot.cas.config.resolver.CASUserArgumentResolver; -import dev.flyfish.boot.cas.config.session.WebSessionDecorator; -import dev.flyfish.boot.cas.config.session.WebSessionListener; -import dev.flyfish.boot.cas.filter.CASFilter; -import dev.flyfish.boot.cas.filter.CASParameter; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.reactive.config.WebFluxConfigurer; -import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; -import org.springframework.web.server.WebSession; -import org.springframework.web.server.session.DefaultWebSessionManager; -import org.springframework.web.server.session.InMemoryWebSessionStore; -import org.springframework.web.server.session.WebSessionManager; -import org.springframework.web.server.session.WebSessionStore; -import reactor.core.publisher.Mono; - -import java.time.Duration; -import java.util.List; - -/** - * cas核心配置 - * - * @author wangyu - */ -@Configuration -public class CASConfig implements WebFluxConfigurer { - - @Override - public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) { - configurer.addCustomResolver(new CASUserArgumentResolver()); - } - - @Bean - @ConfigurationProperties("cas.filter") - public CASParameter casParameter() { - return new CASParameter(); - } - - @Bean - public CASFilter casFilter(CASParameter casParameter) { - return new CASFilter(casParameter); - } - - @Bean - public WebSessionStore webSessionStore(WebSessionManager webSessionManager, ServerProperties serverProperties, - ObjectProvider listeners) { - if (webSessionManager instanceof DefaultWebSessionManager defaultWebSessionManager) { - Duration timeout = serverProperties.getReactive().getSession().getTimeout(); - int maxSessions = serverProperties.getReactive().getSession().getMaxSessions(); - ListenableWebSessionStore sessionStore = new ListenableWebSessionStore(timeout, listeners); - sessionStore.setMaxSessions(maxSessions); - defaultWebSessionManager.setSessionStore(sessionStore); - return sessionStore; - } - throw new IllegalStateException("Cannot find web session manager"); - } - - /** - * 处理session销毁,保证正确退出 - * - * @param casFilter 过滤器 - * @return 结果 - */ - @Bean - public WebSessionListener singleSignOutSessionListener(CASFilter casFilter) { - return new WebSessionListener() { - @Override - public Mono onSessionInvalidated(WebSession session) { - return casFilter.getSessionMappingStorage().removeBySessionById(session.getId()); - } - }; - } - - static final class ListenableWebSessionStore extends InMemoryWebSessionStore { - private final Duration timeout; - private final List listeners; - - private ListenableWebSessionStore(Duration timeout, ObjectProvider listeners) { - this.timeout = timeout; - this.listeners = listeners.stream().toList(); - } - - public Mono createWebSession() { - return super.createWebSession() - .map(session -> (WebSession) new WebSessionDecorator(session, listeners)) - .doOnSuccess(this::setMaxIdleTime); - } - - private void setMaxIdleTime(WebSession session) { - session.setMaxIdleTime(this.timeout); - } - } - -} diff --git a/src/main/java/dev/flyfish/boot/cas/config/annotation/CASUser.java b/src/main/java/dev/flyfish/boot/cas/config/annotation/CASUser.java deleted file mode 100644 index 69be797..0000000 --- a/src/main/java/dev/flyfish/boot/cas/config/annotation/CASUser.java +++ /dev/null @@ -1,9 +0,0 @@ -package dev.flyfish.boot.cas.config.annotation; - -import java.lang.annotation.*; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface CASUser { -} diff --git a/src/main/java/dev/flyfish/boot/cas/config/resolver/CASUserArgumentResolver.java b/src/main/java/dev/flyfish/boot/cas/config/resolver/CASUserArgumentResolver.java deleted file mode 100644 index 519ff51..0000000 --- a/src/main/java/dev/flyfish/boot/cas/config/resolver/CASUserArgumentResolver.java +++ /dev/null @@ -1,27 +0,0 @@ -package dev.flyfish.boot.cas.config.resolver; - -import dev.flyfish.boot.cas.config.annotation.CASUser; -import dev.flyfish.boot.cas.filter.CASLoginFilter; -import org.springframework.core.MethodParameter; -import org.springframework.core.ReactiveAdapterRegistry; -import org.springframework.web.reactive.BindingContext; -import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - -public class CASUserArgumentResolver extends HandlerMethodArgumentResolverSupport { - - public CASUserArgumentResolver() { - super(ReactiveAdapterRegistry.getSharedInstance()); - } - - @Override - public boolean supportsParameter(MethodParameter parameter) { - return checkAnnotatedParamNoReactiveWrapper(parameter, CASUser.class, (anno, type) -> true); - } - - @Override - public Mono resolveArgument(MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange) { - return exchange.getSession().mapNotNull(session -> session.getAttribute(CASLoginFilter.CONST_CAS_USERNAME)); - } -} diff --git a/src/main/java/dev/flyfish/boot/cas/config/session/WebSessionDecorator.java b/src/main/java/dev/flyfish/boot/cas/config/session/WebSessionDecorator.java deleted file mode 100644 index edf4be3..0000000 --- a/src/main/java/dev/flyfish/boot/cas/config/session/WebSessionDecorator.java +++ /dev/null @@ -1,194 +0,0 @@ -package dev.flyfish.boot.cas.config.session; - -import lombok.RequiredArgsConstructor; -import org.springframework.lang.Nullable; -import org.springframework.web.server.WebSession; -import reactor.core.publisher.Mono; - -import java.time.Duration; -import java.time.Instant; -import java.util.List; -import java.util.Map; - -@RequiredArgsConstructor -public class WebSessionDecorator implements WebSession { - - private final WebSession decorated; - - private final List listeners; - - /** - * Return a unique session identifier. - */ - @Override - public String getId() { - return decorated.getId(); - } - - /** - * Return a map that holds session attributes. - */ - @Override - public Map getAttributes() { - return decorated.getAttributes(); - } - - /** - * Return the session attribute value if present. - * - * @param name the attribute name - * @return the attribute value - */ - @Nullable - @Override - public T getAttribute(String name) { - return decorated.getAttribute(name); - } - - /** - * Return the session attribute value or if not present raise an - * {@link IllegalArgumentException}. - * - * @param name the attribute name - * @return the attribute value - */ - @Override - public T getRequiredAttribute(String name) { - return decorated.getRequiredAttribute(name); - } - - /** - * Return the session attribute value, or a default, fallback value. - * - * @param name the attribute name - * @param defaultValue a default value to return instead - * @return the attribute value - */ - @Override - public T getAttributeOrDefault(String name, T defaultValue) { - return decorated.getAttributeOrDefault(name, defaultValue); - } - - /** - * Force the creation of a session causing the session id to be sent when - * {@link #save()} is called. - */ - @Override - public void start() { - decorated.start(); - } - - /** - * Whether a session with the client has been started explicitly via - * {@link #start()} or implicitly by adding session attributes. - * If "false" then the session id is not sent to the client and the - * {@link #save()} method is essentially a no-op. - */ - @Override - public boolean isStarted() { - return decorated.isStarted(); - } - - /** - * Generate a new id for the session and update the underlying session - * storage to reflect the new id. After a successful call {@link #getId()} - * reflects the new session id. - * - * @return completion notification (success or error) - */ - @Override - public Mono changeSessionId() { - return decorated.changeSessionId(); - } - - /** - * Invalidate the current session and clear session storage. - * - * @return completion notification (success or error) - */ - @Override - public Mono invalidate() { - // 后续处理 - Mono consumer = Mono.defer(() -> listeners.stream() - .map(listener -> listener.onSessionInvalidated(this.decorated)) - .reduce(Mono::then) - .orElse(Mono.empty())); - - return decorated.invalidate().then(consumer); - } - - /** - * Save the session through the {@code WebSessionStore} as follows: - *
    - *
  • If the session is new (i.e. created but never persisted), it must have - * been started explicitly via {@link #start()} or implicitly by adding - * attributes, or otherwise this method should have no effect. - *
  • If the session was retrieved through the {@code WebSessionStore}, - * the implementation for this method must check whether the session was - * {@link #invalidate() invalidated} and if so return an error. - *
- *

Note that this method is not intended for direct use by applications. - * Instead it is automatically invoked just before the response is - * committed. - * - * @return {@code Mono} to indicate completion with success or error - */ - @Override - public Mono save() { - return decorated.save(); - } - - /** - * Return {@code true} if the session expired after {@link #getMaxIdleTime() - * maxIdleTime} elapsed. - *

Typically expiration checks should be automatically made when a session - * is accessed, a new {@code WebSession} instance created if necessary, at - * the start of request processing so that applications don't have to worry - * about expired session by default. - */ - @Override - public boolean isExpired() { - return decorated.isExpired(); - } - - /** - * Return the time when the session was created. - */ - @Override - public Instant getCreationTime() { - return decorated.getCreationTime(); - } - - /** - * Return the last time of session access as a result of user activity such - * as an HTTP request. Together with {@link #getMaxIdleTime() - * maxIdleTimeInSeconds} this helps to determine when a session is - * {@link #isExpired() expired}. - */ - @Override - public Instant getLastAccessTime() { - return decorated.getLastAccessTime(); - } - - /** - * Configure the max amount of time that may elapse after the - * {@link #getLastAccessTime() lastAccessTime} before a session is considered - * expired. A negative value indicates the session should not expire. - * - * @param maxIdleTime - */ - @Override - public void setMaxIdleTime(Duration maxIdleTime) { - decorated.setMaxIdleTime(maxIdleTime); - } - - /** - * Return the maximum time after the {@link #getLastAccessTime() - * lastAccessTime} before a session expires. A negative time indicates the - * session doesn't expire. - */ - @Override - public Duration getMaxIdleTime() { - return decorated.getMaxIdleTime(); - } -} diff --git a/src/main/java/dev/flyfish/boot/cas/config/session/WebSessionListener.java b/src/main/java/dev/flyfish/boot/cas/config/session/WebSessionListener.java deleted file mode 100644 index 6a946d9..0000000 --- a/src/main/java/dev/flyfish/boot/cas/config/session/WebSessionListener.java +++ /dev/null @@ -1,21 +0,0 @@ -package dev.flyfish.boot.cas.config.session; - -import org.springframework.web.server.WebSession; -import reactor.core.publisher.Mono; - -/** - * web session监听器 - * - * @author wangyu - * 基于装饰器增强实现,可灵活处理 - */ -public interface WebSessionListener { - - default Mono onSessionCreated(WebSession session) { - return Mono.empty(); - } - - default Mono onSessionInvalidated(WebSession session) { - return Mono.empty(); - } -} diff --git a/src/main/java/dev/flyfish/boot/cas/context/CASContext.java b/src/main/java/dev/flyfish/boot/cas/context/CASContext.java deleted file mode 100644 index 68fd6d3..0000000 --- a/src/main/java/dev/flyfish/boot/cas/context/CASContext.java +++ /dev/null @@ -1,131 +0,0 @@ -package dev.flyfish.boot.cas.context; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.util.ConcurrentReferenceHashMap; -import org.springframework.util.StringUtils; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.WebFilterChain; -import org.springframework.web.server.WebSession; -import reactor.core.publisher.Mono; - -import java.net.URI; -import java.util.Map; - -/** - * cas 上下文 - */ -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public class CASContext { - - @Getter - private String ticket; - - @Setter - @Getter - private WebSession session; - - private final ServerWebExchange exchange; - - private final WebFilterChain chain; - - private final Map> parameters = new ConcurrentReferenceHashMap<>(); - - @Setter - @Getter - private String username; - - public static Mono create(ServerWebExchange exchange, WebFilterChain chain) { - return new CASContext(exchange, chain).init(); - } - - private Mono init() { - Mono ticketMono = getParameter("ticket") - .filter(StringUtils::hasText) - .doOnNext(ticket -> this.ticket = ticket); - // 此处必须保证session不为空 - Mono sessionMono = exchange.getSession() - .doOnNext(session -> this.session = session); - return Mono.zipDelayError(ticketMono, sessionMono) - .onErrorContinue((e, v) -> e.printStackTrace()) - .thenReturn(this); - } - - public boolean isTokenRequest() { - return StringUtils.hasText(ticket); - } - - public Mono filter() { - return chain.filter(exchange); - } - - public Mono redirect(String url) { - ServerHttpResponse response = exchange.getResponse(); - response.setRawStatusCode(HttpStatus.FOUND.value()); - response.getHeaders().setLocation(URI.create(url)); - return Mono.empty(); - } - - public ServerHttpRequest getRequest() { - return exchange.getRequest(); - } - - public ServerHttpResponse getResponse() { - return exchange.getResponse(); - } - - public String getPath() { - return exchange.getRequest().getPath().value(); - } - - public HttpMethod getMethod() { - return exchange.getRequest().getMethod(); - } - - public String getQuery(String key) { - ServerHttpRequest request = exchange.getRequest(); - return request.getQueryParams().getFirst(key); - } - - public Mono getFormData(String key) { - return exchange.getFormData() - .mapNotNull(formData -> formData.getFirst(key)); - } - - public void setSessionAttribute(String key, Object value) { - session.getAttributes().put(key, value); - } - - /** - * 获取参数 - * - * @param key 键 - * @return 异步结果 - */ - private Mono getParameter(String key) { - return parameters.computeIfAbsent(key, this::computeParameter); - } - - private Mono computeParameter(String key) { - return this.readParameter(key).cache(); - } - - private Mono readParameter(String key) { - String query = getQuery(key); - if (StringUtils.hasText(query)) { - return Mono.just(query); - } - MediaType mediaType = exchange.getRequest().getHeaders().getContentType(); - if (null != mediaType && mediaType.isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED)) { - return exchange.getFormData().mapNotNull(formData -> formData.getFirst(key)); - } - return Mono.empty(); - } -} diff --git a/src/main/java/dev/flyfish/boot/cas/context/CASContextInit.java b/src/main/java/dev/flyfish/boot/cas/context/CASContextInit.java deleted file mode 100644 index 4260f66..0000000 --- a/src/main/java/dev/flyfish/boot/cas/context/CASContextInit.java +++ /dev/null @@ -1,13 +0,0 @@ -package dev.flyfish.boot.cas.context; - -/** - * 上下文初始化逻辑 - * - * @author wangyu - */ -public interface CASContextInit { - - String getTranslatorUser(String username); - - void initContext(CASContext casContext, String username); -} diff --git a/src/main/java/dev/flyfish/boot/cas/context/CASReceipt.java b/src/main/java/dev/flyfish/boot/cas/context/CASReceipt.java deleted file mode 100644 index b2d774a..0000000 --- a/src/main/java/dev/flyfish/boot/cas/context/CASReceipt.java +++ /dev/null @@ -1,111 +0,0 @@ -package dev.flyfish.boot.cas.context; - -import dev.flyfish.boot.cas.exception.CASAuthenticationException; -import dev.flyfish.boot.cas.validator.ProxyTicketValidator; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -@Setter -@ToString -public class CASReceipt implements Serializable { - - private static final Log log = LogFactory.getLog(CASReceipt.class); - @Getter - private String casValidateUrl; - @Getter - private String pgtIou; - @Getter - private boolean primaryAuthentication = false; - @Getter - private String proxyCallbackUrl; - - private List proxyList = new ArrayList<>(); - @Getter - private String userName; - - public static CASReceipt getReceipt(ProxyTicketValidator ptv) throws CASAuthenticationException { - if (log.isTraceEnabled()) { - log.trace("entering getReceipt(ProxyTicketValidator=[" + ptv + "])"); - } - - if (!ptv.isAuthenticationSuccessful()) { - try { - ptv.validate(); - } catch (Exception e) { - CASAuthenticationException casException = new CASAuthenticationException("Unable to validate ProxyTicketValidator [" + ptv + "]", e); - log.error(casException); - throw casException; - } - } - - if (!ptv.isAuthenticationSuccessful()) { - log.error("validation of [" + ptv + "] was not successful."); - throw new CASAuthenticationException("Unable to validate ProxyTicketValidator [" + ptv + "]"); - } else { - CASReceipt receipt = new CASReceipt(); - receipt.casValidateUrl = ptv.getCasValidateUrl(); - receipt.pgtIou = ptv.getPgtIou(); - receipt.userName = ptv.getUser(); - receipt.proxyCallbackUrl = ptv.getProxyCallbackUrl(); - receipt.proxyList = ptv.getProxyList(); - receipt.primaryAuthentication = ptv.isRenew(); - if (!receipt.validate()) { - throw new CASAuthenticationException("Validation of [" + ptv + "] did not result in an internally consistent CASReceipt."); - } else { - if (log.isTraceEnabled()) { - log.trace("returning from getReceipt() with return value [" + receipt + "]"); - } - - return receipt; - } - } - } - - public CASReceipt() { - } - - public List getProxyList() { - return Collections.unmodifiableList(this.proxyList); - } - - public boolean isProxied() { - return !this.proxyList.isEmpty(); - } - - public String getProxyingService() { - return this.proxyList.isEmpty() ? null : (String) this.proxyList.getFirst(); - } - - private boolean validate() { - boolean valid = true; - if (this.userName == null) { - log.error("Receipt was invalid because userName was null. Receipt:[" + this + "]"); - valid = false; - } - - if (this.casValidateUrl == null) { - log.error("Receipt was invalid because casValidateUrl was null. Receipt:[" + this + "]"); - valid = false; - } - - if (this.proxyList == null) { - log.error("receipt was invalid because proxyList was null. Receipt:[" + this + "]"); - valid = false; - } - - if (this.primaryAuthentication && !this.proxyList.isEmpty()) { - log.error("If authentication was by primary credentials then it could not have been proxied. Yet, primaryAuthentication is true where proxyList is not empty. Receipt:[" + this + "]"); - valid = false; - } - - return valid; - } -} diff --git a/src/main/java/dev/flyfish/boot/cas/context/SessionMappingStorage.java b/src/main/java/dev/flyfish/boot/cas/context/SessionMappingStorage.java deleted file mode 100644 index 8bdd701..0000000 --- a/src/main/java/dev/flyfish/boot/cas/context/SessionMappingStorage.java +++ /dev/null @@ -1,65 +0,0 @@ -package dev.flyfish.boot.cas.context; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.server.WebSession; -import reactor.core.publisher.Mono; - -import java.util.HashMap; -import java.util.Map; - -/** - * webflux的session mapping存储 - * - * @author wangyu - */ -public interface SessionMappingStorage { - - Mono removeSessionByMappingId(String mappingId); - - Mono removeBySessionById(String mappingId); - - Mono addSessionById(String mappingId, WebSession session); - - @Slf4j - class HashMapBackedSessionStorage implements SessionMappingStorage { - - private final Map MANAGED_SESSIONS = new HashMap<>(); - private final Map ID_TO_SESSION_KEY_MAPPING = new HashMap<>(); - - @Override - public Mono removeSessionByMappingId(String mappingId) { - WebSession session = this.MANAGED_SESSIONS.get(mappingId); - if (session != null) { - return this.removeBySessionById(session.getId()).thenReturn(session); - } - return Mono.empty(); - } - - @Override - public Mono removeBySessionById(String sessionId) { - if (log.isDebugEnabled()) { - log.debug("Attempting to remove Session=[" + sessionId + "]"); - } - - String key = this.ID_TO_SESSION_KEY_MAPPING.get(sessionId); - if (log.isDebugEnabled()) { - if (key != null) { - log.debug("Found mapping for session. Session Removed."); - } else { - log.debug("No mapping for session found. Ignoring."); - } - } - - this.MANAGED_SESSIONS.remove(key); - this.ID_TO_SESSION_KEY_MAPPING.remove(sessionId); - return Mono.empty(); - } - - @Override - public Mono addSessionById(String mappingId, WebSession session) { - this.ID_TO_SESSION_KEY_MAPPING.put(session.getId(), mappingId); - this.MANAGED_SESSIONS.put(mappingId, session); - return Mono.empty(); - } - } -} diff --git a/src/main/java/dev/flyfish/boot/cas/exception/CASAuthenticationException.java b/src/main/java/dev/flyfish/boot/cas/exception/CASAuthenticationException.java deleted file mode 100644 index b666cec..0000000 --- a/src/main/java/dev/flyfish/boot/cas/exception/CASAuthenticationException.java +++ /dev/null @@ -1,11 +0,0 @@ -package dev.flyfish.boot.cas.exception; - -public class CASAuthenticationException extends Exception { - public CASAuthenticationException(String string) { - super(string); - } - - public CASAuthenticationException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/dev/flyfish/boot/cas/filter/CASFilter.java b/src/main/java/dev/flyfish/boot/cas/filter/CASFilter.java deleted file mode 100644 index 4d30a09..0000000 --- a/src/main/java/dev/flyfish/boot/cas/filter/CASFilter.java +++ /dev/null @@ -1,420 +0,0 @@ -package dev.flyfish.boot.cas.filter; - -import dev.flyfish.boot.cas.context.CASContext; -import dev.flyfish.boot.cas.context.CASContextInit; -import dev.flyfish.boot.cas.context.CASReceipt; -import dev.flyfish.boot.cas.context.SessionMappingStorage; -import dev.flyfish.boot.cas.exception.CASAuthenticationException; -import dev.flyfish.boot.cas.validator.ProxyTicketValidator; -import dev.flyfish.boot.cas.validator.XmlUtils; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpCookie; -import org.springframework.http.HttpMethod; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.WebFilter; -import org.springframework.web.server.WebFilterChain; -import org.springframework.web.server.WebSession; -import reactor.core.publisher.Mono; - -import java.lang.reflect.InvocationTargetException; -import java.net.URI; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * cas filter的webflux实现 - * - * @author wangyu - * 实现相关核心逻辑,完成鉴权信息抽取 - */ -@Slf4j -public class CASFilter implements WebFilter { - - public static final String LOGIN_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.loginUrl"; - public static final String VALIDATE_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.validateUrl"; - public static final String SERVICE_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.serviceUrl"; - public static final String SERVERNAME_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.serverName"; - public static final String RENEW_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.renew"; - public static final String AUTHORIZED_PROXY_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.authorizedProxy"; - public static final String PROXY_CALLBACK_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.proxyCallbackUrl"; - public static final String WRAP_REQUESTS_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.wrapRequest"; - public static final String GATEWAY_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.gateway"; - public static final String CAS_FILTER_USER = "edu.yale.its.tp.cas.client.filter.user"; - public static final String CAS_FILTER_RECEIPT = "edu.yale.its.tp.cas.client.filter.receipt"; - - static final String CAS_FILTER_GATEWAYED = "edu.yale.its.tp.cas.client.filter.didGateway"; - static final String CAS_FILTER_INITCONTEXTCLASS = "edu.yale.its.tp.cas.client.filter.initContextClass"; - static final String CAS_FILTER_USERLOGINMARK = "edu.yale.its.tp.cas.client.filter.userLoginMark"; - static final String CAS_FILTER_EXCLUSION = "edu.yale.its.tp.cas.client.filter.filterExclusion"; - - private final CASParameter parameter; - private final CASContextInit initializer; - @Getter - private final SessionMappingStorage sessionMappingStorage = new SessionMappingStorage.HashMapBackedSessionStorage(); - - public CASFilter(CASParameter parameter) { - this.parameter = parameter.checked(); - this.initializer = createInitializer(); - } - - private CASContextInit createInitializer() { - if (null != parameter.casInitContextClass) { - try { - // 未正确配置类型,抛弃 - Class cls = parameter.casInitContextClass.asSubclass(CASContextInit.class); - // 实例化对象并返回 - return cls.getConstructor().newInstance(); - } catch (ClassCastException e) { - log.warn("cas context init class not implements CASContextInit", e); - } catch (IllegalArgumentException | IllegalAccessException | InstantiationException - | SecurityException | NoSuchMethodException e) { - log.error("error when initialize the context init class", e); - } catch (InvocationTargetException e) { - log.error("error when create the cas context initializer's instance!", e); - } - } - return null; - } - - private boolean isReceiptAcceptable(CASReceipt receipt) { - if (receipt == null) { - throw new IllegalArgumentException("Cannot evaluate a null receipt."); - } else if (parameter.casRenew && !receipt.isPrimaryAuthentication()) { - return false; - } else { - return !receipt.isProxied() || parameter.authorizedProxies.contains(receipt.getProxyingService()); - } - } - - private CASReceipt getAuthenticatedUser(CASContext context) throws CASAuthenticationException { - log.trace("entering getAuthenticatedUser()"); - ProxyTicketValidator pv = new ProxyTicketValidator(); - pv.setCasValidateUrl(parameter.casValidate); - pv.setServiceTicket(context.getTicket()); - pv.setService(this.getService(context)); - pv.setRenew(parameter.casRenew); - if (parameter.casProxyCallbackUrl != null) { - pv.setProxyCallbackUrl(parameter.casProxyCallbackUrl); - } - - log.debug("about to validate ProxyTicketValidator: [{}]", pv); - - return CASReceipt.getReceipt(pv); - } - - private String getService(CASContext context) { - log.trace("entering getService()"); - - if (parameter.casServerName == null && parameter.casServiceUrl == null) { - throw new IllegalArgumentException("need one of the following configuration parameters: edu.yale.its.tp.cas.client.filter.serviceUrl or edu.yale.its.tp.cas.client.filter.serverName"); - } - - String serviceString; - if (parameter.casServiceUrl != null) { - serviceString = URLEncoder.encode(parameter.casServiceUrl, StandardCharsets.UTF_8); - } else { - serviceString = computeService(context, parameter.getFullServerUrl()); - } - - if (log.isTraceEnabled()) { - log.trace("returning from getService() with service [{}]", serviceString); - } - return serviceString; - } - - /** - * 计算服务地址,主要是替换url中server的部分,并去除ticket - * - * @param context 上下文 - * @param server 服务 - * @return 结果 - */ - public String computeService(CASContext context, String server) { - if (log.isTraceEnabled()) { - log.trace("entering getService({}, {})", context, server); - } - - if (server == null) { - log.error("getService() argument \"server\" was illegally null."); - throw new IllegalArgumentException("name of server is required"); - } - - URI uri = context.getRequest().getURI(); - - StringBuilder sb = new StringBuilder(server).append(uri.getPath()); - - if (uri.getQuery() != null) { - String query = uri.getQuery(); - - int ticketLoc = query.indexOf("ticket="); - if (ticketLoc == -1) { - sb.append("?").append(query); - } else if (ticketLoc > 0) { - ticketLoc = query.indexOf("&ticket="); - if (ticketLoc == -1) { - sb.append("?").append(query); - } else if (ticketLoc > 0) { - sb.append("?").append(query, 0, ticketLoc); - } - } - } - - String encodedService = URLEncoder.encode(sb.toString(), StandardCharsets.UTF_8); - if (log.isTraceEnabled()) { - log.trace("returning from getService() with encoded service [{}]", encodedService); - } - - return encodedService; - } - - /** - * 核心,跳转cas服务器鉴权 - * - * @param context 上下文 - * @return 结果 - */ - private Mono redirectToCAS(CASContext context) { - ServerHttpRequest request = context.getRequest(); - String sessionId = context.getSession().getId(); - - log.trace("entering redirectToCAS()"); - - StringBuilder casLoginString = new StringBuilder() - .append(parameter.casLogin) - .append("?service=").append(this.getService(context)) - .append(parameter.casRenew ? "&renew=true" : "") - .append(parameter.casGateway ? "&gateway=true" : ""); - - if (StringUtils.hasText(sessionId)) { - String appId = parameter.casServerName + request.getPath().contextPath().value(); - casLoginString.append("&appId=").append(URLEncoder.encode(appId, StandardCharsets.UTF_8)) - .append("&sessionId=").append(sessionId); - } - - List cookies = request.getCookies().get("JSESSIONID"); - if (!CollectionUtils.isEmpty(cookies)) { - cookies.stream() - .filter(Objects::nonNull) - .map(HttpCookie::getValue) - .filter(cookie -> !cookie.equals("null") && !cookie.equals(sessionId)) - .peek(cookie -> log.debug("Session is timeout. The timeout session is {}", cookie)) - .findFirst() - .ifPresent(cookie -> casLoginString.append("&timeOut=").append(cookie)); - } - - log.debug("Redirecting browser to [{})", casLoginString); - log.trace("returning from redirectToCAS()"); - - return context.redirect(casLoginString.toString()); - } - - private Mono redirectToInitFailure(CASContext context, String cause) { - log.trace("entering redirectToInitFailure()"); - - String casLoginString = parameter.casLogin + "?action=initFailure"; - if (cause != null && cause.equals("Illegal user")) { - casLoginString += "&userIllegal=true"; - } - - String locale = context.getQuery("locale"); - if (locale != null) { - casLoginString += "&locale=" + locale; - } - - log.debug("Redirecting browser to [{})", casLoginString); - log.trace("returning from redirectToInitFailure()"); - return context.redirect(casLoginString); - } - - private boolean isExclusion(String url) { - if (parameter.exclusions == null) { - return false; - } else { - return parameter.exclusions.contains(url); - } - } - - private Mono translate(CASContext context) { - // 是代理回调地址,通过 - if (parameter.casProxyCallbackUrl != null && parameter.casProxyCallbackUrl.endsWith(context.getPath()) - && context.getQuery("pgtId") != null && context.getQuery("pgtIou") != null) { - log.trace("passing through what we hope is CAS's request for proxy ticket receptor."); - return context.filter(); - } - - // 请求包装,增强请求并完成自定义功能 - if (parameter.wrapRequest) { - log.trace("Wrapping request with CASFilterRequestWrapper."); - // todo 暂时啥也不干,看看有无问题 -// request = new CASFilterRequestWrapper((HttpServletRequest) request); - } - - WebSession session = context.getSession(); - Map sessionAttributes = session.getAttributes(); - - // 使用了用户标记,快速跳过 - if (parameter.userLoginMark != null && session.getAttribute(parameter.userLoginMark) != null) { - return context.filter(); - } - - // 获取receipt,若存在,则通过 - CASReceipt receipt = session.getAttribute(CAS_FILTER_RECEIPT); - if (receipt != null && this.isReceiptAcceptable(receipt)) { - log.trace("CAS_FILTER_RECEIPT attribute was present and acceptable - passing request through filter.."); - return context.filter(); - } - - // 命中排除地址,跳过请求 - if (this.isExclusion(context.getPath())) { - return context.filter(); - } - - // 判断票据 - String ticket = context.getTicket(); - // 存在票据时,验证票据 - if (StringUtils.hasText(ticket)) { - try { - receipt = this.getAuthenticatedUser(context); - } catch (CASAuthenticationException e) { - return this.redirectToCAS(context); - } - - if (!this.isReceiptAcceptable(receipt)) { - throw new IllegalStateException("Authentication was technically successful but rejected as a matter of policy. [" + receipt + "]"); - } - - // 记录receipt - String pt = context.getQuery("pt"); - if (StringUtils.hasText(pt)) { - context.setSessionAttribute(pt, receipt); - } - - // 获取到用户名 - String userName = receipt.getUserName(); - // 尝试初始化 - if (null != initializer) { - try { - String translated = initializer.getTranslatorUser(userName); - log.debug("translated username: {} to {}", userName, translated); - initializer.initContext(context, translated); - } catch (Exception e) { - String cause = e.getCause().getMessage(); - context.setSessionAttribute("initFailure", cause); - return this.redirectToInitFailure(context, cause); - } - } - - sessionAttributes.put(CAS_FILTER_USER, userName); - sessionAttributes.put(CAS_FILTER_RECEIPT, receipt); - sessionAttributes.remove(CAS_FILTER_GATEWAYED); - - if (log.isTraceEnabled()) { - log.trace("validated ticket to get authenticated receipt [{}], now passing request along filter chain.", receipt); - log.trace("returning from doFilter()"); - } - - return context.filter(); - } - - // 不存在票据,跳转验证 - log.trace("CAS ticket was not present on request."); - boolean didGateway = Boolean.parseBoolean(session.getAttribute(CAS_FILTER_GATEWAYED)); - if (parameter.casLogin == null) { - log.error("casLogin was not set, so filter cannot redirect request for authentication."); - throw new IllegalArgumentException("When CASFilter protects pages that do not receive a 'ticket' parameter, it needs a edu.yale.its.tp.cas.client.filter.loginUrl filter parameter"); - } - - if (!didGateway) { - log.trace("Did not previously gateway. Setting session attribute to true."); - sessionAttributes.put(CAS_FILTER_GATEWAYED, "true"); - return this.redirectToCAS(context); - } - - log.trace("Previously gatewayed."); - if (!parameter.casGateway && session.getAttribute(CAS_FILTER_USER) == null) { - if (session.getAttribute("initFailure") != null) { - String cause = session.getAttribute("initFailure"); - return this.redirectToInitFailure(context, cause); - } - - sessionAttributes.put(CAS_FILTER_GATEWAYED, "true"); - return this.redirectToCAS(context); - } - - log.trace("casGateway was true and CAS_FILTER_USER set: passing request along filter chain."); - return context.filter(); - } - - /** - * 二阶段处理,预处理特殊情况,提前中断请求 - * - * @param context 上下文工具 - * @return 结果 - */ - private Mono handle(CASContext context) { - // 优先处理token请求 - if (context.isTokenRequest()) { - String sessionId = context.getSession().getId(); - log.debug("Storing session identifier for {}", sessionId); - - // 包括ticket,尝试重新替换session - return sessionMappingStorage.removeBySessionById(sessionId) - .onErrorContinue((e, v) -> log.debug("error when remove session")) - .then(Mono.defer(() -> sessionMappingStorage.addSessionById(context.getTicket(), context.getSession()) - .then(translate(context)))); - } - // post请求需要特殊处理 - if (context.getMethod() == HttpMethod.POST) { - // 通过form表单获取注销请求,处理注销逻辑 - return context.getFormData("logoutRequest") - .doOnNext(payload -> log.trace("Logout request=[{}]", payload)) - .defaultIfEmpty("") - .flatMap(payload -> { - if (StringUtils.hasText(payload)) { - String token = XmlUtils.getTextForElement(payload, "SessionIndex"); - if (StringUtils.hasText(token)) { - // 满足条件时断路 - return sessionMappingStorage.removeSessionByMappingId(token) - .doOnNext(session -> log.debug("Invalidating session [{}] for ST [{}]", session.getId(), token)) - .flatMap(WebSession::invalidate) - .doOnError(IllegalStateException.class, e -> log.debug(e.getMessage(), e)) - .onErrorComplete(); - } - } - // 继续执行 - return translate(context); - }); - } - return translate(context); - } - - - @Override - public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - // 拦截器需要基于session判定,故提前使用 - return CASContext.create(exchange, chain) - .flatMap(context -> { - WebSession session = context.getSession(); - if (log.isTraceEnabled()) { - log.trace("entering doFilter()"); - } - // 执行跳过策略 - String pt = context.getQuery("pt"); - if (StringUtils.hasText(pt)) { - if (session.getAttribute(pt) != null) { - return context.filter(); - } - } - return handle(context); - }); - } - - -} diff --git a/src/main/java/dev/flyfish/boot/cas/filter/CASLoginFilter.java b/src/main/java/dev/flyfish/boot/cas/filter/CASLoginFilter.java deleted file mode 100644 index 9523765..0000000 --- a/src/main/java/dev/flyfish/boot/cas/filter/CASLoginFilter.java +++ /dev/null @@ -1,23 +0,0 @@ -package dev.flyfish.boot.cas.filter; - -import dev.flyfish.boot.cas.context.CASContext; -import dev.flyfish.boot.cas.context.CASContextInit; - -/** - * 登录过滤器,旨在缓存用户名 - * - * @author wangyu - */ -public class CASLoginFilter implements CASContextInit { - public static String CONST_CAS_USERNAME = "const_cas_username"; - - @Override - public String getTranslatorUser(String username) { - return username; - } - - @Override - public void initContext(CASContext casContext, String username) { - casContext.setSessionAttribute(CONST_CAS_USERNAME, username); - } -} diff --git a/src/main/java/dev/flyfish/boot/cas/filter/CASParameter.java b/src/main/java/dev/flyfish/boot/cas/filter/CASParameter.java deleted file mode 100644 index 9278e69..0000000 --- a/src/main/java/dev/flyfish/boot/cas/filter/CASParameter.java +++ /dev/null @@ -1,88 +0,0 @@ -package dev.flyfish.boot.cas.filter; - -import com.fasterxml.jackson.annotation.JsonAlias; -import lombok.Data; -import org.springframework.util.StringUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.StringTokenizer; - -@Data -public class CASParameter { - - // 排除的过滤地址 - @JsonAlias(CASFilter.CAS_FILTER_EXCLUSION) - Set exclusions; - - @JsonAlias(CASFilter.LOGIN_INIT_PARAM) - String casLogin; - - @JsonAlias(CASFilter.VALIDATE_INIT_PARAM) - String casValidate; - - @JsonAlias(CASFilter.SERVICE_INIT_PARAM) - String casServiceUrl; - - @JsonAlias(CASFilter.SERVERNAME_INIT_PARAM) - String casServerName; - - String casServerProtocol = "http"; - - @JsonAlias(CASFilter.PROXY_CALLBACK_INIT_PARAM) - String casProxyCallbackUrl; - - @JsonAlias(CASFilter.CAS_FILTER_INITCONTEXTCLASS) - Class casInitContextClass; - - @JsonAlias(CASFilter.RENEW_INIT_PARAM) - boolean casRenew; - - @JsonAlias(CASFilter.WRAP_REQUESTS_INIT_PARAM) - boolean wrapRequest; - - @JsonAlias(CASFilter.GATEWAY_INIT_PARAM) - boolean casGateway = false; - - @JsonAlias(CASFilter.CAS_FILTER_USERLOGINMARK) - String userLoginMark = null; - - // 已授权的代理地址列表 - @JsonAlias(CASFilter.AUTHORIZED_PROXY_INIT_PARAM) - List authorizedProxies = new ArrayList<>(); - - public void setAuthorizedProxies(String casAuthorizedProxy) { - if (casAuthorizedProxy != null) { - StringTokenizer casProxies = new StringTokenizer(casAuthorizedProxy); - - while (casProxies.hasMoreTokens()) { - String anAuthorizedProxy = casProxies.nextToken(); - this.authorizedProxies.add(anAuthorizedProxy); - } - } - } - - public String getFullServerUrl() { - if (StringUtils.hasText(casServerName)) { - return casServerProtocol + "://" + casServerName; - } - return null; - } - - /** - * 检查配置参数是否有误 - */ - public CASParameter checked() { - if (this.casGateway && this.casRenew) { - throw new IllegalArgumentException("gateway and renew cannot both be true in filter configuration"); - } else if (this.casServerName != null && this.casServiceUrl != null) { - throw new IllegalArgumentException("serverName and serviceUrl cannot both be set: choose one."); - } else if (this.casServerName == null && this.casServiceUrl == null) { - throw new IllegalArgumentException("one of serverName or serviceUrl must be set."); - } else if (this.casValidate == null) { - throw new IllegalArgumentException("validateUrl parameter must be set."); - } - return this; - } -} diff --git a/src/main/java/dev/flyfish/boot/cas/validator/ProxyTicketValidator.java b/src/main/java/dev/flyfish/boot/cas/validator/ProxyTicketValidator.java deleted file mode 100644 index 36c8204..0000000 --- a/src/main/java/dev/flyfish/boot/cas/validator/ProxyTicketValidator.java +++ /dev/null @@ -1,86 +0,0 @@ -package dev.flyfish.boot.cas.validator; - -import lombok.ToString; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import java.util.ArrayList; -import java.util.List; - -@ToString -public class ProxyTicketValidator extends ServiceTicketValidator { - protected List proxyList; - - public ProxyTicketValidator() { - } - - public static void main(String[] args) throws Exception { - System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); - ProxyTicketValidator pv = new ProxyTicketValidator(); - pv.setCasValidateUrl("https://portal.yale.edu/cas/proxyValidate"); - pv.setService(args[0]); - pv.setServiceTicket(args[1]); - pv.validate(); - System.out.println(pv.getResponse()); - System.out.println(); - if (pv.isAuthenticationSuccessful()) { - System.out.println("user: " + pv.getUser()); - System.out.println("proxies:\n " + pv.getProxyList()); - } else { - System.out.println("error code: " + pv.getErrorCode()); - System.out.println("error message: " + pv.getErrorMessage()); - } - - } - - public List getProxyList() { - return this.proxyList; - } - - protected DefaultHandler newHandler() { - return new ProxyHandler(); - } - - protected void clear() { - super.clear(); - this.proxyList = null; - } - - protected class ProxyHandler extends ServiceTicketValidator.Handler { - protected static final String PROXIES = "cas:proxies"; - protected static final String PROXY = "cas:proxy"; - protected List proxyList = new ArrayList(); - protected boolean proxyFragment = false; - - protected ProxyHandler() { - super(); - } - - public void startElement(String ns, String ln, String qn, Attributes a) { - super.startElement(ns, ln, qn, a); - if (this.authenticationSuccess && qn.equals("cas:proxies")) { - this.proxyFragment = true; - } - - } - - public void endElement(String ns, String ln, String qn) throws SAXException { - super.endElement(ns, ln, qn); - if (qn.equals("cas:proxies")) { - this.proxyFragment = false; - } else if (this.proxyFragment && qn.equals("cas:proxy")) { - this.proxyList.add(this.currentText.toString().trim()); - } - - } - - public void endDocument() throws SAXException { - super.endDocument(); - if (this.authenticationSuccess) { - ProxyTicketValidator.this.proxyList = this.proxyList; - } - - } - } -} diff --git a/src/main/java/dev/flyfish/boot/cas/validator/SecureURL.java b/src/main/java/dev/flyfish/boot/cas/validator/SecureURL.java deleted file mode 100644 index b9fa633..0000000 --- a/src/main/java/dev/flyfish/boot/cas/validator/SecureURL.java +++ /dev/null @@ -1,44 +0,0 @@ -package dev.flyfish.boot.cas.validator; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URL; -import java.net.URLConnection; -import java.nio.charset.StandardCharsets; - -public class SecureURL { - private static final Log log = LogFactory.getLog(SecureURL.class); - - public SecureURL() { - } - - public static void main(String[] args) throws IOException { - System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); - System.out.println(retrieve(args[0])); - } - - public static String retrieve(String url) throws IOException { - if (log.isTraceEnabled()) { - log.trace("entering retrieve(" + url + ")"); - } - - URL u = URI.create(url).toURL(); - URLConnection uc = u.openConnection(); - uc.setRequestProperty("Connection", "close"); - InputStream in = uc.getInputStream(); - - ByteArrayOutputStream output = new ByteArrayOutputStream(); - - for (int chByte = in.read(); chByte != -1; chByte = in.read()) { - output.write(chByte); - } - - return output.toString(StandardCharsets.UTF_8); - } -} diff --git a/src/main/java/dev/flyfish/boot/cas/validator/ServiceTicketValidator.java b/src/main/java/dev/flyfish/boot/cas/validator/ServiceTicketValidator.java deleted file mode 100644 index 2c8b265..0000000 --- a/src/main/java/dev/flyfish/boot/cas/validator/ServiceTicketValidator.java +++ /dev/null @@ -1,192 +0,0 @@ -package dev.flyfish.boot.cas.validator; - -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.DefaultHandler; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParserFactory; -import java.io.IOException; -import java.io.StringReader; - -@ToString -public class ServiceTicketValidator { - @Getter - @Setter - private String casValidateUrl; - @Getter - @Setter - private String proxyCallbackUrl; - private String st; - @Setter - private String service; - @Getter - private String pgtIou; - @Getter - private String user; - @Getter - private String errorCode; - @Getter - private String errorMessage; - private String entireResponse; - private String ss; - @Setter - @Getter - private boolean renew = false; - private boolean attemptedAuthentication; - private boolean successfulAuthentication; - - public ServiceTicketValidator() { - } - - public static void main(String[] args) throws Exception { - System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); - ServiceTicketValidator sv = new ServiceTicketValidator(); - sv.setCasValidateUrl("https://portal1.wss.yale.edu/cas/serviceValidate"); - sv.setProxyCallbackUrl("https://portal1.wss.yale.edu/casProxy/receptor"); - sv.setService(args[0]); - sv.setServiceTicket(args[1]); - sv.validate(); - System.out.println(sv.getResponse()); - System.out.println(); - if (sv.isAuthenticationSuccessful()) { - System.out.println("user: " + sv.getUser()); - System.out.println("pgtIou: " + sv.getPgtIou()); - } else { - System.out.println("error code: " + sv.getErrorCode()); - System.out.println("error message: " + sv.getErrorMessage()); - } - - } - - public void setServiceTicket(String x) { - this.st = x; - } - - public boolean isAuthenticationSuccessful() { - return this.successfulAuthentication; - } - - public String getResponse() { - return this.entireResponse; - } - - public void validate() throws IOException, SAXException, ParserConfigurationException { - if (this.casValidateUrl != null && this.st != null) { - this.clear(); - this.attemptedAuthentication = true; - StringBuilder sb = new StringBuilder(); - sb.append(this.casValidateUrl); - if (this.casValidateUrl.indexOf(63) == -1) { - sb.append('?'); - } else { - sb.append('&'); - } - - sb.append("service=").append(this.service).append("&ticket=").append(this.st); - if (this.proxyCallbackUrl != null) { - sb.append("&pgtUrl=").append(this.proxyCallbackUrl); - } - - if (this.renew) { - sb.append("&renew=true"); - } - - String url = sb.toString(); - this.ss = url; - String response = SecureURL.retrieve(url); - this.entireResponse = response; - if (response != null) { - XMLReader r = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); - r.setFeature("http://xml.org/sax/features/namespaces", false); - r.setContentHandler(this.newHandler()); - r.parse(new InputSource(new StringReader(response))); - } - - } else { - throw new IllegalStateException("must set validation URL and ticket"); - } - } - - protected DefaultHandler newHandler() { - return new Handler(); - } - - protected void clear() { - this.user = this.pgtIou = this.errorMessage = null; - this.attemptedAuthentication = false; - this.successfulAuthentication = false; - } - - protected class Handler extends DefaultHandler { - protected static final String AUTHENTICATION_SUCCESS = "cas:authenticationSuccess"; - protected static final String AUTHENTICATION_FAILURE = "cas:authenticationFailure"; - protected static final String PROXY_GRANTING_TICKET = "cas:proxyGrantingTicket"; - protected static final String USER = "cas:user"; - protected StringBuffer currentText = new StringBuffer(); - protected boolean authenticationSuccess = false; - protected boolean authenticationFailure = false; - protected String netid; - protected String pgtIou; - protected String errorCode; - protected String errorMessage; - - protected Handler() { - } - - public void startElement(String ns, String ln, String qn, Attributes a) { - this.currentText = new StringBuffer(); - if (qn.equals(AUTHENTICATION_SUCCESS)) { - this.authenticationSuccess = true; - } else if (qn.equals(AUTHENTICATION_FAILURE)) { - this.authenticationFailure = true; - this.errorCode = a.getValue("code"); - if (this.errorCode != null) { - this.errorCode = this.errorCode.trim(); - } - } - - } - - public void characters(char[] ch, int start, int length) { - this.currentText.append(ch, start, length); - } - - public void endElement(String ns, String ln, String qn) throws SAXException { - if (this.authenticationSuccess) { - if (qn.equals(USER)) { - ServiceTicketValidator.this.user = this.currentText.toString().trim(); - } - - if (qn.equals(PROXY_GRANTING_TICKET)) { - this.pgtIou = this.currentText.toString().trim(); - } - } else if (this.authenticationFailure && qn.equals(AUTHENTICATION_FAILURE)) { - this.errorMessage = this.currentText.toString().trim(); - } - - } - - public void endDocument() throws SAXException { - if (this.authenticationSuccess) { - ServiceTicketValidator.this.user = ServiceTicketValidator.this.user; - ServiceTicketValidator.this.pgtIou = this.pgtIou; - ServiceTicketValidator.this.successfulAuthentication = true; - } else { - if (!this.authenticationFailure) { - throw new SAXException("no indication of success or failure from CAS"); - } - - ServiceTicketValidator.this.errorMessage = this.errorMessage; - ServiceTicketValidator.this.errorCode = this.errorCode; - ServiceTicketValidator.this.successfulAuthentication = false; - } - - } - } -} diff --git a/src/main/java/dev/flyfish/boot/cas/validator/XmlUtils.java b/src/main/java/dev/flyfish/boot/cas/validator/XmlUtils.java deleted file mode 100644 index 68a0cce..0000000 --- a/src/main/java/dev/flyfish/boot/cas/validator/XmlUtils.java +++ /dev/null @@ -1,111 +0,0 @@ -package dev.flyfish.boot.cas.validator; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.DefaultHandler; -import org.xml.sax.helpers.XMLReaderFactory; - -import java.io.StringReader; -import java.util.ArrayList; -import java.util.List; - -public final class XmlUtils { - private static final Log LOG = LogFactory.getLog(XmlUtils.class); - - public static XMLReader getXmlReader() { - try { - return XMLReaderFactory.createXMLReader(); - } catch (SAXException var1) { - SAXException e = var1; - throw new RuntimeException("Unable to create XMLReader", e); - } - } - - public static List getTextForElements(String xmlAsString, final String element) { - final List elements = new ArrayList(2); - XMLReader reader = getXmlReader(); - DefaultHandler handler = new DefaultHandler() { - private boolean foundElement = false; - private StringBuffer buffer = new StringBuffer(); - - public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - if (localName.equals(element)) { - this.foundElement = true; - } - - } - - public void endElement(String uri, String localName, String qName) throws SAXException { - if (localName.equals(element)) { - this.foundElement = false; - elements.add(this.buffer.toString()); - this.buffer = new StringBuffer(); - } - - } - - public void characters(char[] ch, int start, int length) throws SAXException { - if (this.foundElement) { - this.buffer.append(ch, start, length); - } - - } - }; - reader.setContentHandler(handler); - reader.setErrorHandler(handler); - - try { - reader.parse(new InputSource(new StringReader(xmlAsString))); - return elements; - } catch (Exception var6) { - Exception e = var6; - LOG.error(e, e); - return null; - } - } - - public static String getTextForElement(String xmlAsString, final String element) { - XMLReader reader = getXmlReader(); - final StringBuffer buffer = new StringBuffer(); - DefaultHandler handler = new DefaultHandler() { - private boolean foundElement = false; - - public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - if (localName.equals(element)) { - this.foundElement = true; - } - - } - - public void endElement(String uri, String localName, String qName) throws SAXException { - if (localName.equals(element)) { - this.foundElement = false; - } - - } - - public void characters(char[] ch, int start, int length) throws SAXException { - if (this.foundElement) { - buffer.append(ch, start, length); - } - - } - }; - reader.setContentHandler(handler); - reader.setErrorHandler(handler); - - try { - reader.parse(new InputSource(new StringReader(xmlAsString))); - } catch (Exception var6) { - Exception e = var6; - LOG.error(e, e); - return null; - } - - return buffer.toString(); - } -}