developercenter徹底剔除webflow

問題記錄前端

一、配置文件報錯,卻能夠正常運行(cas的war包和maven overlay 配置已刪除),一臉懵逼。。。java

二、spring加載bean,id="warnCookieGenerator"時web

實現不是:org.jasig.cas.web.support.CookieRetrievingCookieGeneratorspring

而是:org.jasig.cas.web.WarningCookieRetrievingCookieGeneratorapache

錯誤出現背景,比照cas中login-webflow.xml定義的流程改寫爲相應的controller和輔助類,spring-mvc

而後在在加載"warnCookieGenerator"實現時,class指定錯誤,使其識別不出HttpServletResponse實例,而後斷言一直報錯。cookie

解決思路,比較將本身改造的流程和cas原始流程都debug一遍,比較相關類的實現是否一致。session

三、將cas生成的tgt放入session中,下一次請求時,獲取不到tgt屬性。mvc

緣由多是未修改的cas處理邏輯中,某一處將名爲:「ticketGrantingTicketId「」的屬性移除了。app

暫時未找到移除位置或邏輯,將屬性名改成「ticket」,問題解決。

 

改造過程

一、自定義認證明現

1.一、首先建立實現了AuthenticationHandler接口(其餘AuthenticationHandler的實現類也能夠)的類。

我繼承了抽象類AbstractUsernamePasswordAuthenticationHandler,實現其中

authenticateUsernamePasswordInternal方法,就是根據前端用戶提交的登錄信息,都數據中心查一下,用戶是否合法(可調用dao層方法)。最後如何返回結果能夠參考cas的實現方法:這裏使用了

AcceptUsersAuthenticationHandler中的返回方式:

return createHandlerResult(saturnCredential, this.principalFactory.createPrincipal(accountName), null);

package com.saturn.account.ssoadapter;

import com.saturn.account.domain.common.exception.SAccountInfoException;
import com.saturn.account.domain.common.exception.SUnknowAccountException;
import com.saturn.account.domain.manager.SAccountManager;
import com.saturn.account.domain.model.SAccount;
import com.saturn.account.ssoadapter.credential.SSaturnCredential;
import org.apache.log4j.Logger;
import org.jasig.cas.authentication.*;
import org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * \file       SCASAuthenticationHandler
 * \author     LiJiXiao
 * \date       2018/1/11 12:45
 * \brief      CAS認證處理類
 * \par Copyright (c):
 * Copyright (c) Saturn Team. All rights reserved.
 */
public class SCASAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler
{
   private static final Logger LOGGER = Logger.getLogger(SCASAuthenticationHandler.class);
   /**
    * 具體帳戶認證業務管理類引用
    */
   private SAccountManager accountManager = SAccountManager.getManager();

   @Override
   public HandlerResult authenticateUsernamePasswordInternal(UsernamePasswordCredential credential)
         throws SUnknowAccountException
   {
      final SSaturnCredential saturnCredential = (SSaturnCredential) credential;

      int authPlatform = saturnCredential.getPlatform();

      // valid info
      String accountName = saturnCredential.getUsername();
      if (accountName == null || accountName.trim().equals(""))
      {
         LOGGER.error("accountNmae is null");
         throw new SAccountInfoException("account info error");
      }
      String password = saturnCredential.getPassword();
      if (password == null || password.trim().equals(""))
      {
         LOGGER.error("password is null");
         throw new SAccountInfoException("account info error");
      }

      Map<String, Object> token = new HashMap<>();
      token.put("accountName", accountName);
      token.put("password", password);
      // uri暫時寫死
      token.put("uri", "db://127.0.0.1/no/com.saturn.account.domain.model.SAccount/");

      // authenticate
      SAccount account = accountManager.authenticate(authPlatform, token);

      return createHandlerResult(saturnCredential, this.principalFactory.createPrincipal(accountName), null);
   }
}    

1.二、修改配置引入自定義認證明現

在deployerConfigContext.xml中找到:

<alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" />

下添加bean: 

<bean id="acceptUsersAuthenticationHandler" 
class="com.saturn.account.ssoadapter.SCASAuthenticationHandler"/>

二、實現自定義登錄數據模型

2.一、新建實現了Credential接口(其它實現類也能夠)的自定義數據類,我繼承了

UsernamePasswordCredential

package com.saturn.account.ssoadapter.credential;

import org.jasig.cas.authentication.UsernamePasswordCredential;

import java.io.Serializable;

/**
 * \file       SSaturnCredential
 * \author     LiJiXiao
 * \date       2018/1/11 11:10
 * \brief      帳戶憑證
 * \par Copyright (c):
 * Copyright (c) Saturn Team. All rights reserved.
 */
public class SSaturnCredential extends UsernamePasswordCredential implements Serializable
{
   private static final long serialVersionUID = -1L;
   /**
    * 帳戶認證平臺類別
    */
   private int platform;
   /**
    * 圖形驗證碼
    */
   private String imageCode = "";

   public SSaturnCredential()
   {
   }

   public SSaturnCredential(int platform)
   {
      this.platform = platform;
   }

   @Override
   public String toString()
   {
      return super.toString();
   }

