CAS单点登录方案配置

来源:互联网 发布:小学英语听力软件 编辑:程序博客网 时间:2024/06/07 03:26

CAS单点登录方案

本文将简介cas单点登录方案,如有不对,欢迎指正


web.xml配置

cas在网站中是以多个过滤器的方式去执行,如下配置:1.身份认证过滤器,2.ticket校验拦截器,3实现HttpServletRequest请求,可以通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户名,4.获取登录用户名,如AssertionHolder.getAssertion().getPrincipal().getName():

    <listener>        <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>    </listener>    <!-- 该过滤器用于实现单点登出功能,可选配置。 -->    <filter>        <filter-name>CAS Single Sign Out Filter</filter-name>        <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>    </filter>    <filter-mapping>        <filter-name>CAS Single Sign Out Filter</filter-name>        <url-pattern>/*</url-pattern>    </filter-mapping>    <!-- 该过滤器负责用户的认证工作,必须启用它 -->    <filter>        <filter-name>CASFilter</filter-name>        <filter-class>com.youyuan.chat.filter.AuthenticationFilter</filter-class>        <init-param>            <param-name>casServerLoginUrl</param-name>            <!-- 配置服务端登录地址 -->            <param-value>配置服务端登陆地址</param-value>        </init-param>        <init-param>        <!--配置,在session 中放入username的地址(后台工程项目地址)。 -->            <param-name>serverName</param-name>            <param-value>需要访问的项目的地址</param-value>        </init-param>        <!-- 配置白名单的url -->        <init-param>              <param-name>excludePaths</param-name>              <!-- 匹配/api/或者/resources/或者.txt结尾-->            <param-value>.*/(api/|resources/|.*\\.txt$).*</param-value>          </init-param>     </filter>    <filter-mapping>        <filter-name>CASFilter</filter-name>        <url-pattern>/*</url-pattern>    </filter-mapping>    <!-- 该过滤器负责对Ticket的校验工作,必须启用它 -->    <filter>        <filter-name>CAS Validation Filter</filter-name>        <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>        <!--配置服务端校验地址-->        <init-param>            <param-name>casServerUrlPrefix</param-name>            <param-value> 配置服务端校验地址</param-value>        </init-param>    <!--配置后台项目地址地址-->        <init-param>            <param-name>serverName</param-name>            <param-value>配置访问项目地址</param-value>        </init-param>    </filter>      <filter-mapping>        <filter-name>CAS Validation Filter</filter-name>        <url-pattern>/*</url-pattern>      </filter-mapping>     <!--  该过滤器负责实现HttpServletRequest请求,可以通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户名,可选配置 -->    <filter>        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>        <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>    </filter>     <filter-mapping>        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>        <url-pattern>/*</url-pattern>      </filter-mapping>    <!--  该过滤器org.jasig.cas.client.util.AssertioHolder用来获取登录用户名,如AssertionHolder.getAssertion().getPrincipal().getName() -->    <filter>        <filter-name>CAS Assertion Thread Local Filter</filter-name>        <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>    </filter>    <filter-mapping>        <filter-name>CAS Assertion Thread Local Filter</filter-name>        <url-pattern>/*</url-pattern>    </filter-mapping>    <!-- 以上是单点登录 -->

其中第一个身份认证拦截器可自定义实现,如下:

public class AuthenticationFilter extends AbstractCasFilter{      private static final Logger log = Logger.getLogger(AuthenticationFilter.class);     /**      * The URL to the CAS Server login.      */      private String casServerLoginUrl;      /**      * Whether to send the renew request or not.      */      private boolean renew = false;      /**      * Whether to send the gateway request or not.      */      private boolean gateway = false;      //添加属性,这里用来存放不过滤地址正则表达式,可以根据自己需求定制---1     private String excludePaths;      private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();      protected void initInternal(final FilterConfig filterConfig) throws ServletException {          if (!isIgnoreInitConfiguration()) {              super.initInternal(filterConfig);              setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null));              log.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl);              setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));              log.trace("Loaded renew parameter: " + this.renew);              setGateway(parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false")));              log.trace("Loaded gateway parameter: " + this.gateway);              final String gatewayStorageClass = getPropertyFromInitParams(filterConfig, "gatewayStorageClass", null);              if (gatewayStorageClass != null) {                  try {                      this.gatewayStorage = (GatewayResolver) Class.forName(gatewayStorageClass).newInstance();                  } catch (final Exception e) {                      log.error(e,e);                      throw new ServletException(e);                  }              }              //自定义添加代码,用来读取web配置文件中excludes属性值 ---2              excludePaths = getPropertyFromInitParams(filterConfig, "excludePaths", null);//filterConfig.getInitParameter("excludePaths");              excludePaths = excludePaths.trim();          }      }      public void init() {          super.init();          CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");      }      // url判断逻辑,这里大家可以根据自己需要来制订规则      private boolean isExclude(String uri){          boolean isInWhiteList = false;          if(excludePaths!=null&& uri!=null){              isInWhiteList = uri.matches(excludePaths);          }          return isInWhiteList;      }      public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {          final HttpServletRequest request = (HttpServletRequest) servletRequest;          final HttpServletResponse response = (HttpServletResponse) servletResponse;          final HttpSession session = request.getSession(false);          final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;          // 该判断是自定义的对符合条件的url进行通过处理 ---3          if(isExclude(request.getRequestURI())){              filterChain.doFilter(request, response);              return;          }          if (assertion != null) {              filterChain.doFilter(request, response);               return;          }          final String serviceUrl = constructServiceUrl(request, response);          final String ticket = CommonUtils.safeGetParameter(request,getArtifactParameterName());          final boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);          if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {              filterChain.doFilter(request, response);              return;          }          final String modifiedServiceUrl;          log.debug("no ticket and no assertion found");          if (this.gateway) {              log.debug("setting gateway attribute in session");              modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);          } else {              modifiedServiceUrl = serviceUrl;          }          if (log.isDebugEnabled()) {              log.debug("Constructed service url: " + modifiedServiceUrl);          }          final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);          if (log.isDebugEnabled()) {              log.debug("redirecting to \"" + urlToRedirectTo + "\"");          }          response.sendRedirect(urlToRedirectTo);      }      public final void setRenew(final boolean renew) {          this.renew = renew;      }      public final void setGateway(final boolean gateway) {          this.gateway = gateway;      }      public final void setCasServerLoginUrl(final String casServerLoginUrl) {          this.casServerLoginUrl = casServerLoginUrl;      }      public final void setGatewayStorage(final GatewayResolver gatewayStorage) {          this.gatewayStorage = gatewayStorage;      }  }

