下载cas_overlay5.1.0,删除maven仓库加速拉取,注意java版本需要1.8。
cas_overlay5.1.0的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd ">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-overlay</artifactId>
<packaging>war</packaging>
<version>1.0</version>
<build>
<plugins>
<plugin>
<groupId>com.rimerosolutions.maven.plugins</groupId>
<artifactId>wrapper-maven-plugin</artifactId>
<version>0.0.4</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${springboot.version}</version>
<configuration>
<mainClass>org.springframework.boot.loader.WarLauncher</mainClass>
<addResources>true</addResources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<warName>cas</warName>
<failOnMissingWebXml>false</failOnMissingWebXml>
<recompressZippedFiles>false</recompressZippedFiles>
<archive>
<compress>false</compress>
<manifestFile>${project.build.directory}/war/work/org.apereo.cas/cas-server-webapp${app.server}/META-INF/MANIFEST.MF
</manifestFile>
</archive>
<overlays>
<overlay>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-webapp${app.server}</artifactId>
</overlay>
</overlays>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
</plugin>
</plugins>
<finalName>cas</finalName>
</build>
<dependencies>
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-webapp${app.server}</artifactId>
<version>${cas.version}</version>
<type>war</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-webflow</artifactId>
<version>5.1.10-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-rest</artifactId>
<version>${cas.version}</version>
</dependency>
</dependencies>
<properties>
<cas.version>5.1.9</cas.version>
<springboot.version>1.5.3.RELEASE</springboot.version>
<!-- app.server could be -jetty, -undertow, -tomcat, or blank if you plan to provide appserver -->
<app.server>-tomcat</app.server>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
cas_overlay5.1.0新增并配置properties
#增加配置关闭验证客户端代理信息和请求来源的IP
cas.tgc.cipherEnabled=false
cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true
修改HTTPSandIMAPS-10000001.json文件
"serviceId" : "^(https|imaps|http)://.*",
研究发现把TGC信息写入到客户端浏览器的Cookie中和双向登陆正确跳转需要修改cas源码并在overlay中建同路径文件进行覆盖使用,下载完整代码cas5.1.9
找到InitialFlowSetupAction类的configureWebflowContext方法
需要修改的代码:
WebUtils.putTicketGrantingTicketInScopes(context, this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request));
修改后
String cookie=this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request);
if (cookie==null && "/cas/login".equals(request.getRequestURI())) {
String tgc=request.getParameter("tgc");
if (tgc!=null && !"".equals(tgc.trim())) {
cookie=tgc;
HttpServletResponse response = WebUtils.getHttpServletResponse(context);
Cookie tgcCookie=new Cookie(this.ticketGrantingTicketCookieGenerator.getCookieName(), cookie);
if (this.ticketGrantingTicketCookieGenerator.getCookieDomain() != null) {
tgcCookie.setDomain(this.ticketGrantingTicketCookieGenerator.getCookieDomain());
}
if (this.ticketGrantingTicketCookieGenerator.getCookiePath() != null) {
tgcCookie.setPath(this.ticketGrantingTicketCookieGenerator.getCookiePath());
}
if (this.ticketGrantingTicketCookieGenerator.getCookieMaxAge() != null) {
tgcCookie.setMaxAge(this.ticketGrantingTicketCookieGenerator.getCookieMaxAge());
}
if (this.ticketGrantingTicketCookieGenerator.isCookieSecure()) {
tgcCookie.setSecure(true);
}
if (this.ticketGrantingTicketCookieGenerator.isCookieHttpOnly()) {
tgcCookie.setHttpOnly(true);
}
response.addCookie(tgcCookie);
}
}
WebUtils.putTicketGrantingTicketInScopes(context,cookie);
修改RankedAuthenticationProviderWebflowEventResolver类的resolveInternal方法
@Override
public Set<Event> resolveInternal(final RequestContext context) {
......
if (StringUtils.isBlank(tgt)) {
redirectToLoginUrl(context);
LOGGER.trace("TGT is blank; proceed with flow normally.");
return resumeFlow();
}
final Authentication authentication = this.ticketRegistrySupport.getAuthenticationFrom(tgt);
if (authentication == null) {
redirectToLoginUrl(context);
LOGGER.trace("TGT has no authentication and is blank; proceed with flow normally.");
return resumeFlow();
}
......
}
/**
*
* 重定向到request参数中指明的登陆页面
* @param context
*/
private void redirectToLoginUrl(final RequestContext context) {
HttpServletRequest request=WebUtils.getHttpServletRequest();
String loginUrl=request.getParameter("loginUrl");
if (loginUrl!=null) {
HttpServletResponse response = WebUtils.getHttpServletResponse(context);
try {
response.sendRedirect(loginUrl+"?checked=1");
} catch (IOException e) {
throw new DhccCasException(e.getMessage(),e);
}
}
}
修改返回登录页面的Controller:
@Controller
public class RestLoginController {
private final static String LOGIN_PAGE_PATH = "/restLogin/login";
private final static String CAS_LOGIN_URL = "https://cas.dhcc.com:8443/cas/login";
private final static String APP_SERVICE_URL = "http://localhost:8010/um/login/cas";
private final static String APP_LOGIN_URL = "http://localhost:8010/um/restLogin";
@RequestMapping("/restLogin")
public ModelAndView index(HttpServletRequest request) {
String checked = request.getParameter("checked");
// 如果已重定向到cas的登录页面检查过tgc,那么直接返回当前登录页面。
if ("1".equals(checked)) {
return new ModelAndView(LOGIN_PAGE_PATH);
}
// 如果还没重定向到cas的登录页面检查tgc,那么重定向到cas的登录页面检查tgc。
String redirectUrl = "redirect:" + CAS_LOGIN_URL + "?service=" + APP_SERVICE_URL + "&loginUrl="+APP_LOGIN_URL;
return new ModelAndView(redirectUrl);
}
}
调用restful接口认证cas
// 调用restful接口认证cas
private void restLoginCas(HttpServletResponse response, String casUsername, String casPwd) throws CustomCasException {
try {
// Step 1: 获取 TGT (Ticket Granting Ticket)
String tgt = RestLoginUtils.getTGT(casConfig.getProperty("cas.restLoginUrl"), casUsername, casPwd);
System.out.println("TGT: " + tgt);
// Step 2: 使用 TGT 获取 ST (Service Ticket)
// String serviceUrl = "http://localhost:8081/index";
String st = RestLoginUtils.getServiceTicket(casConfig.getProperty("cas.casBaseUrl"), tgt, casConfig.getProperty("cas.app.serviceUrl"));
System.out.println("ST: " + st);
String proxyValidate = "http://localhost:8080/cas/proxyValidate";
// Step 3: 向服务端验证 ST
ticketValidate(proxyValidate, st, serviceUrl);;
String tgc = tgt.substring(tgt.indexOf("TGT"));
String redirectUrl = casBaseUrl+"/cas/login" + "?service=" + serviceUrl + "&tgc=" + tgc;
// System.out.println(redirectUrl);
//必须跳转一次该页面才能进入分站
response.sendRedirect(redirectUrl);
} catch (IOException e) {
throw new CustomCasException();
}
}
RestLoginUtils
package cn.appWeixin.Utility;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class RestLoginUtils {
public void main(String[] args) {
String casBaseUrl = "http://localhost:8080";
String casRestLoginUrl = casBaseUrl + "/cas/v1/tickets";
String username = "casuser";
String password = "Mellon";
try {
// Step 1: 获取 TGT (Ticket Granting Ticket)
String tgt = getTGT(casRestLoginUrl, username, password);
System.out.println("TGT: " + tgt);
// Step 2: 使用 TGT 获取 ST (Service Ticket)
String serviceUrl = "http://localhost:8081/index";
String st = getServiceTicket(casBaseUrl, tgt, serviceUrl);
System.out.println("ST: " + st);
String proxyValidate = "http://localhost:8080/cas/proxyValidate";
// Step 3: 向服务端验证 ST
ticketValidate(proxyValidate, st, serviceUrl);;
String tgc = tgt.substring(tgt.indexOf("TGT"));
String redirectUrl = casBaseUrl+"/cas/login" + "?service=" + serviceUrl + "&tgc=" + tgc;
// System.out.println(redirectUrl);
//必须跳转一次该页面才能进入分站
// return "redirect:" + redirectUrl;
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getTGT(String casRestLoginUrl, String username, String password) throws IOException {
URL url = new URL(casRestLoginUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setDoOutput(true);
String data = "username=" + username + "&password=" + password;
conn.getOutputStream().write(data.getBytes());
try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
return conn.getHeaderField("Location");
}
}
public static String getServiceTicket(String casBaseUrl, String tgt, String serviceUrl) throws IOException {
String serviceTicketUrl = tgt;
URL url = new URL(serviceTicketUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setDoOutput(true);
String data = "service=" + serviceUrl;
conn.getOutputStream().write(data.getBytes());
try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
return reader.readLine();
}
}
public static String ticketValidate(String serverValidate, String serviceTicket, String service) throws IOException {
URL url = new URL(serverValidate + "?" + "ticket=" + serviceTicket + "&service=" + URLEncoder.encode(service, "UTF-8"));
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
return response.toString();
}
}
}
参考使用Apereo Cas 5.1.3的Restful接口实现SSO及TGC分析 - 代码先锋网 (codeleading.com)