   @Override
   public boolean equals(Object object)
   {
      if (object == null)
      {
         return false;
      }
      if (object instanceof SSaturnCredential)
      {
         return false;
      }
      SSaturnCredential credential = (SSaturnCredential) object;

      if (this.platform != credential.platform)
      {
         return false;
      }
      if (!imageCode.equals(credential.getImageCode()))
      {
         return false;
      }

      return super.equals(object);
   }

   @Override
   public int hashCode()
   {
      int hash = super.hashCode() + this.platform;
      hash += imageCode.hashCode();
      return hash;
   }

   /**
    * \name get/set
    */
   ///@{
   public int getPlatform()
   {
      return platform;
   }

   public void setPlatform(int platform)
   {
      this.platform = platform;
   }

   public String getImageCode()
   {
      return imageCode;
   }

   public void setImageCode(String imageCode)
   {
      this.imageCode = imageCode;
   }
   ///@}
}

2.二、由於本次使用cas再也不使用spring-web-flow,因此不需修改:login-webflow.xml。只須要使用spring-mvc中的@requestBody等標籤,直接接收Credential實例就好。

三、自定義返回用戶信息

有兩種方法,先了解小用戶信息返回流程:

認證完以後,能夠直接獲取須要的帳戶信息,而後將帳戶信息放入handlerResult中的principle中,而後cas會一步步將principle放入authentication類中,咱們能夠從authentication中獲取principle,而後返回前臺。可是此過程當中會有一個principleResolver參與,若是principleResolver是null,則將原來handlerResult中的principle給authentication,若是不爲null,就解析一下在給authentication。cas默認的解析器是

PersonDirectoryPrincipalResolver類

此方法中判斷解析器是否爲空。

此方法中拿到用戶信息的map

具體是經過同類中的此方法獲取

其中attributeRepository,是在spring中配置的實現。

所以自定義用戶信息的兩種方法:

第一:將信息直接放入result中的principle,而後將principleResolve設爲null,可是還不知道如何設置爲空,基礎知識很差。。。

第二:自定義attributeRepository的實現類,重寫getPerson方法,在此方法中將帳戶信息放進map,好比

<"account",account>,就行了。

四、將登錄流程翻譯成controller,直接上碼

登錄controller:

package com.saturn.controller;

import com.saturn.account.domain.model.SAccount;
import com.saturn.account.ssoadapter.credential.SSaturnCredential;
import com.saturn.handler.sso.*;
import com.saturn.model.SResult;
import com.saturn.model.SResultCode;
import org.jasig.cas.authentication.Credential;
import org.jasig.cas.authentication.principal.Principal;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.services.UnauthorizedServiceException;
import org.jasig.cas.web.support.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.binding.message.DefaultMessageContext;
import org.springframework.binding.message.MessageContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * \file       SLoginController
 * \author     LiJiXiao
 * \date       2018/2/2 19:45
 * \brief      L
 * \par Copyright (c):
 * Copyright (c) Saturn Team. All rights reserved.
 */
@Controller
public class SLoginController
{
   private final transient Logger LOGGER = LoggerFactory.getLogger(this.getClass());

   private final long EXPIRE_TIME = 60 * 1000;

   @Autowired
   SInitServletContextAndHttpServletRequestHandler initHandler;
   @Autowired
   STicketGrantingTicketCheckHandler TGTCheckHandler;
   @Autowired
   STerminateSessionHandler terminateSessionHandler;
   @Autowired
   SServiceAuthorizationCheckHandler serviceAuthorizationCheckHandler;
   @Autowired
   SGenerateServiceTicketHandler generateServiceTicketHandler;
   @Autowired
   SToAuthenticationHandler authenticationHandler;

   @RequestMapping (value = "/validation/login", method = RequestMethod.GET)
   @ResponseBody
   public SResult validateLogin(HttpServletRequest request, HttpServletResponse response)
   {
      HttpSession session = request.getSession();

      Service service = (Service) session.getAttribute("service");

      initHandler.initServletContextAndHttpServletRequest(request, service);
      Event TGTCheckEvent = TGTCheckHandler.ticketGrantingTicketCheck(request);
      String TGTChecEventId = TGTCheckEvent.getId();
      // TGT不存在
      if ("notExists".equals(TGTChecEventId))
      {
         try
         {
            serviceAuthorizationCheckHandler.check(service, session);
         }
         catch (UnauthorizedServiceException e)
         {
            return SResult.failure(SResultCode.ACCESS_INVALID);
         }

         return SResult.failure(SResultCode.ACCOUNT_NOT_LOGIN, service);
      }
      // TGT無效
      if ("invalid".equals(TGTChecEventId))
      {
         terminateSessionHandler.terminate(request, response);

         try
         {
            serviceAuthorizationCheckHandler.check(service, session);
         }
         catch (UnauthorizedServiceException e)
         {
            return SResult.failure(SResultCode.ACCESS_INVALID);
         }

         // 去登錄
         return SResult.failure(SResultCode.ACCOUNT_NOT_LOGIN);
      }
      // TGT有效
      if ("valid".equals(TGTChecEventId))
      {
         if (service != null)
         {
            String renew = request.getParameter("renew");
            if (!"".equals(renew) && null != renew)
            {
               try
               {
                  serviceAuthorizationCheckHandler.check(service, session);
               }
               catch (UnauthorizedServiceException e)
               {
                  return SResult.failure(SResultCode.ACCESS_INVALID);
               }
               // 去登錄
               return SResult.failure(SResultCode.ACCOUNT_NOT_LOGIN);
            }
            else
            {
               Event generateServiceTicketEvent =
                     generateServiceTicketHandler.generateServiceTicket(service, request);
               if ("success".equals(generateServiceTicketEvent))
               {
                  Boolean warnCookieValue = (Boolean) session.getAttribute("warnCookieValue");
                  if (warnCookieValue != null && warnCookieValue)
                  {
                     return SResult.success(SResultCode.SYSTEM_WARN);
                  }

                  String serviceTicketId = (String) request.getAttribute("serviceTicketId");
                  if (serviceTicketId == null || "".equals(serviceTicketId))
                  {
                     return SResult.failure(SResultCode.ACCOUNT_NOT_LOGIN);
                  }
                  Map<String, Object> responseInfo = new HashMap<>();
                  responseInfo.put("service", service.getId());
                  responseInfo.put("serviceTicketId", serviceTicketId);

                  return SResult.success(responseInfo);
                  // 回到
               }
               if ("authenticationFailure".equals(generateServiceTicketEvent))
               {
                  return SResult.failure(SResultCode.ACCOUNT_NOT_LOGIN);
               }
            }
         }
         else
         {
            return SResult.success(SResultCode.ACCOUNT_LOGINED,session.getAttribute("account"));
         }
      }
      return SResult.failure(SResultCode.SYSTEM_ERROR);
   }

   @RequestMapping (value = "/real/login")
   @ResponseBody
   public SResult realLogin(
         HttpServletRequest request, HttpServletResponse response, @RequestBody SSaturnCredential credential)
   {
      HttpSession session = request.getSession();

      boolean checkErrorTimesResult = checkErrorTimes(session);
      // 檢查錯誤次數,小於5次正常登錄
      if (checkErrorTimesResult)
      {
         MessageContext messageContext = new DefaultMessageContext();
         Event event = authenticationHandler.submit(request, response, credential, messageContext);
         return responseByEvent(event, session);
      }
      else
      {
         // 大於等於5次須要驗證圖形驗證碼
         boolean isNotExpire = isNotExpire(session);
         if (!isNotExpire)
         {
            return SResult.failure(SResultCode.VALID_CODE_EXPIRE);
         }
         boolean validImageCodeResult = validImageCode(session, credential);
         if (!validImageCodeResult)
         {
            return SResult.failure(SResultCode.VALID_CODE_ERROR);
         }

         // 驗證碼正取進行登錄驗證
         MessageContext messageContext = new DefaultMessageContext();
         Event event = authenticationHandler.submit(request, response, credential, messageContext);
         return responseByEvent(event, session);
      }
   }

   // 錯誤時記錄/添加錯誤次數
   private void setErrorTimes(final HttpSession session)
   {
      int passwordErrorTimes = 0;
      if (session.getAttribute("errorTimes") != null)
      {
         passwordErrorTimes = (Integer) session.getAttribute("errorTimes");
         passwordErrorTimes++;
      }
      else
      {
         session.setAttribute("errorTimes", passwordErrorTimes);
      }
   }

   private boolean checkErrorTimes(final HttpSession session)
   {
      Integer errorTimes = (Integer) session.getAttribute("errorTimes");
      if (errorTimes != null)
      {
         return errorTimes < 5;
      }
      return true;
   }

   private boolean isNotExpire(final HttpSession session)
   {
      Date now = new Date();
      Date createTime = (Date) session.getAttribute("createTime");
      if (createTime == null)
      {
         return false;
      }
      if ((createTime.getTime() - now.getTime()) > EXPIRE_TIME)
      {
         return false;
      }
      return true;
   }

   private boolean validImageCode(final HttpSession session, final Credential credentials)
   {
      // 獲取生成的圖形驗證碼
      String imageCode = (String) session.getAttribute("imageCode");
      if (imageCode == null || imageCode.equals(" "))
      {
         return false;
      }
      session.removeAttribute("imageCode");

      // 獲取提交的圖形驗證碼
      SSaturnCredential credential = (SSaturnCredential) credentials;
      String submitImageCode = credential.getImageCode();

      if (submitImageCode != null && submitImageCode.equals(imageCode))
      {
         return true;
      }

      return false;
   }

   private SResult responseByEvent(Event event, HttpSession session)
   {
      if ("success".equals(event.getId()))
      {
         session.removeAttribute("errorTimes");
         SAccount account = (SAccount) session.getAttribute("account");
         return SResult.success(account);
      }
      if ("successWithWarnings".equals(event.getId()))
      {
         session.removeAttribute("errorTimes");
         return SResult.success("withWarning");
      }
      else
      {
         setErrorTimes(session);
         return SResult.failure(SResultCode.ACCOUNT_LOGIN_FAIL);
      }
   }
}

相關處理輔助類:

4.一、代替流程文件中的初始域數據類initialFlowSetupAction:

package com.saturn.handler.sso;