此自定义代码新增了对于部分不需要受限资源的判断。

cas认证流程:

用户首次登录时流程如下:

1)、用户浏览器访问系统A需登录受限资源,此时进行登录检查,发现未登录,然后进行获取票据操作,发现没有票据。

2)、系统A发现该请求需要登录,将请求重定向到认证中心,获取全局票据操作,没有,进行登录。

3)、认证中心呈现登录页面,用户登录,登录成功后,认证中心重定向请求到系统A,并附上认证通过令牌,此时认证中心同时生成了全局票据。

4)、此时再次进行登录检查,发现未登录,然后再次获取票据操作,此时可以获得票据(令牌),系统A与认证中心通信,验证令牌有效,证明用户已登录。

5)、系统A将受限资源返给用户。


其中需要注意的是:

  1. 浏览器与认证中心维持一个全局会话,浏览器与访问资源维护一个局部会话,当登出的时候,全局会话与局部会话一起被中断。
  2. ticket的认证,在cas服务端维使用map维护了用户登录票据CASTGC与ticket之间的关系,所以当cas客户端发起ticket真实性验证的时候,可以根据CASTGC获取真实ticket与需要比对的ticket对比,若一致,对比通过。

另外,推荐http://www.imooc.com/article/3720 cas系列文章,写的很棒,受益匪浅。

原创粉丝点击