cas_overlay5.1.0启用restful,重载源码增加cookie

101 阅读3分钟

下载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)