developercenter完全剔除webflow

问题记录

1、配置文件报错,却可以正常运行(cas的war包和maven overlay 配置已删除),一脸懵逼。。。

2、spring加载bean,id="warnCookieGenerator"时

实现不是:org.jasig.cas.web.support.CookieRetrievingCookieGenerator

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

错误出现背景,比照cas中login-webflow.xml定义的流程改写为相应的controller和辅助类,

然后在在加载"warnCookieGenerator"实现时,class指定错误,使其识别不出HttpServletResponse实例,然后断言一直报错。

解决思路,比较将自己改造的流程和cas原始流程都debug一遍,比较相关类的实现是否一致。

3、将cas生成的tgt放入session中,下一次请求时,获取不到tgt属性。

原因可能是未修改的cas处理逻辑中,某一处将名为:“ticketGrantingTicketId“”的属性移除了。

暂时未找到移除位置或逻辑,将属性名改为“ticket”,问题解决。

 

改造过程

1、自定义认证实现

1.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.2、修改配置引入自定义认证实现

在deployerConfigContext.xml中找到:

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

下添加bean: 

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

2、实现自定义登陆数据模型

2.1、新建实现了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.2、因为本次使用cas不再使用spring-web-flow,所以不需修改:login-webflow.xml。只需要使用spring-mvc中的@requestBody等标签,直接接收Credential实例就好。

3、自定义返回用户信息

有两种方法,先了解小用户信息返回流程:

认证完之后,可以直接获取需要的账户信息,然后将账户信息放入handlerResult中的principle中,然后cas会一步步将principle放入authentication类中,我们可以从authentication中获取principle,然后返回前台。但是此过程中会有一个principleResolver参与,如果principleResolver是null,则将原来handlerResult中的principle给authentication,如果不为null,就解析一下在给authentication。cas默认的解析器是

PersonDirectoryPrincipalResolver类

此方法中判断解析器是否为空。

205428_f22a_3725601.png

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

210014_lsaL_3725601.png

具体是通过同类中的此方法获取

210047_1W5A_3725601.png

其中attributeRepository,是在spring中配置的实现。

因此自定义用户信息的两种方法:

第一:将信息直接放入result中的principle,然后将principleResolve设为null,但是还不知道如何设置为空,基础知识不好。。。

第二:自定义attributeRepository的实现类,重写getPerson方法,在此方法中将账户信息放进map,比如

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

4、将登陆流程翻译成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.1、代替流程文件中的初始域数据类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.2、流程中的第一个处理节点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.3、第二个节点: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.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.5、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.6、修改认证入口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");
      }
   }
}

5、记着修改访问路径映射。。。

 

 

转载于:https://my.oschina.net/u/3725601/blog/1618106

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值