import org.apache.commons.lang3.StringUtils;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.services.RegisteredService;
import org.jasig.cas.services.RegisteredServiceAccessStrategy;
import org.jasig.cas.services.ServicesManager;
import org.jasig.cas.services.UnauthorizedServiceException;
import org.jasig.cas.web.support.ArgumentExtractor;
import org.jasig.cas.web.support.CookieRetrievingCookieGenerator;
import org.jasig.cas.web.support.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;

/**
 * this class is for
 * @since 1.8
 */
@Component
public class SInitServletContextAndHttpServletRequestHandler
{
   private final transient Logger logger = LoggerFactory.getLogger(this.getClass());

   /**
    * The services manager with access to the registry.
    **/
   @NotNull
   @Autowired
   private ServicesManager servicesManager;

   /**
    * CookieGenerator for the Warnings.
    */
   @NotNull
   @Autowired
   private CookieRetrievingCookieGenerator warnCookieGenerator;

   /**
    * CookieGenerator for the TicketGrantingTickets.
    */
   @NotNull
   @Autowired
   private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator;

   /**
    * Extractors for finding the service.
    */
   @NotNull
   @Size (min = 1)
   @Autowired
   @Qualifier ("argumentExtractor")
   private List<ArgumentExtractor> argumentExtractors;

   /**
    * If no authentication request from a service is present, halt and warn the user.
    */
   private boolean hasAuthenticationRequest = true;

   public void initServletContextAndHttpServletRequest(HttpServletRequest request, Service service)
   {
      HttpSession session = request.getSession();
      ServletContext context = session.getServletContext();
      final String contextPath = context.getContextPath();
      final String cookiePath = StringUtils.isNotBlank(contextPath) ? contextPath + '/' : "/";

      if (StringUtils.isBlank(warnCookieGenerator.getCookiePath()))
      {
         logger.info("Setting path for cookies for warn cookie generator to: {} ", cookiePath);
         this.warnCookieGenerator.setCookiePath(cookiePath);
      }
      else
      {
         logger.debug("Warning cookie path is set to {} and path {}", warnCookieGenerator.getCookieDomain(),
                      warnCookieGenerator.getCookiePath());
      }

      if (StringUtils.isBlank(ticketGrantingTicketCookieGenerator.getCookiePath()))
      {
         logger.info("Setting path for cookies for TGC cookie generator to: {} ", cookiePath);
         this.ticketGrantingTicketCookieGenerator.setCookiePath(cookiePath);
      }
      else
      {
         logger.debug("TGC cookie path is set to {} and path {}",
                      ticketGrantingTicketCookieGenerator.getCookieDomain(),
                      ticketGrantingTicketCookieGenerator.getCookiePath());
      }

      String ticketValue = this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request);
      Boolean cookieValue = Boolean.valueOf(this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request));
      request.setAttribute("ticketGrantingTicketId", ticketValue);
      session.setAttribute("ticketGrantingTicketId", ticketValue);
      session.setAttribute("warnCookieValue", cookieValue);

      service = WebUtils.getService(this.argumentExtractors, request);
      if (service != null)
      {
         logger.debug("Placing service in context scope: [{}]", service.getId());

         final RegisteredService registeredService = this.servicesManager.findServiceBy(service);
         if (registeredService != null && registeredService.getAccessStrategy().isServiceAccessAllowed())
         {
            logger.debug("Placing registered service [{}] with id [{}] in context scope",
                         registeredService.getServiceId(), registeredService.getId());
            session.setAttribute("registeredService", registeredService);

            final RegisteredServiceAccessStrategy accessStrategy = registeredService.getAccessStrategy();
            if (accessStrategy.getUnauthorizedRedirectUrl() != null)
            {
               logger.debug(
                     "Placing registered service's unauthorized redirect url [{}] with id [{}] in context " +
                     "scope", accessStrategy.getUnauthorizedRedirectUrl(), registeredService.getServiceId());
               session.setAttribute("unauthorizedRedirectUrl", accessStrategy.getUnauthorizedRedirectUrl());
            }
         }
      }
      else if (!this.hasAuthenticationRequest)
      {
         logger.warn(
               "No service authentication request is available at [{}]. CAS is configured to disable the flow.",
               request.getRequestURL());
         throw new UnauthorizedServiceException("screen.service.required.message", "Service is required");
      }
      session.setAttribute("service", service);
   }

   /**
    * Decide whether CAS should allow authentication requests
    * when no service is present in the request. Default is enabled.
    * @param enableFlowOnAbsentServiceRequest the enable flow on absent service request
    */
   @Autowired
   public void setEnableFlowOnAbsentServiceRequest(
         @Value ("${create.sso.missing.service:true}") final boolean enableFlowOnAbsentServiceRequest)
   {
      this.hasAuthenticationRequest = enableFlowOnAbsentServiceRequest;
   }
}

4.二、流程中的第一個處理節點ticketGrantingTicketCheck:

package com.saturn.handler.sso;

import org.jasig.cas.CentralAuthenticationService;
import org.jasig.cas.ticket.AbstractTicketException;
import org.jasig.cas.ticket.Ticket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.webflow.execution.Event;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotNull;

/**
 * this class is for
 * @since 1.8
 */
