問題


使用restful接口進行登陸:
 
思路和用postman 進行測試是一樣的效果
client1的登陸界面:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> 
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0"> 
    <title>登錄界面</title>
    <link href="../css/styles.css" rel="stylesheet" type="text/css" />
    <link href="../css/demo.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" src="../js/requestAnimationFrame.js"></script>
    <script type="text/javascript" src="../js/jquery-2.1.1.min.js"></script>
    <script type="text/javascript" src="../js/Star.js"></script>
    <script type="text/javascript" src="../js/Particle.js"></script>
    <script type="text/javascript" src="../js/Smoke.js"></script>
    <script type="text/javascript" src="../js/jquery-ui.min.js"></script>
    <script src="../js/Treatment.js" type="text/javascript"></script>
    <script src="../js/jquery.mockjax.js" type="text/javascript"></script>
</head>
<body>
     <div style="position: absolute;z-index: 1;">
        <canvas id="canvas_app" class="first"></canvas>
      </div>
    <div class='login' style="position: absolute;z-index: 10;">
      <div class='login_title'>
        <span><center><h3>用戶登錄</h3></center></span>
      </div>
      <div class='login_fields'>
<!-- 使用form表單提交 https://mmoayyed.unicon.net:8433/cas/v1/tickets/login訪問cas server提供的這個接口,借此來實現用戶登錄 -->
         <form action="https://mmoayyed.unicon.net:8433/cas/v1/tickets/login" id="loginForm" method="POST">
          <!--<form action="<%=request.getContextPath()%>/hello/restLogin" id="loginForm" method="POST">-->
          <!--<form action="http://app1.cas.com:9001/hello/restLogin" id="loginForm" method="POST">-->
          <div class='login_fields'>
                  <div class='login_fields__user'>
                      <div class='icon'>
                          <img alt="" src='img/user_icon_copy.png'>
                      </div>
                      <input name="username" id="username" placeholder='用戶名' maxlength="16" type='text' autocomplete="off"/>
                      <div class='validation'>
                          <img alt="" src='img/tick.png'>
                      </div>
                  </div>
                  <div class='login_fields__password'>
                      <div class='icon'>
                          <img alt="" src='img/lock_icon_copy.png'>
                      </div>
                      <input name="password" id="password" placeholder='密碼' maxlength="16" type='text' autocomplete="off">
                      <div class='validation'>
                          <img alt="" src='img/tick.png'>
                      </div>
                  </div>
                  <div class='login_fields__password'>
                      <div class='icon'>
                          <img alt="" src='img/lock_icon_copy.png'>
                      </div>
                      <input name="email" id="email" placeholder='郵箱' maxlength="16" type='text' autocomplete="off">
                      <div class='validation'>
                          <img alt="" src='img/tick.png'>
                      </div>
                  </div>
                  <div class='login_fields__password'>
                      <div class='icon'>
                          <img alt="" src='img/lock_icon_copy.png'>
                      </div>
                      <input name="telephone" id="telephone" placeholder='手機' maxlength="16" type='text' autocomplete="off">
                      <div class='validation'>
                          <img alt="" src='img/tick.png'>
                      </div>
                  </div>
                  <div class='login_fields__password'>
                      <div class='icon'>
                          <img alt="" src='img/key.png'>
                      </div>
                      <input name="capcha" id="capcha" placeholder='驗證碼' maxlength="4" type='text'>
                      <div class='validation' style="opacity: 1; right: -5px;top: -3px;">
                          <canvas class="J_codeimg" id="myCanvas" onclick="Code();">對不起,您的瀏覽器不支持canvas,請下載最新版瀏覽器!</canvas>
                      </div>
                  </div>
                  <div class='login_fields__password'>
                      <input name="Service" type='hidden' value="http://app1.cas.com:9001">
                  </div>
                  <div class='login_fields__submit'>
                      <input type='submit' onClick="checkCode()" value='登錄'>
                  </div>
            </div>
          </form>
        </div>
      </div>
    </div>
    <script type="text/javascript">
        var canGetCookie = 0;//是否支持存儲Cookie 0 不支持 1 支持
        //默認賬號密碼
        var CodeVal = 0;
        Code();
        function Code() {
            if(canGetCookie == 1){
                createCode("AdminCode");
                var AdminCode = getCookieValue("AdminCode");
                showCheck(AdminCode);
            }
            else{
                showCheck(createCode(""));
            }
        }
        function showCheck(a) {
            CodeVal = a;
            var c = document.getElementById("myCanvas");
            var ctx = c.getContext("2d");
            ctx.clearRect(0, 0, 1000, 1000);
            ctx.font = "80px 'Hiragino Sans GB'";
            ctx.fillStyle = "#E8DFE8";
            ctx.fillText(a, 0, 100);
        }
        $(document).keypress(function (e) {
            // 回車鍵事件  
            if (e.which == 13) {
                $('input[type="button"]').click();
            }
        });
        $('input[name="password"]').focus(function () {
            $(this).attr('type', 'password');
        });
        $('input[type="text"]').focus(function () {
            $(this).prev().animate({ 'opacity': '1' }, 200);
        });
        $('input[type="text"],input[type="password"]').blur(function () {
            $(this).prev().animate({ 'opacity': '.5' }, 200);
        });
        function checkCode() {
            if($("#username").val().length==0||$("#password").val().length==0){
                alert("用戶名或密碼不能為空");
            }
            else if($("#code").val().length==0){
                alert("驗證碼不能為空");
            }
            else if($("#code").val().toUpperCase()!=CodeVal.toUpperCase()){
                alert("驗證碼錯誤!");
            }
        }
    </script>
