feat: 优化模式

This commit is contained in:
wangyu 2024-10-12 17:10:48 +08:00
parent 8d75560d7d
commit 8202f4ad05
13 changed files with 633 additions and 28 deletions

14
pom.xml
View File

@ -62,13 +62,13 @@
<!-- <scope>system</scope>--> <!-- <scope>system</scope>-->
<!-- <systemPath>${project.basedir}/lib/cas-client-core-3.1.12.jar</systemPath>--> <!-- <systemPath>${project.basedir}/lib/cas-client-core-3.1.12.jar</systemPath>-->
<!-- </dependency>--> <!-- </dependency>-->
<dependency> <!-- <dependency>-->
<groupId>edu.yale.its</groupId> <!-- <groupId>edu.yale.its</groupId>-->
<artifactId>cas-client-java</artifactId> <!-- <artifactId>cas-client-java</artifactId>-->
<version>7.0.8</version> <!-- <version>7.0.8</version>-->
<scope>system</scope> <!-- <scope>system</scope>-->
<systemPath>${project.basedir}/lib/sso-client-java-7.0.8.jar</systemPath> <!-- <systemPath>${project.basedir}/lib/sso-client-java-7.0.8.jar</systemPath>-->
</dependency> <!-- </dependency>-->
</dependencies> </dependencies>

View File

@ -1,4 +1,4 @@
package dev.flyfish.boot.cas.filter; package dev.flyfish.boot.cas.context;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
@ -42,7 +42,7 @@ public class CASContext {
@Getter @Getter
private String username; private String username;
static Mono<CASContext> create(ServerWebExchange exchange, WebFilterChain chain) { public static Mono<CASContext> create(ServerWebExchange exchange, WebFilterChain chain) {
return new CASContext(exchange, chain).init(); return new CASContext(exchange, chain).init();
} }
@ -58,48 +58,48 @@ public class CASContext {
.thenReturn(this); .thenReturn(this);
} }
boolean isTokenRequest() { public boolean isTokenRequest() {
return StringUtils.hasText(ticket); return StringUtils.hasText(ticket);
} }
Mono<Void> filter() { public Mono<Void> filter() {
return chain.filter(exchange); return chain.filter(exchange);
} }
Mono<Void> redirect(String url) { public Mono<Void> redirect(String url) {
ServerHttpResponse response = exchange.getResponse(); ServerHttpResponse response = exchange.getResponse();
response.setRawStatusCode(HttpStatus.FOUND.value()); response.setRawStatusCode(HttpStatus.FOUND.value());
response.getHeaders().setLocation(URI.create(url)); response.getHeaders().setLocation(URI.create(url));
return Mono.empty(); return Mono.empty();
} }
ServerHttpRequest getRequest() { public ServerHttpRequest getRequest() {
return exchange.getRequest(); return exchange.getRequest();
} }
ServerHttpResponse getResponse() { public ServerHttpResponse getResponse() {
return exchange.getResponse(); return exchange.getResponse();
} }
String getPath() { public String getPath() {
return exchange.getRequest().getPath().value(); return exchange.getRequest().getPath().value();
} }
HttpMethod getMethod() { public HttpMethod getMethod() {
return exchange.getRequest().getMethod(); return exchange.getRequest().getMethod();
} }
String getQuery(String key) { public String getQuery(String key) {
ServerHttpRequest request = exchange.getRequest(); ServerHttpRequest request = exchange.getRequest();
return request.getQueryParams().getFirst(key); return request.getQueryParams().getFirst(key);
} }
Mono<String> getFormData(String key) { public Mono<String> getFormData(String key) {
return exchange.getFormData() return exchange.getFormData()
.mapNotNull(formData -> formData.getFirst(key)); .mapNotNull(formData -> formData.getFirst(key));
} }
void setSessionAttribute(String key, Object value) { public void setSessionAttribute(String key, Object value) {
session.getAttributes().put(key, value); session.getAttributes().put(key, value);
} }

View File

@ -1,4 +1,4 @@
package dev.flyfish.boot.cas.filter; package dev.flyfish.boot.cas.context;
/** /**
* 上下文初始化逻辑 * 上下文初始化逻辑

View File

@ -0,0 +1,111 @@
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.isAuthenticationSuccesful()) {
try {
ptv.validate();
} catch (Exception e) {
CASAuthenticationException casException = new CASAuthenticationException("Unable to validate ProxyTicketValidator [" + ptv + "]", e);
log.error(casException);
throw casException;
}
}
if (!ptv.isAuthenticationSuccesful()) {
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;
}
}

View File

@ -1,4 +1,4 @@
package dev.flyfish.boot.cas.filter; package dev.flyfish.boot.cas.context;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.web.server.WebSession; import org.springframework.web.server.WebSession;

View File

@ -0,0 +1,11 @@
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);
}
}

View File

@ -1,9 +1,12 @@
package dev.flyfish.boot.cas.filter; package dev.flyfish.boot.cas.filter;
import edu.yale.its.tp.cas.client.CASAuthenticationException; import dev.flyfish.boot.cas.context.CASContext;
import edu.yale.its.tp.cas.client.CASReceipt; import dev.flyfish.boot.cas.context.CASContextInit;
import edu.yale.its.tp.cas.client.ProxyTicketValidator; import dev.flyfish.boot.cas.context.CASReceipt;
import edu.yale.its.tp.cas.util.XmlUtils; 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.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpCookie; import org.springframework.http.HttpCookie;
@ -147,8 +150,12 @@ public class CASFilter implements WebFilter {
URI uri = context.getRequest().getURI(); URI uri = context.getRequest().getURI();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (server.startsWith("http")) {
sb.append(uri.getScheme()).append("://").append(server).append(uri.getPath()); sb.append(server);
} else {
sb.append(uri.getScheme()).append("://").append(server);
}
sb.append(uri.getPath());
if (uri.getQuery() != null) { if (uri.getQuery() != null) {
String query = uri.getQuery(); String query = uri.getQuery();

View File

@ -1,5 +1,8 @@
package dev.flyfish.boot.cas.filter; package dev.flyfish.boot.cas.filter;
import dev.flyfish.boot.cas.context.CASContext;
import dev.flyfish.boot.cas.context.CASContextInit;
/** /**
* 登录过滤器旨在缓存用户名 * 登录过滤器旨在缓存用户名
* *

View File

@ -0,0 +1,86 @@
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.isAuthenticationSuccesful()) {
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;
}
}
}
}

View File

@ -0,0 +1,55 @@
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.URL;
import java.net.URLConnection;
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 + ")");
}
BufferedReader r = null;
try {
URL u = new URL(url);
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);
}
String var7 = output.toString("utf-8");
return var7;
} finally {
try {
if (r != null) {
((BufferedReader) r).close();
}
} catch (IOException var14) {
}
}
}
}

View File

@ -0,0 +1,221 @@
package dev.flyfish.boot.cas.validator;
import lombok.Getter;
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 {
private String casValidateUrl;
private String proxyCallbackUrl;
private String st;
private String service;
private String pgtIou;
private String user;
private String errorCode;
private String errorMessage;
private String entireResponse;
private String ss;
@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.isAuthenticationSuccesful()) {
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 setCasValidateUrl(String x) {
this.casValidateUrl = x;
}
public String getCasValidateUrl() {
return this.casValidateUrl;
}
public void setProxyCallbackUrl(String x) {
this.proxyCallbackUrl = x;
}
public void setRenew(boolean b) {
this.renew = b;
}
public String getProxyCallbackUrl() {
return this.proxyCallbackUrl;
}
public void setServiceTicket(String x) {
this.st = x;
}
public void setService(String x) {
this.service = x;
}
public String getUser() {
return this.user;
}
public String getPgtIou() {
return this.pgtIou;
}
public boolean isAuthenticationSuccesful() {
return this.successfulAuthentication;
}
public String getErrorMessage() {
return this.errorMessage;
}
public String getErrorCode() {
return this.errorCode;
}
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;
StringBuffer sb = new StringBuffer();
sb.append(this.casValidateUrl);
if (this.casValidateUrl.indexOf(63) == -1) {
sb.append('?');
} else {
sb.append('&');
}
sb.append("service=" + this.service + "&ticket=" + this.st);
if (this.proxyCallbackUrl != null) {
sb.append("&pgtUrl=" + 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("cas:authenticationSuccess")) {
this.authenticationSuccess = true;
} else if (qn.equals("cas:authenticationFailure")) {
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("cas:user")) {
ServiceTicketValidator.this.user = this.currentText.toString().trim();
}
if (qn.equals("cas:proxyGrantingTicket")) {
this.pgtIou = this.currentText.toString().trim();
}
} else if (this.authenticationFailure && qn.equals("cas:authenticationFailure")) {
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;
}
}
}
}

View File

@ -0,0 +1,111 @@
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();
}
}

View File

@ -9,6 +9,6 @@ cas:
filter: filter:
cas-login: https://sdsfzt.sxu.edu.cn/authserver/login cas-login: https://sdsfzt.sxu.edu.cn/authserver/login
cas-validate: https://sdsfzt.sxu.edu.cn/authserver/serviceValidate cas-validate: https://sdsfzt.sxu.edu.cn/authserver/serviceValidate
cas-server-name: aef8-218-26-163-214.ngrok-free.app cas-server-name: https://magnetic-first-yak.ngrok-free.app
cas-init-context-class: dev.flyfish.boot.cas.filter.CASLoginFilter cas-init-context-class: dev.flyfish.boot.cas.filter.CASLoginFilter
debug: true debug: true