@Component
public class STicketGrantingTicketCheckHandler
{
   private final transient Logger LOGGER = LoggerFactory.getLogger(this.getClass());
   /**
    * TGT does not exist event ID={@value}.
    **/
   private static final String NOT_EXISTS = "notExists";

   /**
    * TGT invalid event ID={@value}.
    **/
   private static final String INVALID = "invalid";

   /**
    * TGT valid event ID={@value}.
    **/
   private static final String VALID = "valid";

   /**
    * The Central authentication service.
    */
   @NotNull
   private final CentralAuthenticationService centralAuthenticationService;

   @Autowired
   public STicketGrantingTicketCheckHandler(
         @Qualifier ("centralAuthenticationService") final CentralAuthenticationService
               centralAuthenticationService)
   {
      this.centralAuthenticationService = centralAuthenticationService;
   }

   /**
    * \param[] a
    * \return a
    * \see
    * \note    從session中獲取ticket屬性值,使用ticketGrantingTicketId命名屬性時,cas可能會將此屬性從session中移除,因此換了個名字「ticket」。
    * \warning
    */
   public Event ticketGrantingTicketCheck(HttpServletRequest request)
   {
      HttpSession session = request.getSession();

      final String tgtFromRequest = (String) request.getAttribute("ticket");
      final String tgtFromSession = (String) session.getAttribute("ticket");
      final String tgtId = tgtFromRequest != null ? tgtFromRequest : tgtFromSession;

      if (!StringUtils.hasText(tgtId))
      {
         return new Event(this, NOT_EXISTS);
      }

      String eventId = INVALID;
      try
      {
         final Ticket ticket = this.centralAuthenticationService.getTicket(tgtId, Ticket.class);
         if (ticket != null && !ticket.isExpired())
         {
            eventId = VALID;
         }
      }
      catch (final AbstractTicketException e)
      {
         LOGGER.trace("Could not retrieve ticket id {} from registry.", e);
      }
      return new Event(this, eventId);
   }
}

4.三、第二個節點:terminateSession

package com.saturn.handler.sso;

import org.jasig.cas.CentralAuthenticationService;
import org.jasig.cas.authentication.AuthenticationSystemSupport;
import org.jasig.cas.authentication.DefaultAuthenticationSystemSupport;
import org.jasig.cas.logout.LogoutRequest;
import org.jasig.cas.web.support.CookieRetrievingCookieGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.webflow.action.EventFactorySupport;
import org.springframework.webflow.execution.Event;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotNull;
import java.util.List;

/**
 * this class is for
 * @since 1.8
 */
@Component
public class STerminateSessionHandler
{
   /** Webflow event helper component. */
   private final EventFactorySupport eventFactorySupport = new EventFactorySupport();

   /** The CORE to which we delegate for all CAS functionality. */
   @NotNull
   @Autowired
   @Qualifier ("centralAuthenticationService")
   private CentralAuthenticationService centralAuthenticationService;

   /** CookieGenerator for TGT Cookie. */
   @NotNull
   @Autowired
   @Qualifier("ticketGrantingTicketCookieGenerator")
   private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator;

   /** CookieGenerator for Warn Cookie. */
   @NotNull
   @Autowired
   @Qualifier("warnCookieGenerator")
   private CookieRetrievingCookieGenerator warnCookieGenerator;

   @NotNull
   @Autowired(required=false)
   @Qualifier("defaultAuthenticationSystemSupport")
   private AuthenticationSystemSupport authenticationSystemSupport = new DefaultAuthenticationSystemSupport();

   /**
    * Creates a new instance with the given parameters.
    */
   public STerminateSessionHandler() {}

   public Event terminate(HttpServletRequest request,HttpServletResponse response) {
      // in login's webflow : we can get the value from context as it has already been stored
      HttpSession session = request.getSession();
      final String tgtFromRequest = (String) request.getAttribute("ticketGrantingTicketId");
      final String tgtFromSession = (String) session.getAttribute("ticketGrantingTicketId");
      String tgtId = tgtFromRequest != null ? tgtFromRequest : tgtFromSession;

      // for logout, we need to get the cookie's value
      if (tgtId == null) {
         tgtId = this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request);
      }
      if (tgtId != null) {
         final List<LogoutRequest>
               logoutRequests = this.centralAuthenticationService.destroyTicketGrantingTicket(tgtId);
         session.setAttribute("logoutRequests",logoutRequests);
      }
      this.ticketGrantingTicketCookieGenerator.removeCookie(response);
      this.warnCookieGenerator.removeCookie(response);

      return this.eventFactorySupport.success(this);
   }
}

4.四、第三各節點(暫不考慮gatewayRequestCheck):serviceAuthorizationCheck

package com.saturn.handler.sso;

import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.services.RegisteredService;
import org.jasig.cas.services.ServicesManager;
import org.jasig.cas.services.UnauthorizedServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.webflow.action.EventFactorySupport;
import org.springframework.webflow.execution.Event;

import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotNull;
import java.net.URI;

/**
 * \file       SServiceAuthorizationCheckHandler
 * \author     LiJiXiao
 * \date       2018/2/2 20:48
 * \brief      L
 * \par Copyright (c):
 * Copyright (c) Saturn Team. All rights reserved.
 */
@Component
public class SServiceAuthorizationCheckHandler
{
   private final transient Logger logger = LoggerFactory.getLogger(this.getClass());

   @NotNull
   private final ServicesManager servicesManager;

   /**
    * Initialize the component with an instance of the services manager.
    * @param servicesManager the service registry instance.
    */
   @Autowired
   public SServiceAuthorizationCheckHandler(@Qualifier ("servicesManager")
                                    final ServicesManager servicesManager) {
      this.servicesManager = servicesManager;
   }

   public Event check(final Service service, HttpSession session) throws UnauthorizedServiceException {
      //No service == plain /login request. Return success indicating transition to the login form
      if (service == null) {
         return this.getEventFactorySupport().success(this);
      }

      if (this.servicesManager.getAllServices().isEmpty()) {
         final String msg = String.format("No service definitions are found in the service manager. "
                                          + "Service [%s] will not be automatically authorized to request authentication.", service.getId());
         logger.warn(msg);
         throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_EMPTY_SVC_MGMR);
      }
      final RegisteredService registeredService = this.servicesManager.findServiceBy(service);

      if (registeredService == null) {
         final String msg = String.format("Service Management: Unauthorized Service Access. "
                                          + "Service [%s] is not found in service registry.", service.getId());
         logger.warn(msg);
         throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg);
      }
      if (!registeredService.getAccessStrategy().isServiceAccessAllowed()) {
         final String msg = String.format("Service Management: Unauthorized Service Access. "
                                          + "Service [%s] is not allowed access via the service registry.", service.getId());

         logger.warn(msg);

         URI url = registeredService.getAccessStrategy().getUnauthorizedRedirectUrl();
         session.setAttribute("unauthorizedRedirectUrl",url);
         throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg);
      }

      return this.getEventFactorySupport().success(this);
   }

   private EventFactorySupport getEventFactorySupport() {
      return new EventFactorySupport();
   }
}

4.五、generateServiceTicket節點:

package com.saturn.handler.sso;

import org.jasig.cas.CentralAuthenticationService;
import org.jasig.cas.authentication.*;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.services.RegisteredService;
import org.jasig.cas.services.ServicesManager;
import org.jasig.cas.ticket.AbstractTicketException;
import org.jasig.cas.ticket.InvalidTicketException;
import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.registry.TicketRegistrySupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.webflow.action.EventFactorySupport;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.execution.Event;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotNull;
import java.net.URI;

/**
 * this class is for
 * @since 1.8
 */
@Component
public class SGenerateServiceTicketHandler
{
   private final transient Logger logger = LoggerFactory.getLogger(this.getClass());
   /**
    * Instance of CentralAuthenticationService.
    */
   @NotNull
   @Autowired
   @Qualifier ("centralAuthenticationService")
   private CentralAuthenticationService centralAuthenticationService;

   @Autowired
   @Qualifier("servicesManager")
   private ServicesManager servicesManager;

   @NotNull
   @Autowired
   @Qualifier("defaultAuthenticationSystemSupport")
   private AuthenticationSystemSupport authenticationSystemSupport = new DefaultAuthenticationSystemSupport();

   @Autowired
   @Qualifier("defaultTicketRegistrySupport")
   private TicketRegistrySupport ticketRegistrySupport;

   public Event generateServiceTicket(Service service, HttpServletRequest request) {

      HttpSession session = request.getSession();
      final String tgtFromRequest = (String) request.getAttribute("ticketGrantingTicketId");
      final String tgtFromSession = (String) session.getAttribute("ticketGrantingTicketId");
      final String ticketGrantingTicket = tgtFromRequest != null ? tgtFromRequest : tgtFromSession;

      try {
         /**
          * In the initial primary authentication flow, credentials are cached and available.
          * Since they are authenticated as part of submission first, there is no need to doubly
          * authenticate and verify credentials.
          *
          * In subsequent authentication flows where a TGT is available and only an ST needs to be
          * created, there are no cached copies of the credential, since we do have a TGT available.
          * So we will simply grab the available authentication and produce the final result based on that.
          */
         final Authentication authentication = ticketRegistrySupport.getAuthenticationFrom(ticketGrantingTicket);
         if (authentication == null) {
            throw new InvalidTicketException(new AuthenticationException(), ticketGrantingTicket);
         }

         final RegisteredService registeredService = servicesManager.findServiceBy(service);
         session.setAttribute("registeredService",registeredService);
         session.setAttribute("service",service);
         URI url = registeredService.getAccessStrategy().getUnauthorizedRedirectUrl();
         session.setAttribute("unauthorizedRedirectUrl", url);

         final AuthenticationContextBuilder builder = new DefaultAuthenticationContextBuilder(
               this.authenticationSystemSupport.getPrincipalElectionStrategy());

         Credential cFromRequest = (Credential)request.getAttribute("credential");
         Credential cFromSession = (Credential)session.getAttribute("credential");
         Credential credential = cFromRequest != null ? cFromRequest : cFromSession;
         credential = credential != null && org.apache.commons.lang3.StringUtils.isBlank(credential.getId()) ? null : credential;
         final AuthenticationContext authenticationContext =
               builder.collect(credential)
                     .collect(authentication).build(service);

         final ServiceTicket serviceTicketId = this.centralAuthenticationService
               .grantServiceTicket(ticketGrantingTicket, service, authenticationContext);
         request.setAttribute("serviceTicketId",serviceTicketId);
         return this.getEventFactorySupport().success(this);

      } catch (final AuthenticationException e) {
         logger.error("Could not verify credentials to grant service ticket", e);
      } catch (final AbstractTicketException e) {
         if (e instanceof InvalidTicketException) {
            this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicket);
         }

         return newEvent("sendTicketGrantingTicket", e);
      }
      return this.getEventFactorySupport().error(this);
   }

   public void setCentralAuthenticationService(final CentralAuthenticationService centralAuthenticationService) {
      this.centralAuthenticationService = centralAuthenticationService;
   }

   public void setAuthenticationSystemSupport(final AuthenticationSystemSupport authenticationSystemSupport) {
      this.authenticationSystemSupport = authenticationSystemSupport;
   }

   public void setTicketRegistrySupport(final TicketRegistrySupport ticketRegistrySupport) {
      this.ticketRegistrySupport = ticketRegistrySupport;
   }

   public void setServicesManager(final ServicesManager servicesManager) {
      this.servicesManager = servicesManager;
   }

   /**
    * New event based on the id, which contains an error attribute referring to the exception occurred.
    *
    * @param id    the id
    * @param error the error
    * @return the event
    */
   private Event newEvent(final String id, final Exception error) {
      return new Event(this, id, new LocalAttributeMap<>("error", error));
   }

   private EventFactorySupport getEventFactorySupport() {
      return new EventFactorySupport();
   }
}

4.六、修改認證入口authenticationViaFormAction:

package com.saturn.handler.sso;

import com.saturn.account.domain.model.SAccount;
import org.apache.commons.lang3.StringUtils;
import org.jasig.cas.CentralAuthenticationService;
import org.jasig.cas.authentication.*;
import org.jasig.cas.authentication.principal.Principal;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.ticket.AbstractTicketException;
import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.TicketCreationException;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.stereotype.Component;
import org.springframework.web.util.CookieGenerator;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.execution.Event;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotNull;
import java.util.Iterator;
import java.util.Map;

/**
 * \file       SToAuthenticationHandler
 * \author     LiJiXiao
 * \date       2018/2/2 20:24
 * \brief      L
 * \par Copyright (c):
 * Copyright (c) Saturn Team. All rights reserved.
 */
@Component
public class SToAuthenticationHandler
{
   private static final String SUCCESS_WITH_WARNINGS = "successWithWarnings";
   private static final String AUTHENTICATION_FAILURE = "authenticationFailure";
   private static final String PUBLIC_WORKSTATION_ATTRIBUTE = "publicWorkstation";
   private static final String WARN = "warn";
   private final transient Logger LOGGER = LoggerFactory.getLogger(this.getClass());
   @NotNull
   @Autowired
   @Qualifier ("centralAuthenticationService")
   private CentralAuthenticationService centralAuthenticationService;
   @NotNull
   @Autowired
   @Qualifier ("warnCookieGenerator")
   private CookieGenerator warnCookieGenerator;
   @NotNull
   @Autowired (required = false)
   @Qualifier ("defaultAuthenticationSystemSupport")
   private AuthenticationSystemSupport authenticationSystemSupport = new DefaultAuthenticationSystemSupport();

   public SToAuthenticationHandler()
   {
   }

   public final Event submit(
         HttpServletRequest request, HttpServletResponse response, Credential credential,
         MessageContext messageContext)
   {
      return this.isRequestAskingForServiceTicket(request) ? this.grantServiceTicket(request, response, credential) :
             this.createTicketGrantingTicket(request, response, credential, messageContext);
   }

   private boolean isRequestAskingForServiceTicket(HttpServletRequest request)
   {
      HttpSession session = request.getSession();
      Service service = (Service) session.getAttribute("service");
      String ticketGrantingTicketId = (String) session.getAttribute("ticketGrantingTicketId");
      return StringUtils.isNotBlank(request.getParameter("renew")) && ticketGrantingTicketId != null &&
             service != null;
   }

   private Event grantServiceTicket(HttpServletRequest request, HttpServletResponse response, Credential credential)
   {
      HttpSession session = request.getSession();
      String ticketGrantingTicketId = (String) session.getAttribute("ticketGrantingTicketId");

      try
      {
         Service service = (Service) session.getAttribute("service");
         AuthenticationContextBuilder builder = new DefaultAuthenticationContextBuilder(
               this.authenticationSystemSupport.getPrincipalElectionStrategy());
         AuthenticationTransaction transaction = AuthenticationTransaction.wrap(credential);
         this.authenticationSystemSupport.getAuthenticationTransactionManager().handle(transaction, builder);
         AuthenticationContext authenticationContext = builder.build(service);
         ServiceTicket serviceTicketId = this.centralAuthenticationService
               .grantServiceTicket(ticketGrantingTicketId, service, authenticationContext);
         request.setAttribute("serviceTicketId", serviceTicketId.getId());

         putWarnCookieIfRequestParameterPresent(request, response);

         return this.newEvent("warn");
      }
      catch (AuthenticationException var9)
      {
         return this.newEvent(AUTHENTICATION_FAILURE, var9);
      }
      catch (TicketCreationException var10)
      {
         this.LOGGER
               .warn("Invalid attempt to access service using renew=true with different credential. Ending sso " +
                     "session.");
         this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicketId);
         return this.newEvent("error");
      }
      catch (AbstractTicketException var11)
      {
         return this.newEvent("error", var11);
      }
   }

   private Event createTicketGrantingTicket(
         HttpServletRequest request, HttpServletResponse response, Credential credential,
         MessageContext messageContext)
   {
      try
      {
         HttpSession session = request.getSession();
         Service service = (Service) session.getAttribute("service");
         AuthenticationContextBuilder builder = new DefaultAuthenticationContextBuilder(
               this.authenticationSystemSupport.getPrincipalElectionStrategy());
         AuthenticationTransaction transaction = AuthenticationTransaction.wrap(credential);
         this.authenticationSystemSupport.getAuthenticationTransactionManager().handle(transaction, builder);
         AuthenticationContext authenticationContext = builder.build(service);

         // 獲取帳戶信息
         Authentication authentication = authenticationContext.getAuthentication();
         Principal principal = authentication.getPrincipal();
         SAccount account = (SAccount) principal.getAttributes().get("account");
         session.setAttribute("account", account);

         TicketGrantingTicket tgt =
               this.centralAuthenticationService.createTicketGrantingTicket(authenticationContext);
         String ticketValue = tgt != null ? tgt.getId() : null;
         request.setAttribute("ticket", ticketValue);

         session.setAttribute("ticket", ticketValue);

         putWarnCookieIfRequestParameterPresent(request, response);

         putPublicWorkstationToFlowIfRequestParameterPresent(request);
         return this.addWarningMessagesToMessageContextIfNeeded(tgt, messageContext) ?
                this.newEvent(SUCCESS_WITH_WARNINGS) : this.newEvent("success");
      }
      catch (AuthenticationException var9)
      {
         this.LOGGER.debug(var9.getMessage(), var9);
         return this.newEvent("authenticationFailure", var9);
      }
      catch (Exception var10)
      {
         this.LOGGER.debug(var10.getMessage(), var10);
         return this.newEvent("error", var10);
      }
   }

   private boolean addWarningMessagesToMessageContextIfNeeded(
         TicketGrantingTicket tgt, MessageContext messageContext)
   {
      boolean foundAndAddedWarnings = false;
      Iterator var5 = tgt.getAuthentication().getSuccesses().entrySet().iterator();

      while (var5.hasNext())
      {
         Map.Entry<String, HandlerResult> entry = (Map.Entry) var5.next();

         for (Iterator var7 = ((HandlerResult) entry.getValue()).getWarnings().iterator(); var7.hasNext();
              foundAndAddedWarnings = true)
         {
            MessageDescriptor message = (MessageDescriptor) var7.next();
            addWarningToContext(messageContext, message);
         }
      }

      return foundAndAddedWarnings;
   }

   private static void putPublicWorkstationToFlowIfRequestParameterPresent(HttpServletRequest request)
   {
      HttpSession session = request.getSession();
      if (StringUtils.isNotBlank(request.getParameter(PUBLIC_WORKSTATION_ATTRIBUTE)))
      {
         session.setAttribute(PUBLIC_WORKSTATION_ATTRIBUTE, Boolean.TRUE);
      }

   }

   private Event newEvent(String id)
   {
      return new Event(this, id);
   }

   private Event newEvent(String id, Exception error)
   {
      return new Event(this, id, new LocalAttributeMap<>("error", error));
   }

   private static void addWarningToContext(MessageContext context, MessageDescriptor warning)
   {
      MessageBuilder builder =
            (new MessageBuilder()).warning().code(warning.getCode()).defaultText(warning.getDefaultMessage())
                  .args(warning.getParams());
      context.addMessage(builder.build());
   }

   public void setCentralAuthenticationService(CentralAuthenticationService centralAuthenticationService)
   {
      this.centralAuthenticationService = centralAuthenticationService;
   }

   public void setWarnCookieGenerator(CookieGenerator warnCookieGenerator)
   {
      this.warnCookieGenerator = warnCookieGenerator;
   }

   public void setAuthenticationSystemSupport(AuthenticationSystemSupport authenticationSystemSupport)
   {
      this.authenticationSystemSupport = authenticationSystemSupport;
   }

   private void putWarnCookieIfRequestParameterPresent(HttpServletRequest request, HttpServletResponse response)
   {
      if (warnCookieGenerator != null)
      {
         LOGGER.debug("Evaluating request to determine if warning cookie should be generated");
         if (StringUtils.isNotBlank(request.getParameter(WARN)))
         {
            warnCookieGenerator.addCookie(response, "true");
         }
         else
         {
            warnCookieGenerator.removeCookie(response);
         }
      }
      else
      {
         LOGGER.debug("No warning cookie generator is defined");
      }
   }
}

五、記着修改訪問路徑映射。。。

相關文章
相關標籤/搜索