</body>
</html>
<script>
    $(window).resize(resizeCanvas);
    $(window).load(onloadFun);
    var model = { //數據配置
        hue: 237,
        stars: [],
        starImg: null,
        count: 0,
        smoke: null,
        maxStars: 4000,
    }

    var canvas = $("#canvas_app");
    var ctx = canvas.get(0).getContext("2d");
    var starImg;

    function init() {
        createStar(); //星星
        createSmoke(); //煙霧
        animation();
    }

    function animation() {
        ctx.globalCompositeOperation = 'source-over';
        ctx.globalAlpha = 0.3;
        ctx.fillStyle = 'hsla(' + model.hue + ', 64%, 6%, 1)';
        ctx.fillRect(0, 0, $(window).get(0).innerWidth, $(window).get(0).innerHeight)
        ctx.globalCompositeOperation = 'lighter';

        for (var i = 1, l = model.stars.length; i < l; i++) {
            model.stars[i].draw(ctx, model.starImg);
        };

        model.smoke.update(ctx);

        window.requestAnimationFrame(animation);
    }

    function createStar() {

        //圓點
        var canvas2 = document.createElement('canvas');
        model.starImg = canvas2;
        var ctx2 = canvas2.getContext("2d");
        canvas2.width = 88;
        canvas2.height = 88;
        var half = canvas2.width / 2;
        var gradient2 = ctx2.createRadialGradient(half, half, 0, half, half, half);
        gradient2.addColorStop(0.01, '#fff');
        gradient2.addColorStop(0.1, 'hsl(' + model.hue + ', 61%, 50%)');
        gradient2.addColorStop(0.15, 'hsl(' + model.hue + ', 64%, 80%)');
        gradient2.addColorStop(0.26, 'transparent');
        ctx2.fillStyle = gradient2;
        ctx2.beginPath();
        ctx2.arc(half, half, half, 0, Math.PI * 2);
        ctx2.fill();

        for (var i = 0; i < model.maxStars; i++) {
            var star = new Star($(window).get(0).innerWidth, $(window).get(0).innerHeight, model.maxStars);
            model.stars[i] = star;
        }
    }

    function createSmoke() {
        model.smoke = new Smoke();
    }

    function resizeCanvas() {
        canvas.attr("width", $(window).get(0).innerWidth);
        canvas.attr("height", $(window).get(0).innerHeight);
        ctx.fillRect(0, 0, canvas.width(), canvas.height());
        //圖片
        $(".second").attr("width", $(window).get(0).innerWidth);
        $(".second").attr("height", $(window).get(0).innerHeight);
    }

    function onloadFun() {
        init();
        resizeCanvas();
    }
</script>
登陸界面如下圖所示:
服務端進行的修改操作:

編寫https://mmoayyed.unicon.net:8433/cas/v1/tickets/login 接口,也就是重寫TicketGrantingTicketResource這個實現類,重寫的實現類名為:CutomTicketGrantingTicketResource

 @PostMapping(value = "/v1/tickets/login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    public ResponseEntity<String> createTicketGrantingTicket(@RequestBody(required=false) final MultiValueMap<String, String> requestBody,
                                                             final HttpServletRequest request, final HttpServletResponse response) {
        try {
            TicketGrantingTicket tgtId = createTicketGrantingTicketForRequest(requestBody, request);
            var cookies = request.getCookies();
            if(cookies!=null){
                System.out.println("您上次的訪問時間是");
                for(int i=0;i<cookies.length;i++){
                    Cookie cookie = cookies[i];
                    System.out.println("cookie的名字"+cookie.getName());
                    System.out.println("cookie的值"+cookie.getValue());
                }
            }else {
                System.out.println("這是您第一次訪問本站");
            }
            return createResponseEntityForTicket(request, tgtId);
        } catch (final AuthenticationException e) {
            return RestResourceUtils.createResponseEntityForAuthnFailure(e, request, applicationContext);
        } catch (final BadRestRequestException e) {
            return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
        } catch (final Exception e) {
            return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

 

https://mmoayyed.unicon.net:8433/cas/v1/tickets/login/{tgtId:.+}  {tgtId:.+}這個是tgt的值
 @PostMapping(value = "/v1/tickets/login/{tgtId:.+}", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    public ResponseEntity<String> createServiceTicket(final HttpServletRequest httpServletRequest,
                                                      @RequestBody(required = false) final MultiValueMap<String, String> requestBody,
                                                      @PathVariable("tgtId") final String tgtId) {
        try {
            Authentication authn = this.ticketRegistrySupport.getAuthenticationFrom(tgtId);
            AuthenticationCredentialsThreadLocalBinder.bindCurrent(authn);
            if (authn == null) {
                throw new InvalidTicketException(tgtId);
            }
            Service service = this.argumentExtractor.extractService(httpServletRequest);//AbstractWebApplicationService(id=http://localhost:8081/cas-sample-java-webapp, originalUrl=http://localhost:8081/cas-sample-java-webapp, artifactId=null, principal=null, source=service, loggedOutAlready=false, format=XML, attributes={})
            if (service == null) {
                throw new IllegalArgumentException("Target service/application is unspecified or unrecognized in the request");
            }
            //getParameter 返回的是String, 用於讀取提交的表單中的值;
            if (BooleanUtils.toBoolean(httpServletRequest.getParameter(CasProtocolConstants.PARAMETER_RENEW))) {
                List<Credential> credential = this.credentialFactory.fromRequest(httpServletRequest, requestBody);
                if (credential == null || credential.isEmpty()) {
                    throw new BadRestRequestException("No credentials are provided or extracted to authenticate the REST request");
                }
                AuthenticationResult authenticationResult =
                        authenticationSystemSupport.handleAndFinalizeSingleAuthenticationTransaction(service, credential);

                return this.serviceTicketResourceEntityResponseFactory.build(tgtId, service, authenticationResult);
            } else {
                DefaultAuthenticationResultBuilder builder = new DefaultAuthenticationResultBuilder();
                AuthenticationResult authenticationResult = builder
                        .collect(authn)
                        .build(this.authenticationSystemSupport.getPrincipalElectionStrategy(), service);
                System.out.println("service"+service+"*****tgtId*******"+tgtId+"*****authenticationResult*******"+authenticationResult);
                return this.serviceTicketResourceEntityResponseFactory.build(tgtId, service, authenticationResult);
            }
        } catch (final InvalidTicketException e) {
            return new ResponseEntity<>(tgtId + " could not be found or is considered invalid", HttpStatus.NOT_FOUND);
        } catch (final AuthenticationException e) {
            return RestResourceUtils.createResponseEntityForAuthnFailure(e, httpServletRequest, applicationContext);
        } catch (final BadRestRequestException e) {
            return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
        } catch (final Exception e) {
            return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        } finally {
            AuthenticationCredentialsThreadLocalBinder.clear();
        }
    }
 
         

編寫完成后,要對該實現類進行配置並注入,配置類參考:CasRestConfiguration 將 TicketGrantingTicketResource替換為我們自己的實現類CutomTicketGrantingTicketResource

那么對st驗證的操作,也是同樣的操作

最后關於注入配置spring.factories文件,使我們自己的實現類生效,(這里要將我們的實現類和配置類一起諸如該文件才可以生效,參考大神說法是:與之前自定義實現使用自己的數據庫來存儲用戶名密碼的諸如方式不同,自定義實現使用自己的數據庫驗證只需要實現配置類的注入,因為是給內部使用的;而現在我們寫的接口都是給外部使用的,所以注入的時候要把全部都寫進去,注入形式不再描述):

此時進行驗證操作:這里注意的是cas 默認的認證類型是:UsernamePasswordCredential,如果cas server的認證類型為自定義的,此時會報錯,驗證通不過

點擊登陸之后的界面為:

點擊Submit,可以得到ST的值:ST-1-wSsZGyn3yEZL2gojGnzZEV1b4cADESKTOP-NEPM8G8
接下來使用st進行校驗登陸: http://app1.cas.com:9001/?ticket=ST-1-wSsZGyn3yEZL2gojGnzZEV1b4cADESKTOP-NEPM8G8  說明校驗成功
完整日志信息:
*********CustomerHandlerAuthentication執行*********
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
2019-01-28 16:26:07,214 INFO [org.apereo.cas.authentication.PolicyBasedAuthenticationManager] - <Authenticated principal [111] with attributes [{}] via credentials [[UsernamePasswordCredential(username=111, source=null)]].>
2019-01-28 16:26:07,223 INFO [org.apereo.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - <Audit trail record BEGIN
=============================================================
WHO: 111
WHAT: Supplied credentials: [UsernamePasswordCredential(username=111, source=null)]
ACTION: AUTHENTICATION_SUCCESS
APPLICATION: CAS
WHEN: Mon Jan 28 16:26:07 CST 2019
CLIENT IP ADDRESS: 127.0.0.1
SERVER IP ADDRESS: 127.0.0.1
=============================================================

>
2019-01-28 16:26:07,347 INFO [org.apereo.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - <Audit trail record BEGIN
=============================================================
WHO: 111
WHAT: TGT-2-*****lGyY-3GlcUADESKTOP-NEPM8G8
ACTION: TICKET_GRANTING_TICKET_CREATED
APPLICATION: CAS
WHEN: Mon Jan 28 16:26:07 CST 2019
CLIENT IP ADDRESS: 127.0.0.1
SERVER IP ADDRESS: 127.0.0.1
=============================================================

>
您上次的訪問時間是Mon Jan 28 16:26:07 CST 2019
cookie的名字JSESSIONID
cookie的值F432529EE1811DC43BEAF8F551D7A902
2019-01-28 16:26:07,390 INFO [org.apereo.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - <Audit trail record BEGIN
=============================================================
WHO: 111
WHAT: [status=201-CREATED,location=https://mmoayyed.unicon.net:8433/cas/v1/tickets/login/TGT-2-*****lGyY-3GlcUADESKTOP-NEPM8G8]
ACTION: REST_API_TICKET_GRANTING_TICKET_CREATED
APPLICATION: CAS
WHEN: Mon Jan 28 16:26:07 CST 2019
CLIENT IP ADDRESS: 127.0.0.1
SERVER IP ADDRESS: 127.0.0.1
=============================================================

>
serviceAbstractWebApplicationService(id=http://app1.cas.com:9001/, originalUrl=http://app1.cas.com:9001/, artifactId=null, principal=null, source=service, loggedOutAlready=false, format=XML, attributes={})*****tgtId*******TGT-2-v8yUrMSxaHAr-YWVpuh48sDRniz-DPKpAtzFeXYHdRJS7cZsuSje6gmHlGyY-3GlcUADESKTOP-NEPM8G8*****authenticationResult*******DefaultAuthenticationResult(authentication=org.apereo.cas.authentication.DefaultAuthentication@cc89e1b4, service=AbstractWebApplicationService(id=http://app1.cas.com:9001/, originalUrl=http://app1.cas.com:9001/, artifactId=null, principal=null, source=service, loggedOutAlready=false, format=XML, attributes={}), credentialProvided=false)
2019-01-28 16:26:38,588 INFO [org.apereo.cas.ticket.registry.DefaultTicketRegistryCleaner] - <[0] expired tickets removed.>
2019-01-28 16:28:02,495 INFO [org.apereo.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - <Audit trail record BEGIN
=============================================================
WHO: 111
WHAT: [result=Service Access Granted,service=http://app1.cas.com:9001/,requiredAttributes={}]
ACTION: SERVICE_ACCESS_ENFORCEMENT_TRIGGERED
APPLICATION: CAS
WHEN: Mon Jan 28 16:28:02 CST 2019
CLIENT IP ADDRESS: 127.0.0.1
SERVER IP ADDRESS: 127.0.0.1
=============================================================

>
2019-01-28 16:28:04,866 INFO [org.apereo.cas.DefaultCentralAuthenticationService] - <Granted ticket [ST-1-R4bNjOBlFeHz74g8XrmFlzFLPo0DESKTOP-NEPM8G8] for service [http://app1.cas.com:9001/] and principal [111]>
2019-01-28 16:28:04,867 INFO [org.apereo.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - <Audit trail record BEGIN
=============================================================
WHO: 111
WHAT: ST-1-R4bNjOBlFeHz74g8XrmFlzFLPo0DESKTOP-NEPM8G8 for http://app1.cas.com:9001/
ACTION: SERVICE_TICKET_CREATED
APPLICATION: CAS
WHEN: Mon Jan 28 16:28:04 CST 2019
CLIENT IP ADDRESS: 127.0.0.1
SERVER IP ADDRESS: 127.0.0.1
=============================================================

>
2019-01-28 16:28:04,867 INFO [org.apereo.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - <Audit trail record BEGIN
=============================================================
WHO: 111
WHAT: [status=200-OK,body=ST-1-R4bNjOBlFeHz74g8XrmFlzFLPo0DESKTOP-NEPM8G8]
ACTION: REST_API_SERVICE_TICKET_CREATED
APPLICATION: CAS
WHEN: Mon Jan 28 16:28:04 CST 2019
CLIENT IP ADDRESS: 127.0.0.1
SERVER IP ADDRESS: 127.0.0.1
=============================================================

>
2019-01-28 16:28:18,331 INFO [org.apereo.cas.web.flow.login.InitialFlowSetupAction] - <Setting path for cookies for warn cookie generator to: [/cas/] >
2019-01-28 16:28:18,345 INFO [org.apereo.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - <Audit trail record BEGIN
=============================================================
WHO: audit:unknown
WHAT: [event=success,timestamp=Mon Jan 28 16:28:18 CST 2019,source=RankedMultifactorAuthenticationProviderWebflowEventResolver]
ACTION: AUTHENTICATION_EVENT_TRIGGERED
APPLICATION: CAS
WHEN: Mon Jan 28 16:28:18 CST 2019
CLIENT IP ADDRESS: 127.0.0.1
SERVER IP ADDRESS: 127.0.0.1
=============================================================

>
2019-01-28 16:28:20,126 WARN [org.apache.catalina.util.SessionIdGeneratorBase] - <Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [120] milliseconds.>
2019-01-28 16:28:29,333 INFO [org.apereo.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - <Audit trail record BEGIN
=============================================================
WHO: audit:unknown
WHAT: [result=Service Access Granted,service=http://app1.cas.com:9001/,principal=SimplePrincipal(id=111, attributes={}),requiredAttributes={}]
ACTION: SERVICE_ACCESS_ENFORCEMENT_TRIGGERED
APPLICATION: CAS
WHEN: Mon Jan 28 16:28:29 CST 2019
CLIENT IP ADDRESS: 127.0.0.1
SERVER IP ADDRESS: 127.0.0.1
=============================================================

>
2019-01-28 16:28:29,334 INFO [org.apereo.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - <Audit trail record BEGIN
=============================================================
WHO: 111
WHAT: ST-1-R4bNjOBlFeHz74g8XrmFlzFLPo0DESKTOP-NEPM8G8
ACTION: SERVICE_TICKET_VALIDATE_SUCCESS
APPLICATION: CAS
WHEN: Mon Jan 28 16:28:29 CST 2019
CLIENT IP ADDRESS: 127.0.0.1
SERVER IP ADDRESS: 127.0.0.1

=============================================================基於此restful認證已經完成。
 
問題:
一、cas 單點登陸是依靠cookie實現的,也就是TGC來進行校驗,看是否需要重新登陸。而使用restful進行登錄的時候,客戶端瀏覽器里面根本沒有TGC這個cookie;所以其他配置了單點登陸的客戶端依然需要重新登陸,失去單點登陸的意義。
二、這個resturl接口並沒有完全的完成登錄認證,因為中間涉及其他的一些頁面跳轉,理想狀態要求是在客戶端的登陸界面中,就可以直接完成整個登陸流程。
 
 
 
參考鏈接:https://www.cnblogs.com/bryanx/p/8588270.html
       https://blog.csdn.net/cn_yh/article/details/77962467
關於TGC了解
cas server version:6.0.0
cas client:3.4.1
client1登陸的流程,輸入client1的請求url:http://localhost:8081/cas-sample-java-webapp
302:臨時重定向(指出被請求的文檔已被臨時移動到別處,此文檔的新的URL在Location響應頭中給出)此處client1由於未登錄,因此被重定向到https://mmoayyed.unicon.net:8433/cas/login?service=http%3A%2F%2Flocalhost%3A8081%2Fcas-sample-java-webapp%2F
200:訪問成功(表示一切正常,返回的是正常請求結果),指出重定向加載出的正確結果:https://mmoayyed.unicon.net:8433/cas/login?service=http%3A%2F%2Flocalhost%3A8081%2Fcas-sample-java-webapp%2F
 接下來輸入用戶名,密碼進行登陸:
響應的location為:http://localhost:8081/cas-sample-java-webapp/?ticket=ST-7-1xEm8PtwNknJ4HX0WdBoAuR3nU0DESKTOP-NEPM8G8

請求中的cookie為:8074ACBDF7367A3EBDFF049730D741BF

這個cookie創建的位置為:

在對於驗證碼頁面進行加載的時候,重置了sessionid

關於/login提交后,從數據庫驗證用戶名,密碼;通過后創建TGT,並創建jsessionid和TGC的創建;之后重定向到請求的url並傳入st參數進行驗證

cookie的詳細內容

/login登陸頁面的提交參數有:

 

重定向到url的請求:

Set-Cookie的值:A792112B087A9DB1E333E76868ED7B4C

 

至此,完成整個登陸界面

 在cas client1已經登陸的情況下,client2在cas server中的認證流程是這樣的:

輸入訪問client2資源的url:http://localhost:8082/cas-sample-java-webapp/
對改請求的處理如下:會將該請求資源響應到 https://mmoayyed.unicon.net:84…82%2Fcas-sample-java-webapp%2F
這是響應的url:https://mmoayyed.unicon.net:8433/cas/login?service=http://localhost:8082/cas-sample-java-webapp/(會發現這是我們首次登陸被攔截之后重定向的界面)
在這個界面中,  CAS Server 會主動獲到這個 TGC cookie,流程如下:
InitialFlowSetupAction類的doExecute方法調用this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request)方法獲取TGT並存放入當前作用域中,
private void configureWebflowContext(final RequestContext context) {
        val request = WebUtils.getHttpServletRequestFromExternalWebflowContext(context);
        WebUtils.putTicketGrantingTicketInScopes(context, this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request));
        WebUtils.putWarningCookie(context, Boolean.valueOf(this.warnCookieGenerator.retrieveCookieValue(request)));

        WebUtils.putGoogleAnalyticsTrackingIdIntoFlowScope(context, casProperties.getGoogleAnalytics().getGoogleAnalyticsTrackingId());
        WebUtils.putGeoLocationTrackingIntoFlowScope(context, casProperties.getEvents().isTrackGeolocation());
        WebUtils.putPasswordManagementEnabled(context, casProperties.getAuthn().getPm().isEnabled());
        WebUtils.putRememberMeAuthenticationEnabled(context, casProperties.getTicket().getTgt().getRememberMe().isEnabled());
        WebUtils.putStaticAuthenticationIntoFlowScope(context,
            StringUtils.isNotBlank(casProperties.getAuthn().getAccept().getUsers())
                || StringUtils.isNotBlank(casProperties.getAuthn().getReject().getUsers()));

        if (casProperties.getAuthn().getPolicy().isSourceSelectionEnabled()) {
            val availableHandlers = authenticationEventExecutionPlan.getAuthenticationHandlers()
                .stream()
                .filter(h -> h.supports(UsernamePasswordCredential.class))
                .map(h -> StringUtils.capitalize(h.getName().trim()))
                .distinct()
                .sorted()
                .collect(Collectors.toList());
            WebUtils.putAvailableAuthenticationHandleNames(context, availableHandlers);
        }
    }
具體實現類CookieRetrievingCookieGenerator類的retrieveCookieValue方法直接從cookie中獲取TGC,如果TGC不為空,調用this.casCookieValueManager.obtainCookieValue(cookie, request)方法解析TGC得到TGT,
 public String retrieveCookieValue(final HttpServletRequest request) {
        try {
            var cookie = org.springframework.web.util.WebUtils.getCookie(request, getCookieName());
            if (cookie == null) {
                val cookieValue = request.getHeader(getCookieName());
                if (StringUtils.isNotBlank(cookieValue)) {
                    LOGGER.trace("Found cookie [{}] under header name [{}]", cookieValue, getCookieName());
                    cookie = createCookie(cookieValue);
                }
            }
            return cookie == null ? null : this.casCookieValueManager.obtainCookieValue(cookie, request);
        } catch (final Exception e) {
            LOGGER.debug(e.getMessage(), e);
        }
        return null;
    }
問題逐漸明朗了,TGT是根據TGC獲取到的。DefaultCasCookieValueManager.obtainValueFromCompoundCookie()使用該方法進行驗證,在找到這個DefaultCasCookieValueManager實現類時,很麻煩,不知道怎樣快速找到,有經驗的歡迎賜教。
protected String obtainValueFromCompoundCookie(final String cookieValue, final HttpServletRequest request) {
        val cookieParts = Splitter.on(String.valueOf(COOKIE_FIELD_SEPARATOR)).splitToList(cookieValue);
        if (cookieParts.isEmpty()) {
            throw new IllegalStateException("Invalid empty cookie");
        }
        val value = cookieParts.get(0);
        if (!cookieProperties.isPinToSession()) {
            LOGGER.debug("Cookie session-pinning is disabled. Returning cookie value as it was provided");
            return value;
        }

        if (cookieParts.size() != COOKIE_FIELDS_LENGTH) {
            throw new IllegalStateException("Invalid cookie. Required fields are missing");
        }
        val remoteAddr = cookieParts.get(1);
        val userAgent = cookieParts.get(2);

        if (Stream.of(value, remoteAddr, userAgent).anyMatch(StringUtils::isBlank)) {
            throw new IllegalStateException("Invalid cookie. Required fields are empty");
        }

        val clientInfo = ClientInfoHolder.getClientInfo();
        if (!remoteAddr.equals(clientInfo.getClientIpAddress())) {
            throw new IllegalStateException("Invalid cookie. Required remote address "
                + remoteAddr + " does not match " + clientInfo.getClientIpAddress());
        }

        val agent = HttpRequestUtils.getHttpServletRequestUserAgent(request);
        if (!userAgent.equals(agent)) {
            throw new IllegalStateException("Invalid cookie. Required user-agent " + userAgent + " does not match " + agent);
        }
        return value;
    }

代碼說明:首先解密TGC后得到一個由@符號分隔的字符串,分隔后獲取到TGT、客戶端IP、客戶端代理信息。並將從TGC中解密的客戶端IP信息和客戶端代理信息與當前請求的客戶端IP信息和客戶端代理信息進行比較,若不相等就拋出異常(Cas的安全策略)。

驗證通過以后相應的url為:http://localhost:8082/cas-sample-java-webapp/?ticket=ST-3-SwyI4c-Ky-BHSWfGdD8PUk98zZ4DESKTOP-NEPM8G8
此時信息顯示為:
此時,st認證成功,回到訪問資源的登陸界面:http://localhost:8082/cas-sample-java-webapp/
至此,第二個客戶端登陸已經成功。
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM