cas 服务端、客服端详细配置

一、准备

    1、下载官方源码

       CAS-Server下载地址:https://www.apereo.org/projects/cas/download-cas

         CAS-Client下载地址:http://developer.jasig.org/cas-clients/

    版本:

  • CAS Server版本:cas-server-4.1.5
  • CAS Client版本:cas-client-3.3.3

  2、新建一个maven 聚合项目

  项目结构

   2.1、解压 cas-server-4.1.5后把cas-server-webapp所有文件拷贝到新建的maven项目中 

   2.2、pom.xml 配置源码

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>xyz.cas</groupId>
  <artifactId>xyz-cas</artifactId>
  <version>1.0.0.20170214</version>
  <packaging>pom</packaging>

  <name>xyz-cas</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <cas.version>4.1.5</cas.version>
  </properties>
  <modules>
      <module>xyz-cas-assemble</module>
  </modules>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    
     <!-- cas server -->
            <dependency>
                <groupId>org.jasig.cas</groupId>
                <artifactId>cas-server-webapp</artifactId>
                <version>${cas.version}</version>
                <type>war</type>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.jasig.cas</groupId>
                <artifactId>cas-server-webapp-support</artifactId>
                <version>${cas.version}</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.jasig.cas</groupId>
                <artifactId>cas-server-support-jdbc</artifactId>
                <version>${cas.version}</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.jasig.cas</groupId>
                <artifactId>cas-server-support-rest</artifactId>
                <version>${cas.version}</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                  <groupId>org.jasig.cas</groupId>
                  <artifactId>cas-server-integration-ehcache</artifactId>
                  <version>${cas.version}</version>
                  <scope>runtime</scope>
            </dependency>
         
     <!-- mybatis 依赖包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <!-- 分页包 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.17</version>
        </dependency>
        <!-- fastjson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.7</version>
            </dependency>
            <!-- httpclient -->
            <dependency>
                <groupId>commons-httpclient</groupId>
                <artifactId>commons-httpclient</artifactId>
                <version>3.1</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
            </dependency>
</dependencies>

    <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <configuration>
          <warName>cas</warName>
          <webResources>
            <resource>
              <directory>${basedir}/src/main/webapp/WEB-INF</directory>
              <filtering>true</filtering>
              <targetPath>WEB-INF</targetPath>
              <includes>
                <include>**/web.xml</include>
              </includes>
            </resource>
          </webResources>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>${maven-jetty-plugin.version}</version>
        <configuration>
          <webApp>
            <contextPath>/cas</contextPath>
          </webApp>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
    
View Code

  2.3、打包成war放到tomcat目录下webapps中,启动tomcat 访问到cas,能访问到cas 准备工作也就完成!

二、修改cas配置

   1、打开 deployerConfigContext.xml 文件,找到 serviceRegistryDao 修改成下图所示。

 <!--  <bean id="serviceRegistryDao" class="org.jasig.cas.services.JsonServiceRegistryDao"
          c:configDirectory="${service.registry.config.location:classpath:services}" /> -->

  <!-- 注册服务 -->  
    <bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl">
        <property name="registeredServices">
            <list>
                <bean class="org.jasig.cas.services.RegexRegisteredService">
                    <property name="id" value="0" />
                    <property name="name" value="HTTP and IMAP" />
                    <property name="description" value="Allows HTTP(S) and IMAP(S) protocols" />
                    <property name="serviceId" value="^(https?|imaps?)://.*" />
                    <property name="evaluationOrder" value="10000001" />
                </bean>
            </list>
        </property>
    </bean> 

  1.1、直接修改cas自带的服务文件,打开图上标记文件 

修改serviceId为  ^(https?|imaps?)://.*   这样我们的客服端就能访问到cas服务端了

 2、自定义用户登录验证

    2.1、找到deployerConfigContext.xml 文件 authenticationManager 把 primaryAuthenticationHandler 注释掉改成自己的验证类

    <bean id="authenticationManager" class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager">
        <constructor-arg>
            <map>
                <!--
                   | IMPORTANT
                   | Every handler requires a unique name.
                   | If more than one instance of the same handler class is configured, you must explicitly
                   | set its name to something other than its default name (typically the simple class name).
                   -->
                <entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />
               <!--  <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" /> -->
                <entry key-ref="ssoLoginHander" value-ref="primaryPrincipalResolver" />
            </map>
        </constructor-arg>

   2.2、加入注解context,在authenticationManager 它的上面添加

  <context:component-scan base-package="com.xyz.cas.*" />

完整的beans

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:util="http://www.springframework.org/schema/util"       xmlns:sec="http://www.springframework.org/schema/security"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

2.2.1、找到deployerConfigContext.xml 文件 attributeRepository 注释掉

<!--     <bean id="attributeRepository" class="org.jasig.services.persondir.support.NamedStubPersonAttributeDao"
          p:backingMap-ref="attrRepoBackingMap" />

    <util:map id="attrRepoBackingMap">
        <entry key="uid" value="uid" />
        <entry key="eduPersonAffiliation" value="eduPersonAffiliation" />
        <entry key="groupMembership" value="groupMembership" />
        <entry>
            <key><value>memberOf</value></key>
            <list>
                <value>faculty</value>
                <value>staff</value>
                <value>org</value>
            </list>
        </entry>
    </util:map> -->

 2.3、在biz下新建文件,如图

2.3.1、在login下新建SSOLoginHander类继承 AbstractPreAndPostProcessingAuthenticationHandler 用于登录验证,源码如下

package com.xyz.cas.login;

import java.security.GeneralSecurityException;
import java.util.List;

import javax.security.auth.login.FailedLoginException;

import org.jasig.cas.authentication.Credential;
import org.jasig.cas.authentication.HandlerResult;
import org.jasig.cas.authentication.PreventedException;
import org.jasig.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.xyz.cas.common.md5.Md5Encrypt;
import com.xyz.cas.model.MyCredential;
import com.xyz.dal.mybatis.maps.UserMapper;
import com.xyz.dal.mybatis.model.User;
import com.xyz.dal.mybatis.model.UserExample;

/**
 *
 *
 * @author 
 *AbstractPreAndPostProcessingAuthenticationHandler
 */
@Service("ssoLoginHander")
public class SSOLoginHander extends AbstractPreAndPostProcessingAuthenticationHandler {
    
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    public UserMapper userMapper;
    
    /**
     * @param credential
     * @return
     * @see org.jasig.cas.authentication.AuthenticationHandler#supports(org.jasig.cas.authentication.Credential)
     */
    @Override
    public boolean supports(Credential credential) {
        return true;
    }

    /**
     * @param credential
     * @return
     * @throws GeneralSecurityException
     * @throws PreventedException
     * @see org.jasig.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler#doAuthentication(org.jasig.cas.authentication.Credential)
     */
    @Override
    protected HandlerResult doAuthentication(Credential credential) throws GeneralSecurityException, PreventedException {
        MyCredential  credential2 =null;
        try
        {
        credential2= (MyCredential)credential;
        UserExample example=new UserExample();
        example.createCriteria().andLoginNameEqualTo(credential2.getUsername()).andPasswordEqualTo(Md5Encrypt.encrypt(credential2.getPassword()));
        List<User>list= userMapper.selectByExample(example);
        if (list.isEmpty()&&list.size()==0)
        {
            throw new FailedLoginException("账号密码错误");
        }
            
        } catch (Exception e)
        {
            logger.info("web login fail, password does not match username on record. ");
            e.printStackTrace();
            throw new FailedLoginException(e.getMessage());
        }
    

        //验证是否登录成功
        //if(result.isFail()){
            logger.info("=============================================================");
        //    logger.info("who:"+username);
            logger.info("web login fail, password does not match username on record. ");
            logger.info("=============================================================");
            //throw new FailedLoginException("password does not match username on record.");
        //}
    //    upu.setUserId(result.getList().get(0).getBaseId());
        return createHandlerResult(credential, this.principalFactory.createPrincipal(credential2.getUsername()), null);
    }

}
View Code

在dal下的java 下新建 MyCredential 类 用于接收用户名密码

package com.xyz.dal.mybatis.model;

import org.jasig.cas.authentication.UsernamePasswordCredential;


public class MyCredential extends UsernamePasswordCredential {
    
    private static final long serialVersionUID = 2693123647112406019L;
    //
    private String serviceUrl;
    //类型
    private String idtype;  
    //用户ID
    private String userId;
    //票据
    private String tgt; 

    
    
    public String getUserId()
    {
        return userId;
    }

    public void setUserId(String userId)
    {
        this.userId = userId;
    }

    public String getTgt()
    {
        return tgt;
    }

    public void setTgt(String tgt)
    {
        this.tgt = tgt;
    }

    public static long getSerialversionuid()
    {
        return serialVersionUID;
    }

    public void setIdtype(String idtype)
    {
        this.idtype = idtype;
    }

    public String getServiceUrl()
    {
        return serviceUrl;
    }

    public void setServiceUrl(String serviceUrl)
    {
        this.serviceUrl = serviceUrl;
    }

    public String getIdtype()
    {
        return idtype;
    }
}
View Code

在 attribute 下 新建UserStubPersonAttributeDao类 用于登录验证成功后返回信息给客户端,这里我使用了mybatis 来操作数据库

package com.xyz.cas.attribute;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jasig.services.persondir.IPersonAttributes;
import org.jasig.services.persondir.support.AttributeNamedPersonImpl;
import org.jasig.services.persondir.support.StubPersonAttributeDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.xyz.dal.mybatis.maps.UserMapper;
import com.xyz.dal.mybatis.model.MyCredential;
import com.xyz.dal.mybatis.model.User;
import com.xyz.dal.mybatis.model.UserExample;


@Service("attributeRepository")
public class UserStubPersonAttributeDao extends StubPersonAttributeDao {
    
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
     @Autowired
        public UserMapper userMapper;

    /**
     * @param uid
     * @return
     * @see org.jasig.services.persondir.support.StubPersonAttributeDao#getPerson(java.lang.String)
     */
    @Override
    public IPersonAttributes getPerson(String uid) {
        
        Map<String, List<Object>> attributes = new HashMap<String, List<Object>>();
        
        MyCredential mycredential = JSONObject.parseObject(uid, MyCredential.class);
        
        UserExample example=new UserExample();
        example.createCriteria().andLoginNameEqualTo(mycredential.getUsername());
        
        List<User>list= userMapper.selectByExample(example);
        
        mycredential.setUserId(list.get(0).getId());
        
        String userInfo=JSONObject.toJSONString(list);
        attributes.put("userInfo", Collections.singletonList((Object)userInfo));
       // attributes.put("permissionInfo", Collections.singletonList((Object)permissionInfo));
        //attributes.put("roleInfo", Collections.singletonList((Object)roleInfo));
        return new AttributeNamedPersonImpl(attributes);
    }

}
View Code

在assemble新建 org.jasig.cas.authentication.principal 包 新建类 PersonDirectoryPrincipalResolver

源码如下

/*
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License.  You may obtain a
 * copy of the License at the following location:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.jasig.cas.authentication.principal;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.validation.constraints.NotNull;

import org.jasig.cas.authentication.Credential;
import org.jasig.services.persondir.IPersonAttributeDao;
import org.jasig.services.persondir.IPersonAttributes;
import org.jasig.services.persondir.support.StubPersonAttributeDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;
import com.xyz.dal.mybatis.model.MyCredential;

/**
 * Resolves principals by querying a data source using the Jasig
 * <a href="http://developer.jasig.org/projects/person-directory/1.5.0-SNAPSHOT/apidocs/">Person Directory API</a>.
 * The {@link org.jasig.cas.authentication.principal.Principal#getAttributes()} are populated by the results of the
 * query and the principal ID may optionally be set by proving an attribute whose first non-null value is used;
 * otherwise the credential ID is used for the principal ID.
 *
 * @author Marvin S. Addison
 * @since 4.0.0
 *
 */
public class PersonDirectoryPrincipalResolver implements PrincipalResolver {

    /** Log instance. */
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    private boolean returnNullIfNoAttributes;

    /** Repository of principal attributes to be retrieved. */
    @NotNull
    private IPersonAttributeDao attributeRepository = new StubPersonAttributeDao(new HashMap<String, List<Object>>());

    /** Factory to create the principal type. **/
    @NotNull
    private PrincipalFactory principalFactory = new DefaultPrincipalFactory();

    /** Optional principal attribute name. */
    private String principalAttributeName;
    /**
     * @param credential
     * @return
     * @see org.jasig.cas.authentication.principal.PrincipalResolver#supports(org.jasig.cas.authentication.Credential)
     */
    public boolean supports(Credential credential)
    {
        return true;
    }
    /**
     * @param credential
     * @return
     * @see org.jasig.cas.authentication.principal.PrincipalResolver#resolve(org.jasig.cas.authentication.Credential)
     */
    public Principal resolve(Credential credential)
    {
         logger.debug("Attempting to resolve a principal...");

            String principalId = extractPrincipalId(credential);

            if (principalId == null) {
                logger.debug("Got null for extracted principal ID; returning null.");
                return null;
            }

            logger.debug("Creating SimplePrincipal for [{}]", principalId);
            final MyCredential  upu = (MyCredential)credential;;
            String principalIdExtend = JSONObject.toJSONString(upu);
            final IPersonAttributes personAttributes = this.attributeRepository.getPerson(principalIdExtend);
            final Map<String, List<Object>> attributes;

            if (personAttributes == null) {
                attributes = null;
            } else {
                attributes = personAttributes.getAttributes();
            }

            if (attributes == null || attributes.isEmpty()) {
                if (!this.returnNullIfNoAttributes) {
                    return this.principalFactory.createPrincipal(principalId);
                }
                return null;
            }

            final Map<String, Object> convertedAttributes = new HashMap<String,Object>();
            for (final Map.Entry<String, List<Object>> entry : attributes.entrySet()) {
                final String key = entry.getKey();
                final List<Object> values = entry.getValue();
                if (key.equalsIgnoreCase(this.principalAttributeName)) {
                    if (values.isEmpty()) {
                        logger.debug("{} is empty, using {} for principal", this.principalAttributeName, principalId);
                    } else {
                        principalId = values.get(0).toString();
                        logger.debug(
                                "Found principal attribute value {}; removing {} from attribute map.",
                                principalId,
                                this.principalAttributeName);
                    }
                } else {
                    convertedAttributes.put(key, values.size() == 1 ? values.get(0) : values);
                }
            }
           return this.principalFactory.createPrincipal(principalId, convertedAttributes);
    }

    public final void setAttributeRepository(final IPersonAttributeDao attributeRepository) {
        this.attributeRepository = attributeRepository;
    }

    public void setReturnNullIfNoAttributes(final boolean returnNullIfNoAttributes) {
        this.returnNullIfNoAttributes = returnNullIfNoAttributes;
    }

    /**
     * Sets the name of the attribute whose first non-null value should be used for the principal ID.
     *
     * @param attribute Name of attribute containing principal ID.
     */
    public void setPrincipalAttributeName(final String attribute) {
        this.principalAttributeName = attribute;
    }

    /**
     * Sets principal factory to create principal objects.
     *
     * @param principalFactory the principal factory
     */
    public void setPrincipalFactory(final PrincipalFactory principalFactory) {
        this.principalFactory = principalFactory;
    }

    /**
     * Extracts the id of the user from the provided credential. This method should be overridded by subclasses to
     * achieve more sophisticated strategies for producing a principal ID from a credential.
     *
     * @param credential the credential provided by the user.
     * @return the username, or null if it could not be resolved.
     */
    protected String extractPrincipalId(final Credential credential) {
        return credential.getId();
    }


}
View Code

修改 webflow下面的 login-webflow.xml,找到 credential 修改成上面定义的类,用于接收页面输入的用户名密码

<!--替换class 路径的  -->
<var name="credential" class="com.xyz.cas.model.MyCredential"/>

<!--页面新增登录参数 --> <view-state id="viewLoginForm" view="casLoginView" model="credential"> <binder> <binding property="username" required="true"/> <binding property="password" required="true"/> <!-- <binding property="你的参数名称" required="true"/> --> </binder> <on-entry> <set name="viewScope.commandName" value="'credential'"/> <!-- <evaluate expression="samlMetadataUIParserAction" /> --> </on-entry> <transition on="submit" bind="true" validate="true" to="realSubmit"/> </view-state>

修改完成自定义类路径后在当前login-webflow.xml中找到 节点id serviceUnauthorizedCheck  修改如下

    <end-state id="viewServiceErrorView" view="serviceErrorView"/>
 <!--   注释掉当前节点 -->
<!--     <decision-state id="serviceUnauthorizedCheck">
        <if test="flowScope.unauthorizedRedirectUrl != null"
            then="viewRedirectToUnauthorizedUrlView"
            else="viewServiceErrorView"/>
    </decision-state>

    <end-state id="viewRedirectToUnauthorizedUrlView" view="externalRedirect:#{flowScope.unauthorizedRedirectUrl}"/> -->
<!-- 把serviceErrorSsoView 替换成 viewServiceSsoErrorView --> <!-- <end-state id="viewServiceSsoErrorView" view="serviceErrorSsoView" /> --> <end-state id="viewServiceSsoErrorView" view="viewServiceSsoErrorView" /> <global-transitions> <transition to="viewLoginForm" on-exception="org.jasig.cas.services.UnauthorizedSsoServiceException"/> <transition to="viewServiceErrorView" on-exception="org.springframework.webflow.execution.repository.NoSuchFlowExecutionException"/>
<!-- 把 serviceUnauthorizedCheck 替换成 viewServiceErrorView --> <!-- <transition to="serviceUnauthorizedCheck" on-exception="org.jasig.cas.services.UnauthorizedServiceException"/> --> <transition to="viewServiceErrorView" on-exception="org.jasig.cas.services.UnauthorizedServiceException"/> </global-transitions>

ps:这部分不修改 使用tomcat启动访问后 报错 :CAS is Unavailable There was an error trying to complete your request. Please notify your support desk or try again.

查看日志显示:Unable to load class ' com.xyz.cas.model.MyCredential'

完整的 login-webflow.xml 源码

<?xml version="1.0" encoding="UTF-8"?>
<!--

    Licensed to Apereo under one or more contributor license
    agreements. See the NOTICE file distributed with this work
    for additional information regarding copyright ownership.
    Apereo licenses this file to you under the Apache License,
    Version 2.0 (the "License"); you may not use this file
    except in compliance with the License.  You may obtain a
    copy of the License at the following location:

      http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.

-->
<flow xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://www.springframework.org/schema/webflow"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow
                          http://www.springframework.org/schema/webflow/spring-webflow.xsd">

    <var name="credential" class="com.xyz.cas.model.MyCredential"/>
    <on-start>
        <evaluate expression="initialFlowSetupAction"/>
    </on-start>

    <action-state id="ticketGrantingTicketCheck">
        <evaluate expression="ticketGrantingTicketCheckAction"/>
        <transition on="notExists" to="gatewayRequestCheck"/>
        <transition on="invalid" to="terminateSession"/>
        <transition on="valid" to="hasServiceCheck"/>
    </action-state>

    <action-state id="terminateSession">
        <evaluate expression="terminateSessionAction.terminate(flowRequestContext)"/>
        <transition to="gatewayRequestCheck"/>
    </action-state>

    <decision-state id="gatewayRequestCheck">
        <if test="requestParameters.gateway != '' and requestParameters.gateway != null and flowScope.service != null"
            then="gatewayServicesManagementCheck" else="serviceAuthorizationCheck"/>
    </decision-state>

    <decision-state id="hasServiceCheck">
        <if test="flowScope.service != null" then="renewRequestCheck" else="viewGenericLoginSuccess"/>
    </decision-state>

    <decision-state id="renewRequestCheck">
        <if test="requestParameters.renew != '' and requestParameters.renew != null" then="serviceAuthorizationCheck"
            else="generateServiceTicket"/>
    </decision-state>

    <!-- Do a service authorization check early without the need to login first -->
    <action-state id="serviceAuthorizationCheck">
        <evaluate expression="serviceAuthorizationCheck"/>
        <transition to="generateLoginTicket"/>
    </action-state>

    <!--
        The "warn" action makes the determination of whether to redirect directly to the requested
        service or display the "confirmation" page to go back to the server.
    -->
    <decision-state id="warn">
        <if test="flowScope.warnCookieValue" then="showWarningView" else="redirect"/>
    </decision-state>

    <!--
    <action-state id="startAuthenticate">
        <action bean="x509Check" />
        <transition on="success" to="sendTicketGrantingTicket" />
        <transition on="warn" to="warn" />
        <transition on="error" to="generateLoginTicket" />
    </action-state>
     -->

    <action-state id="generateLoginTicket">
        <evaluate expression="generateLoginTicketAction.generate(flowRequestContext)"/>
        <transition on="generated" to="viewLoginForm"/>
    </action-state>

    <view-state id="viewLoginForm" view="casLoginView" model="credential">
        <binder>
            <binding property="username" required="true"/>
            <binding property="password" required="true"/>
        </binder>
        <on-entry>
            <set name="viewScope.commandName" value="'credential'"/>

            <!--
            <evaluate expression="samlMetadataUIParserAction" />
            -->
        </on-entry>
        <transition on="submit" bind="true" validate="true" to="realSubmit"/>
    </view-state>

    <action-state id="realSubmit">
        <evaluate
                expression="authenticationViaFormAction.submit(flowRequestContext, flowScope.credential, messageContext)"/>
        <transition on="warn" to="warn"/>
        <!--
        To enable AUP workflows, replace the 'success' transition with the following:
        <transition on="success" to="acceptableUsagePolicyCheck" />
        -->
        <transition on="success" to="sendTicketGrantingTicket"/>
        <transition on="successWithWarnings" to="showMessages"/>
        <transition on="authenticationFailure" to="handleAuthenticationFailure"/>
        <transition on="error" to="generateLoginTicket"/>
    </action-state>

    <!-- Enable AUP flow
    <action-state id="acceptableUsagePolicyCheck">
      <evaluate expression="acceptableUsagePolicyFormAction.verify(flowRequestContext, flowScope.credential, messageContext)" />
      <transition on="success" to="sendTicketGrantingTicket" />
      <transition to="acceptableUsagePolicyView" />
    </action-state>

    <view-state id="acceptableUsagePolicyView" view="casAcceptableUsagePolicyView">
        <transition on="submit" to="aupAcceptedAction" />
        <transition to="generateLoginTicket" />
    </view-state>

    <action-state id="aupAcceptedAction">
      <evaluate expression="acceptableUsagePolicyFormAction.submit(flowRequestContext, flowScope.credential, messageContext)" />
      <transition on="error" to="generateLoginTicket" />
      <transition on="success" to="sendTicketGrantingTicket" />
    </action-state>
    -->

    <view-state id="showMessages" view="casLoginMessageView">
        <on-entry>
            <evaluate expression="sendTicketGrantingTicketAction"/>
            <set name="requestScope.messages" value="messageContext.allMessages"/>
        </on-entry>
        <transition on="proceed" to="serviceCheck"/>
    </view-state>

    <action-state id="handleAuthenticationFailure">
        <evaluate expression="authenticationExceptionHandler.handle(currentEvent.attributes.error, messageContext)"/>
        <transition on="AccountDisabledException" to="casAccountDisabledView"/>
        <transition on="AccountLockedException" to="casAccountLockedView"/>
        <transition on="AccountPasswordMustChangeException" to="casMustChangePassView"/>
        <transition on="CredentialExpiredException" to="casExpiredPassView"/>
        <transition on="InvalidLoginLocationException" to="casBadWorkstationView"/>
        <transition on="InvalidLoginTimeException" to="casBadHoursView"/>
        <transition on="FailedLoginException" to="generateLoginTicket"/>
        <transition on="AccountNotFoundException" to="generateLoginTicket"/>
        <transition on="UNKNOWN" to="generateLoginTicket"/>
    </action-state>

    <action-state id="sendTicketGrantingTicket">
        <evaluate expression="sendTicketGrantingTicketAction"/>
        <transition to="serviceCheck"/>
    </action-state>

    <decision-state id="serviceCheck">
        <if test="flowScope.service != null" then="generateServiceTicket" else="viewGenericLoginSuccess"/>
    </decision-state>

    <action-state id="generateServiceTicket">
        <evaluate expression="generateServiceTicketAction"/>
        <transition on="success" to="warn"/>
        <transition on="authenticationFailure" to="handleAuthenticationFailure"/>
        <transition on="error" to="generateLoginTicket"/>
        <transition on="gateway" to="gatewayServicesManagementCheck"/>
    </action-state>

    <action-state id="gatewayServicesManagementCheck">
        <evaluate expression="gatewayServicesManagementCheck"/>
        <transition on="success" to="redirect"/>
    </action-state>

    <action-state id="redirect">
        <evaluate expression="flowScope.service.getResponse(requestScope.serviceTicketId)"
                  result-type="org.jasig.cas.authentication.principal.Response" result="requestScope.response"/>
        <transition to="postRedirectDecision"/>
    </action-state>

    <decision-state id="postRedirectDecision">
        <if test="requestScope.response.responseType.name() == 'POST'" then="postView" else="redirectView"/>
    </decision-state>

    <!--
        the "viewGenericLoginSuccess" is the end state for when a user attempts to login without coming directly from a service.
        They have only initialized their single-sign on session.
    -->

    <end-state id="viewGenericLoginSuccess" view="casGenericSuccessView">
        <on-entry>
            <evaluate expression="genericSuccessViewAction.getAuthenticationPrincipal(flowScope.ticketGrantingTicketId)"
                      result="requestScope.principal"
                      result-type="org.jasig.cas.authentication.principal.Principal"/>
        </on-entry>
    </end-state>


    <!--
    The "showWarningView" end state is the end state for when the user has requested privacy settings (to be "warned") to be turned on.  It delegates to a
    view defines in default_views.properties that display the "Please click here to go to the service." message.
    -->
    <end-state id="showWarningView" view="casConfirmView"/>


    <!-- Password policy failure states -->
    <end-state id="abstactPasswordChangeView">
        <on-entry>
            <set name="flowScope.passwordPolicyUrl" value="passwordPolicy.passwordPolicyUrl"/>
        </on-entry>
    </end-state>
    <end-state id="casExpiredPassView" view="casExpiredPassView" parent="#abstactPasswordChangeView"/>
    <end-state id="casMustChangePassView" view="casMustChangePassView" parent="#abstactPasswordChangeView"/>
    <end-state id="casAccountDisabledView" view="casAccountDisabledView"/>
    <end-state id="casAccountLockedView" view="casAccountLockedView"/>
    <end-state id="casBadHoursView" view="casBadHoursView"/>
    <end-state id="casBadWorkstationView" view="casBadWorkstationView"/>

    <end-state id="postView" view="postResponseView">
        <on-entry>
            <set name="requestScope.parameters" value="requestScope.response.attributes"/>
            <set name="requestScope.originalUrl" value="flowScope.service.id"/>
        </on-entry>
    </end-state>

    <!--
        The "redirect" end state allows CAS to properly end the workflow while still redirecting
        the user back to the service required.
    -->
    <end-state id="redirectView" view="externalRedirect:#{requestScope.response.url}"/>

    <end-state id="viewServiceErrorView" view="serviceErrorView"/>

    <!-- <decision-state id="serviceUnauthorizedCheck">
        <if test="flowScope.unauthorizedRedirectUrl != null"
            then="viewRedirectToUnauthorizedUrlView"
            else="viewServiceErrorView"/>
    </decision-state>

    <end-state id="viewRedirectToUnauthorizedUrlView" view="externalRedirect:#{flowScope.unauthorizedRedirectUrl}"/> -->
    <end-state id="viewServiceSsoErrorView" view="viewServiceSsoErrorView" />

    <global-transitions>
        <transition to="viewLoginForm" on-exception="org.jasig.cas.services.UnauthorizedSsoServiceException"/>
        <transition to="viewServiceErrorView"
                    on-exception="org.springframework.webflow.execution.repository.NoSuchFlowExecutionException"/>
        <transition to="viewServiceErrorView" on-exception="org.jasig.cas.services.UnauthorizedServiceException"/>
    </global-transitions>
</flow>
View Code

打开如图所示的文件

 在 <cas:user>${fn:escapeXml(principal.id)}</cas:user> 后面添加下面这段,添加后客服端才能接收返回的信息

<c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes) > 0}">
            <cas:attributes>
                <c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}">
                    <cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>
                </c:forEach>
            </cas:attributes>
        </c:if>

 三、客服端配置 

客户端项目结构如下

1、在客服端pom.xml中增加一下依赖包

            <!-- Spring 整合Shiro需要的依赖 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.1</version>
        </dependency>
         <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-cas</artifactId>
                <version>1.2.4</version>
       </dependency>

 2、在xyz-domo-web 下srcmain esources 下新建包 cache,在cache 下新建文件 ehcache.xsd 

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" version="1.7">

    <xs:element name="ehcache">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="1" minOccurs="0" ref="diskStore"/>
                <xs:element maxOccurs="1" minOccurs="0" ref="sizeOfPolicy"/>
                <xs:element maxOccurs="1" minOccurs="0" ref="transactionManagerLookup"/>
                <xs:element maxOccurs="1" minOccurs="0" ref="cacheManagerEventListenerFactory"/>
                <xs:element maxOccurs="unbounded" minOccurs="0" ref="cacheManagerPeerProviderFactory"/>
                <xs:element maxOccurs="unbounded" minOccurs="0" ref="cacheManagerPeerListenerFactory"/>
                <xs:element maxOccurs="1" minOccurs="0" ref="terracottaConfig"/>
                <xs:element maxOccurs= "1" minOccurs="0" ref="defaultCache"/>
                <xs:element maxOccurs="unbounded" minOccurs="0" ref="cache"/>
            </xs:sequence>
            <xs:attribute name="name" use="optional"/>
            <xs:attribute default="true" name="updateCheck" type="xs:boolean" use="optional"/>
            <xs:attribute default="autodetect" name="monitoring" type="monitoringType" use="optional"/>
            <xs:attribute default="true" name="dynamicConfig" type="xs:boolean" use="optional"/>
            <xs:attribute default="15" name="defaultTransactionTimeoutInSeconds" type="xs:integer" use="optional"/>
            <xs:attribute default="0" name="maxBytesLocalHeap" type="memoryUnitOrPercentage" use="optional"/>
            <xs:attribute default="0" name="maxBytesLocalOffHeap" type="memoryUnit" use="optional"/>
            <xs:attribute default="0" name="maxBytesLocalDisk" type="memoryUnit" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="diskStore">
        <xs:complexType>
            <xs:attribute name="path" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="transactionManagerLookup">
        <xs:complexType>
            <xs:attribute name="class" use="required"/>
            <xs:attribute name="properties" use="optional"/>
            <xs:attribute name="propertySeparator" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="cacheManagerEventListenerFactory">
        <xs:complexType>
            <xs:attribute name="class" use="required"/>
            <xs:attribute name="properties" use="optional"/>
            <xs:attribute name="propertySeparator" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="cacheManagerPeerProviderFactory">
        <xs:complexType>
            <xs:attribute name="class" use="required"/>
            <xs:attribute name="properties" use="optional"/>
            <xs:attribute name="propertySeparator" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="cacheManagerPeerListenerFactory">
        <xs:complexType>
            <xs:attribute name="class" use="required"/>
            <xs:attribute name="properties" use="optional"/>
            <xs:attribute name="propertySeparator" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="terracottaConfig">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="1" minOccurs="0" name="tc-config">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:any maxOccurs="unbounded" minOccurs="0" processContents="skip"/>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
            <xs:attribute default="localhost:9510" name="url" use="optional"/>
            <xs:attribute name="rejoin" type="xs:boolean" use="optional" default="false"/>
        </xs:complexType>
    </xs:element>
    <!-- add clone support for addition of cacheExceptionHandler. Important! -->
    <xs:element name="defaultCache">
        <xs:complexType>
            <xs:sequence>
                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheEventListenerFactory"/>
                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheExtensionFactory"/>
                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheLoaderFactory"/>
                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheDecoratorFactory"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="bootstrapCacheLoaderFactory"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="cacheExceptionHandlerFactory"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="pinning"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="terracotta"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="cacheWriter"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="copyStrategy"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="elementValueComparator"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="sizeOfPolicy"/>
            </xs:sequence>
            <xs:attribute name="diskExpiryThreadIntervalSeconds" type="xs:integer" use="optional"/>
            <xs:attribute name="diskSpoolBufferSizeMB" type="xs:integer" use="optional"/>
            <xs:attribute name="diskPersistent" type="xs:boolean" use="optional"/>
            <xs:attribute name="diskAccessStripes" type="xs:integer" use="optional" default="1"/>
            <xs:attribute name="eternal" type="xs:boolean" use="optional" default="false"/>
            <xs:attribute name="maxElementsInMemory" type="xs:integer" use="optional"/>
            <xs:attribute name="maxEntriesLocalHeap" type="xs:integer" use="optional"/>
            <xs:attribute name="clearOnFlush" type="xs:boolean" use="optional"/>
            <xs:attribute name="memoryStoreEvictionPolicy" type="xs:string" use="optional"/>
            <xs:attribute name="overflowToDisk" type="xs:boolean" use="optional"/>
            <xs:attribute name="timeToIdleSeconds" type="xs:integer" use="optional"/>
            <xs:attribute name="timeToLiveSeconds" type="xs:integer" use="optional"/>
            <xs:attribute name="maxElementsOnDisk" type="xs:integer" use="optional"/>
            <xs:attribute name="maxEntriesLocalDisk" type="xs:integer" use="optional"/>
            <xs:attribute name="transactionalMode" type="transactionalMode" use="optional" default="off"/>
            <xs:attribute name="statistics" type="xs:boolean" use="optional" default="false"/>
            <xs:attribute name="copyOnRead" type="xs:boolean" use="optional" default="false"/>
            <xs:attribute name="copyOnWrite" type="xs:boolean" use="optional" default="false"/>
            <xs:attribute name="cacheLoaderTimeoutMillis" type="xs:integer" use="optional" default="0"/>
            <xs:attribute name="overflowToOffHeap" type="xs:boolean" use="optional" default="false"/>
            <xs:attribute name="maxMemoryOffHeap" type="xs:string" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="cache">
        <xs:complexType>
            <xs:sequence>
                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheEventListenerFactory"/>
                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheExtensionFactory"/>
                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheLoaderFactory"/>
                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheDecoratorFactory"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="bootstrapCacheLoaderFactory"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="cacheExceptionHandlerFactory"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="pinning"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="terracotta"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="cacheWriter"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="copyStrategy"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="searchable"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="elementValueComparator"/>
                <xs:element minOccurs="0" maxOccurs="1" ref="sizeOfPolicy"/>
            </xs:sequence>
            <xs:attribute name="diskExpiryThreadIntervalSeconds" type="xs:integer" use="optional"/>
            <xs:attribute name="diskSpoolBufferSizeMB" type="xs:integer" use="optional"/>
            <xs:attribute name="diskPersistent" type="xs:boolean" use="optional"/>
            <xs:attribute name="diskAccessStripes" type="xs:integer" use="optional" default="1"/>
            <xs:attribute name="eternal" type="xs:boolean" use="optional" default="false"/>
            <xs:attribute name="maxElementsInMemory" type="xs:integer" use="optional"/>
            <xs:attribute name="maxEntriesLocalHeap" type="xs:integer" use="optional"/>
            <xs:attribute name="memoryStoreEvictionPolicy" type="xs:string" use="optional"/>
            <xs:attribute name="clearOnFlush" type="xs:boolean" use="optional"/>
            <xs:attribute name="name" type="xs:string" use="required"/>
            <xs:attribute name="overflowToDisk" type="xs:boolean" use="optional"/>
            <xs:attribute name="timeToIdleSeconds" type="xs:integer" use="optional"/>
            <xs:attribute name="timeToLiveSeconds" type="xs:integer" use="optional"/>
            <xs:attribute name="maxElementsOnDisk" type="xs:integer" use="optional"/>
            <xs:attribute name="maxEntriesLocalDisk" type="xs:integer" use="optional"/>
            <xs:attribute name="transactionalMode" type="transactionalMode" use="optional" default="off" />
            <xs:attribute name="statistics" type="xs:boolean" use="optional" default="false"/>
            <xs:attribute name="copyOnRead" type="xs:boolean" use="optional" default="false"/>
            <xs:attribute name="copyOnWrite" type="xs:boolean" use="optional" default="false"/>
            <xs:attribute name="logging" type="xs:boolean" use="optional" default="false"/>
            <xs:attribute name="cacheLoaderTimeoutMillis" type="xs:integer" use="optional" default="0"/>
            <xs:attribute name="overflowToOffHeap" type="xs:boolean" use="optional" default="false"/>
            <xs:attribute name="maxMemoryOffHeap" type="xs:string" use="optional"/>
            <xs:attribute default="0" name="maxBytesLocalHeap" type="memoryUnitOrPercentage" use="optional"/>
            <xs:attribute default="0" name="maxBytesLocalOffHeap" type="memoryUnitOrPercentage" use="optional"/>
            <xs:attribute default="0" name="maxBytesLocalDisk" type="memoryUnitOrPercentage" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="cacheEventListenerFactory">
        <xs:complexType>
            <xs:attribute name="class" use="required"/>
            <xs:attribute name="properties" use="optional"/>
            <xs:attribute name="propertySeparator" use="optional"/>
            <xs:attribute name="listenFor" use="optional" type="notificationScope" default="all"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="bootstrapCacheLoaderFactory">
        <xs:complexType>
            <xs:attribute name="class" use="required"/>
            <xs:attribute name="properties" use="optional"/>
            <xs:attribute name="propertySeparator" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="cacheExtensionFactory">
        <xs:complexType>
            <xs:attribute name="class" use="required"/>
            <xs:attribute name="properties" use="optional"/>
            <xs:attribute name="propertySeparator" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="cacheExceptionHandlerFactory">
        <xs:complexType>
            <xs:attribute name="class" use="required"/>
            <xs:attribute name="properties" use="optional"/>
            <xs:attribute name="propertySeparator" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="cacheLoaderFactory">
        <xs:complexType>
            <xs:attribute name="class" use="required"/>
            <xs:attribute name="properties" use="optional"/>
            <xs:attribute name="propertySeparator" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="cacheDecoratorFactory">
        <xs:complexType>
            <xs:attribute name="class" use="required"/>
            <xs:attribute name="properties" use="optional"/>
            <xs:attribute name="propertySeparator" use="optional"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="searchAttribute">
        <xs:complexType>
            <xs:attribute name="name" use="required" type="xs:string"/>
            <xs:attribute name="expression" type="xs:string"/>
            <xs:attribute name="class" type="xs:string"/>
            <xs:attribute name="properties" use="optional"/>
            <xs:attribute name="propertySeparator" use="optional"/>
        </xs:complexType>
    </xs:element>

    <xs:element name="searchable">
      <xs:complexType>
        <xs:sequence>
          <xs:element minOccurs="0" maxOccurs="unbounded" ref="searchAttribute"/>
        </xs:sequence>
        <xs:attribute name="keys" use="optional" type="xs:boolean" default="true"/>
        <xs:attribute name="values" use="optional" type="xs:boolean" default="true"/>
      </xs:complexType>
    </xs:element>

    <xs:element name="pinning">
        <xs:complexType>
            <xs:attribute name="store" use="required" type="pinningStoreType"/>
        </xs:complexType>
    </xs:element>

    <xs:element name="terracotta">
        <xs:complexType>
            <xs:sequence>
                <xs:element minOccurs="0" maxOccurs="1" ref="nonstop"/>
            </xs:sequence>
            <xs:attribute name="clustered" use="optional" type="xs:boolean" default="true"/>
            <xs:attribute name="valueMode" use="optional" type="terracottaCacheValueType" default="serialization"/>
            <xs:attribute name="coherentReads" use="optional" type="xs:boolean" default="true"/>
            <xs:attribute name="localKeyCache" use="optional" type="xs:boolean" default="false"/>
            <xs:attribute name="localKeyCacheSize" use="optional" type="xs:positiveInteger" default="300000"/>
            <xs:attribute name="orphanEviction" use="optional" type="xs:boolean" default="true"/>
            <xs:attribute name="orphanEvictionPeriod" use="optional" type="xs:positiveInteger" default="4"/>
            <xs:attribute name="copyOnRead" use="optional" type="xs:boolean" default="false"/>
            <xs:attribute name="coherent" use="optional" type="xs:boolean" default="false"/>
            <xs:attribute name="consistency" use="optional" type="consistencyType" default="eventual"/>
            <xs:attribute name="synchronousWrites" use="optional" type="xs:boolean" default="false"/>
            <xs:attribute name="storageStrategy" use="optional" type="storageStrategyType" default="DCV2"/>
            <xs:attribute name="concurrency" use="optional" type="xs:nonNegativeInteger" default="0"/>
            <xs:attribute name="localCacheEnabled" use="optional" type="xs:boolean" default="true"/>
            <xs:attribute name="compressionEnabled" use="optional" type="xs:boolean" default="false"/>
        </xs:complexType>
    </xs:element>
    <xs:simpleType name="consistencyType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="strong" />
            <xs:enumeration value="eventual" />
        </xs:restriction>
    </xs:simpleType>
    <xs:element name="nonstop">
        <xs:complexType>
            <xs:sequence>
                <xs:element minOccurs="0" maxOccurs="1" ref="timeoutBehavior"/>
            </xs:sequence>
            <xs:attribute name="enabled" use="optional" type="xs:boolean" default="true"/>
            <xs:attribute name="immediateTimeout" use="optional" type="xs:boolean" default="false"/>
            <xs:attribute name="timeoutMillis" use="optional" type="xs:positiveInteger" default="30000"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="timeoutBehavior">
        <xs:complexType>
            <xs:attribute name="type" use="optional" type="timeoutBehaviorType" default="exception"/>
            <xs:attribute name="properties" use="optional" default=""/>
            <xs:attribute name="propertySeparator" use="optional" default=","/>
        </xs:complexType>
    </xs:element>
    <xs:simpleType name="timeoutBehaviorType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="noop" />
            <xs:enumeration value="exception" />
            <xs:enumeration value="localReads" />
        </xs:restriction>
    </xs:simpleType>
    <xs:simpleType name="monitoringType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="autodetect"/>
            <xs:enumeration value="on"/>
            <xs:enumeration value="off"/>
        </xs:restriction>
    </xs:simpleType>
    <xs:simpleType name="pinningStoreType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="localHeap" />
            <xs:enumeration value="localMemory" />
            <xs:enumeration value="inCache" />
        </xs:restriction>
    </xs:simpleType>
    <xs:simpleType name="terracottaCacheValueType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="serialization" />
            <xs:enumeration value="identity" />
        </xs:restriction>
    </xs:simpleType>
    <xs:simpleType name="storageStrategyType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="classic" />
            <xs:enumeration value="DCV2" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="transactionalMode">
        <xs:restriction base="xs:string">
            <xs:enumeration value="off"/>
            <xs:enumeration value="xa_strict"/>
            <xs:enumeration value="xa"/>
            <xs:enumeration value="local"/>
        </xs:restriction>
    </xs:simpleType>

    <xs:element name="cacheWriter">
        <xs:complexType>
            <xs:sequence >
                <xs:element minOccurs="0" maxOccurs="1" ref="cacheWriterFactory"/>
            </xs:sequence>
            <xs:attribute name="writeMode" use="optional" type="writeModeType" default="write-through"/>
            <xs:attribute name="notifyListenersOnException" use="optional" type="xs:boolean" default="false"/>
            <xs:attribute name="minWriteDelay" use="optional" type="xs:nonNegativeInteger" default="1"/>
            <xs:attribute name="maxWriteDelay" use="optional" type="xs:nonNegativeInteger" default="1"/>
            <xs:attribute name="rateLimitPerSecond" use="optional" type="xs:nonNegativeInteger" default="0"/>
            <xs:attribute name="writeCoalescing" use="optional" type="xs:boolean" default="false"/>
            <xs:attribute name="writeBatching" use="optional" type="xs:boolean" default="false"/>
            <xs:attribute name="writeBatchSize" use="optional" type="xs:positiveInteger" default="1"/>
            <xs:attribute name="retryAttempts" use="optional" type="xs:nonNegativeInteger" default="0"/>
            <xs:attribute name="retryAttemptDelaySeconds" use="optional" type="xs:nonNegativeInteger" default="1"/>
            <xs:attribute name="writeBehindConcurrency" use="optional" type="xs:nonNegativeInteger" default="1"/>
            <xs:attribute name="writeBehindMaxQueueSize" use="optional" type="xs:nonNegativeInteger" default="0"/>
        </xs:complexType>
    </xs:element>
    <xs:simpleType name="writeModeType">
        <xs:restriction base="xs:string">
            <xs:enumeration value="write-through" />
            <xs:enumeration value="write-behind" />
        </xs:restriction>
    </xs:simpleType>
    <xs:element name="cacheWriterFactory">
        <xs:complexType>
            <xs:attribute name="class" use="required"/>
            <xs:attribute name="properties" use="optional"/>
            <xs:attribute name="propertySeparator" use="optional"/>
        </xs:complexType>
    </xs:element>

    <xs:element name="copyStrategy">
        <xs:complexType>
            <xs:attribute name="class" use="required" type="xs:string" />
        </xs:complexType>
    </xs:element>

    <xs:element name="elementValueComparator">
        <xs:complexType>
            <xs:attribute name="class" use="required" type="xs:string" />
        </xs:complexType>
    </xs:element>

    <xs:element name="sizeOfPolicy">
        <xs:complexType>
            <xs:attribute name="maxDepth" use="required" type="xs:integer" />
            <xs:attribute name="maxDepthExceededBehavior" use="optional" default="continue" type="maxDepthExceededBehavior" />
        </xs:complexType>
    </xs:element>

    <xs:simpleType name="maxDepthExceededBehavior">
        <xs:restriction base="xs:string">
            <xs:enumeration value="continue"/>
            <xs:enumeration value="abort"/>
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="notificationScope">
        <xs:restriction base="xs:string">
            <xs:enumeration value="local"/>
            <xs:enumeration value="remote"/>
            <xs:enumeration value="all"/>
        </xs:restriction>
    </xs:simpleType>
    <xs:simpleType name="memoryUnit">
        <xs:restriction base="xs:token">
            <xs:pattern value="[0-9]+[bBkKmMgG]?"/>
        </xs:restriction>
    </xs:simpleType>
    <xs:simpleType name="memoryUnitOrPercentage">
        <xs:restriction base="xs:token">
            <xs:pattern value="([0-9]+[bBkKmMgG]?|100%|[0-9]{1,2}%)"/>
        </xs:restriction>
    </xs:simpleType>
</xs:schema>
View Code

2.1、新建shiro-cache.xml 文件 

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="
     http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context 
     http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/util
     http://www.springframework.org/schema/util/spring-util-3.0.xsd"
    default-lazy-init="true">
    
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!-- 设定角色的登录链接,这里为cas登录页面的链接可配置回调地址  -->
        <property name="loginUrl" value="${sso.shiro.login.url}" />
        <!-- <property name="loginUrl" value="http://192.168.1.52:8181/cas/login?service=http://192.168.1.40:7040/cas" /> -->
        
        <property name="filters">
            <util:map>
                <!-- 添加casFilter到shiroFilter -->
                <entry key="casFilter" value-ref="casFilter"/>
                <entry key="authc">
                    <bean class="com.xyz.domo.filter.FormAuthenticationFilterExt"></bean><!--修改为自己类路径-->
                </entry>
            </util:map>
            
        </property> 
        <property name="filterChainDefinitions">
            <value>                
                <!-- /shiro-cas = casFilter -->
                <!-- /admin/** = roles[ROLE_USER] -->
                /cas = casFilter
                /version/uploadImage = anon
                /anon/** = anon
                /druid/** = anon
                /** = authc

            </value>
        </property>
    </bean>
    
    <bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
        <!-- 配置验证错误时的失败页面  -->
        <property name="failureUrl" value="/error.jsp"/>
    </bean>
    
    <bean id="casRealm" class="com.xyz.domo.controller.realm.MyCasRealm">
        <property name="defaultRoles" value="ROLE_USER"/>        
        <property name="casServerUrlPrefix" value="${sso.cas.url.prefix}"/>
        <!-- 客户端的回调地址设置,必须和下面的shiro-cas过滤器拦截的地址一致 -->
        <property name="casService" value="${sso.callback.url}"/>
        <!-- <property name="casService" value="http://192.168.1.40:7040/cas"/> -->
    </bean>
    
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">      
<!--         <property name="cacheManager" ref="shiroCacheManager" /> -->
<!--         <property name="sessionManager" ref="sessionManager" /> -->
        <property name="realm" ref="casRealm"/>
        <property name="subjectFactory" ref="casSubjectFactory"/>
    </bean>
    
    <!-- 如果要实现cas的remember me的功能,需要用到下面这个bean,并设置到securityManager的subjectFactory中 -->
    <bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory"/>

    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>
    
    <!-- 本地缓存-->
    <bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
        <property name="configLocation" value="classpath:cache/shiro-cache.xml"/>  
    </bean>  
    <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">  
        <property name="cacheManager" ref="cacheManagerFactory"/>  
    </bean>

    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
        <property name="arguments" ref="securityManager"/>
    </bean>
    
    <!-- 处理shiro抛出的异常 -->
    <bean id="handlerExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="org.apache.shiro.authz.UnauthorizedException">authError</prop>
                <prop key="org.apache.shiro.authz.AuthorizationException">authError</prop>
               <!--  <prop key="java.lang.Exception">error</prop> -->
            </props>
        </property>
    </bean> 
</beans>
View Code

3、在srcmain esources 下新建包 spring,在spring下新建 shiro-spring.xml 

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="
     http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context 
     http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/util
     http://www.springframework.org/schema/util/spring-util-3.0.xsd"
    default-lazy-init="true">
    
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!-- 设定角色的登录链接,这里为cas登录页面的链接可配置回调地址  -->
        <property name="loginUrl" value="${sso.shiro.login.url}" />
        <!-- <property name="loginUrl" value="http://192.168.1.52:8181/cas/login?service=http://192.168.1.40:7040/cas" /> -->
        
        <property name="filters">
            <util:map>
                <!-- 添加casFilter到shiroFilter -->
                <entry key="casFilter" value-ref="casFilter"/>
                <entry key="authc">
                    <bean class="com.xyz.domo.filter.FormAuthenticationFilterExt"></bean><!--修改为自己类路径-->
                </entry>
            </util:map>
            
        </property> 
        <property name="filterChainDefinitions">
            <value>                
                <!-- /shiro-cas = casFilter -->
                <!-- /admin/** = roles[ROLE_USER] -->
                /cas = casFilter
                /version/uploadImage = anon
                /anon/** = anon
                /druid/** = anon
                /** = authc

            </value>
        </property>
    </bean>
    
    <bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
        <!-- 配置验证错误时的失败页面  -->
        <property name="failureUrl" value="/error.jsp"/>
    </bean>
    
    <bean id="casRealm" class="com.xyz.domo.controller.realm.MyCasRealm">
        <property name="defaultRoles" value="ROLE_USER"/>        
        <property name="casServerUrlPrefix" value="${sso.cas.url.prefix}"/>
        <!-- 客户端的回调地址设置,必须和下面的shiro-cas过滤器拦截的地址一致 -->
        <property name="casService" value="${sso.callback.url}"/>
        <!-- <property name="casService" value="http://192.168.1.40:7040/cas"/> -->
    </bean>
    
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">      
<!--         <property name="cacheManager" ref="shiroCacheManager" /> -->
<!--         <property name="sessionManager" ref="sessionManager" /> -->
        <property name="realm" ref="casRealm"/>
        <property name="subjectFactory" ref="casSubjectFactory"/>
    </bean>
    
    <!-- 如果要实现cas的remember me的功能,需要用到下面这个bean,并设置到securityManager的subjectFactory中 -->
    <bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory"/>

    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>
    
    <!-- 本地缓存-->
    <bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
        <property name="configLocation" value="classpath:cache/shiro-cache.xml"/>  
    </bean>  
    <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">  
        <property name="cacheManager" ref="cacheManagerFactory"/>  
    </bean>

    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
        <property name="arguments" ref="securityManager"/>
    </bean>
    
    <!-- 处理shiro抛出的异常 -->
    <bean id="handlerExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="org.apache.shiro.authz.UnauthorizedException">authError</prop>
                <prop key="org.apache.shiro.authz.AuthorizationException">authError</prop>
               <!--  <prop key="java.lang.Exception">error</prop> -->
            </props>
        </property>
    </bean> 
</beans>
View Code

3.1、新建文件 spring-cas.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="
     http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context 
     http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/util
     http://www.springframework.org/schema/util/spring-util-3.0.xsd"
    default-lazy-init="true">

    <context:component-scan base-package="com.xyz.*" />

    <context:property-placeholder location="classpath*:spring/easyhome-${spring.profiles.active}.properties" />

    <bean name="authenticationFilter"
        class="org.jasig.cas.client.authentication.AuthenticationFilter">
        <property name="casServerLoginUrl" value="${sso.cas.url.prefix}"></property>
        <property name="renew" value="false"></property>
        <property name="gateway" value="false"></property>
        <property name="serverName" value="${sso.server.name}"></property>
    </bean>

    <bean name="ticketValidationFilter"
        class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter">
        <property name="serverName" value="${sso.server.name}"></property>
        <property name="redirectAfterValidation" value="false"></property>
        <property name="ticketValidator">
            <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <constructor-arg index="0" value="${sso.cas.url.prefix}" />
            </bean>
        </property>
    </bean>
</beans>
View Code

4、在 srcmainjava 下新建包 com.xyz.domo.filter ,新建类 FormAuthenticationFilterExt

/*
 * CHONGQING XYZ TECH CO.,LTD
 * Copyright (c) 2015 All Rights Reserved.
 */

/*
 * 修订记录:
 * xiangzy 上午11:09:54 创建
 */
package com.xyz.domo.filter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class FormAuthenticationFilterExt extends AuthenticatingFilter{

    public static final String DEFAULT_ERROR_KEY_ATTRIBUTE_NAME = "shiroLoginFailure";

    public static final String DEFAULT_USERNAME_PARAM = "username";
    public static final String DEFAULT_PASSWORD_PARAM = "password";
    public static final String DEFAULT_REMEMBER_ME_PARAM = "rememberMe";

    private static final Logger log = LoggerFactory.getLogger(FormAuthenticationFilter.class);

    private String usernameParam = DEFAULT_USERNAME_PARAM;
    private String passwordParam = DEFAULT_PASSWORD_PARAM;
    private String rememberMeParam = DEFAULT_REMEMBER_ME_PARAM;

    private String failureKeyAttribute = DEFAULT_ERROR_KEY_ATTRIBUTE_NAME;

    public FormAuthenticationFilterExt() {
        setLoginUrl(DEFAULT_LOGIN_URL);
    }

    @Override
    public void setLoginUrl(String loginUrl) {
        String previous = getLoginUrl();
        if (previous != null) {
            this.appliedPaths.remove(previous);
        }
        super.setLoginUrl(loginUrl);
        if (log.isTraceEnabled()) {
            log.trace("Adding login url to applied paths.");
        }
        this.appliedPaths.put(getLoginUrl(), null);
    }

    public String getUsernameParam() {
        return usernameParam;
    }

    /**
     * Sets the request parameter name to look for when acquiring the username.  Unless overridden by calling this
     * method, the default is <code>username</code>.
     *
     * @param usernameParam the name of the request param to check for acquiring the username.
     */
    public void setUsernameParam(String usernameParam) {
        this.usernameParam = usernameParam;
    }

    public String getPasswordParam() {
        return passwordParam;
    }

    /**
     * Sets the request parameter name to look for when acquiring the password.  Unless overridden by calling this
     * method, the default is <code>password</code>.
     *
     * @param passwordParam the name of the request param to check for acquiring the password.
     */
    public void setPasswordParam(String passwordParam) {
        this.passwordParam = passwordParam;
    }

    public String getRememberMeParam() {
        return rememberMeParam;
    }

    /**
     * Sets the request parameter name to look for when acquiring the rememberMe boolean value.  Unless overridden
     * by calling this method, the default is <code>rememberMe</code>.
     * <p/>
     * RememberMe will be <code>true</code> if the parameter value equals any of those supported by
     * {@link org.apache.shiro.web.util.WebUtils#isTrue(javax.servlet.ServletRequest, String) WebUtils.isTrue(request,value)}, <code>false</code>
     * otherwise.
     *
     * @param rememberMeParam the name of the request param to check for acquiring the rememberMe boolean value.
     */
    public void setRememberMeParam(String rememberMeParam) {
        this.rememberMeParam = rememberMeParam;
    }

    public String getFailureKeyAttribute() {
        return failureKeyAttribute;
    }

    public void setFailureKeyAttribute(String failureKeyAttribute) {
        this.failureKeyAttribute = failureKeyAttribute;
    }

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
         HttpServletRequest httpServletRequest = (HttpServletRequest)request;
         String ajaxFlag = httpServletRequest.getHeader("x-requested-with");
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                if (log.isTraceEnabled()) {
                    log.trace("Login submission detected.  Attempting to execute login.");
                }
                return executeLogin(request, response);
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("Login page view.");
                }
                //allow them to see the login page ;)
                if(null!=ajaxFlag&&"XMLHttpRequest".equals(ajaxFlag)){
                    log.debug("ajax request must return json update by xzy...");
                    String returnStr = "{"recordsFiltered":0,"data":[],"notLogin":true,"recordsTotal":0}";
                      response.getWriter().write(returnStr);
                      return false;
                  }
                return true;
            }
        } else {
            if (log.isTraceEnabled()) {
                log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                        "Authentication url [" + getLoginUrl() + "]");
            }
           
            if(null!=ajaxFlag&&"XMLHttpRequest".equals(ajaxFlag)){
                log.debug("ajax request must return json update by xzy...");
                String returnStr = "{"recordsFiltered":0,"data":[],"notLogin":true,"recordsTotal":0}";
                    response.getWriter().write(returnStr);
           }else{
               saveRequestAndRedirectToLogin(request, response);
           }
            //saveRequestAndRedirectToLogin(request, response);
           return false;
        }
    }

    /**
     * This default implementation merely returns <code>true</code> if the request is an HTTP <code>POST</code>,
     * <code>false</code> otherwise. Can be overridden by subclasses for custom login submission detection behavior.
     *
     * @param request  the incoming ServletRequest
     * @param response the outgoing ServletResponse.
     * @return <code>true</code> if the request is an HTTP <code>POST</code>, <code>false</code> otherwise.
     */
    protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) {
        return (request instanceof HttpServletRequest) && WebUtils.toHttp(request).getMethod().equalsIgnoreCase(POST_METHOD);
    }

    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        String username = getUsername(request);
        String password = getPassword(request);
        return createToken(username, password, request, response);
    }

    protected boolean isRememberMe(ServletRequest request) {
        return WebUtils.isTrue(request, getRememberMeParam());
    }

    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
                                     ServletRequest request, ServletResponse response) throws Exception {
        issueSuccessRedirect(request, response);
        //we handled the success redirect directly, prevent the chain from continuing:
        return false;
    }

    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,
                                     ServletRequest request, ServletResponse response) {
        setFailureAttribute(request, e);
        //login failed, let request continue back to the login page:
        return true;
    }

    protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {
        String className = ae.getClass().getName();
        request.setAttribute(getFailureKeyAttribute(), className);
    }

    protected String getUsername(ServletRequest request) {
        return WebUtils.getCleanParam(request, getUsernameParam());
    }

    protected String getPassword(ServletRequest request) {
        return WebUtils.getCleanParam(request, getPasswordParam());
    }
}
View Code

4.1、新建类 SSOLogoutFilter

/*
 * CHONGQING XYZ TECH CO.,LTD
 * Copyright (c) 2015 All Rights Reserved.
 */

/*
 * 修订记录:
 * xiangzy 下午6:11:24 创建
 */
package com.xyz.domo.filter;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xyz.domo.common.http.WebUtils;
import com.xyz.domo.common.properties.PropertiesUtil;



public class SSOLogoutFilter implements Filter {

    protected final Logger    logger  = LoggerFactory.getLogger(getClass());
    /**
     * @param filterConfig
     * @throws ServletException
     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    /**
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
        try {
            HttpServletRequest httpservletrequest = (HttpServletRequest) request;
            String logoutRequest = httpservletrequest.getParameter("logoutRequest");
            logger.debug("cas post logout logoutRequest parameter value===>"+logoutRequest);
            String logoutFlag = httpservletrequest.getParameter("logoutFlag");
            if(StringUtils.isNotBlank(logoutRequest)&&StringUtils.isBlank(logoutFlag)){
                String url1 = PropertiesUtil.getInstance().getSysPro("server.ip1");
                String url2 = PropertiesUtil.getInstance().getSysPro("server.ip2");
                String currentIp = getIpAddress();
                logger.debug("cas post logout client, currentIp:【{}】, url1:【{}】, url2:【{}】", new Object[]{currentIp, url1, url2});
                if(url1.contains(currentIp)){
                    WebUtils.doPost(url2, getAllParam(httpservletrequest), 30000, 30000);
                }else if(url2.contains(currentIp)){
                    WebUtils.doPost(url1, getAllParam(httpservletrequest), 30000, 30000);
                }
                logger.debug("cas post logout client");
            }
        } catch (Exception e) {
            logger.error("shiro logout error",e);
        }
        chain.doFilter(request, response);
    }

    /**
     * 
     * @see javax.servlet.Filter#destroy()
     */
    @Override
    public void destroy() {
    }
    
    //获取当前IP
    private  String getIpAddress() throws UnknownHostException {   
        InetAddress address = InetAddress.getLocalHost();   
        return address.getHostAddress();   
    }  

    //获取request对象的所有请求参数
    private Map<String, String> getAllParam(HttpServletRequest request){
        Map<String, String> map = new HashMap<String, String>();
        Enumeration<?> paramNames = request.getParameterNames();
        StringBuffer strBuffer = new StringBuffer();
        while (paramNames.hasMoreElements()) {
            String paramName = (String) paramNames.nextElement();

            String[] paramValues = request.getParameterValues(paramName);
            if (paramValues.length == 1) {
                String paramValue = paramValues[0];
                if (paramValue.length() != 0) {
                    strBuffer.append(paramName).append("=").append(paramValue).append(",");
                    map.put(paramName, paramValue);
                }
            }
        }
        map.put("logoutFlag", "yes");
        return map;
    }

}
View Code

5、在 srcmainjava 下新建包 com.xyz.domo.controller.realm,新建类 MyCasRealm 用于接收服务端返回的登录信息

/*
 * CHONGQING XYZ TECH CO.,LTD
 * Copyright (c) 2015 All Rights Reserved.
 */

/*
 * 修订记录:
 * Tzw 上午10:02:21 创建
 */
package com.xyz.domo.controller.realm;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.bcel.generic.NEW;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cas.CasAuthenticationException;
import org.apache.shiro.cas.CasRealm;
import org.apache.shiro.cas.CasToken;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.util.StringUtils;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.TicketValidationException;
import org.jasig.cas.client.validation.TicketValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.support.AbstractCacheManager;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xyz.dal.mybatis.model.User;


/**
 * @Filename MyCasRealm.java
 *
 * @Description:
 *
 * @Version 1.0
 *
 * @Author 
 *
 * @Email
 *
 */
public class MyCasRealm extends CasRealm {
    
    private static Logger log = LoggerFactory.getLogger(MyCasRealm.class);
    
    @Autowired
    AbstractCacheManager ehCacheCacheManager;
    
    @SuppressWarnings("unchecked")
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        Subject subject = SecurityUtils.getSubject();
        
        Set<String> permissions = new HashSet<String>();
        List<String> permissionInfo = (List<String>)subject.getSession().getAttribute("permissionInfo");
        if(permissionInfo != null){
            permissions.addAll(permissionInfo);
        }
        
        Set<String> roles = new HashSet<String>();
        List<String> roleInfo = (List<String>)subject.getSession().getAttribute("roleInfo");
        if(roleInfo != null){
            roles.addAll(roleInfo);
        }
        
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setStringPermissions(permissions);
        authorizationInfo.setRoles(roles);
        
        return authorizationInfo;
    }
    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        CasToken casToken = (CasToken) token;
        if (token == null) {
            return null;
        }
        
        String ticket = (String) casToken.getCredentials();
        if (!StringUtils.hasText(ticket)) {
            return null;
        }
        
        TicketValidator ticketValidator = ensureTicketValidator();
        
        try {
            // contact CAS server to validate service ticket
            Assertion casAssertion = ticketValidator.validate(ticket, getCasService());
            // get principal, user id and attributes
            AttributePrincipal casPrincipal = casAssertion.getPrincipal();
            String userId = casPrincipal.getName();
            log.debug("Validate ticket : {} in CAS server : {} to retrieve user : {}", new Object[] { ticket, getCasServerUrlPrefix(), userId });
            
            Map<String, Object> attributes = casPrincipal.getAttributes();
            Subject subject = SecurityUtils.getSubject();
            
            // 获取用户信息
            String userStr = (String)attributes.get("userInfo");
            List<User> staffInfo = JSON.parseArray(userStr, User.class);
            subject.getSession().setAttribute("staffInfo", staffInfo);
            // 获取权限信息
        /*String permissionInfo = (String)attributes.get("permissionInfo");
            List<String> permissionInfos = JSON.parseArray(permissionInfo, String.class);
            subject.getSession().setAttribute("permissionInfo", permissionInfos);
            // 获取角色信息
            String roleInfo = (String)attributes.get("roleInfo");
            List<String> roleInfos = JSON.parseArray(roleInfo, String.class);
            subject.getSession().setAttribute("roleInfo", roleInfos);*/
            // refresh authentication token (user id + remember me)
            casToken.setUserId(userId);
            String rememberMeAttributeName = getRememberMeAttributeName();
            String rememberMeStringValue = (String) attributes.get(rememberMeAttributeName);
            boolean isRemembered = rememberMeStringValue != null&& Boolean.parseBoolean(rememberMeStringValue);
            if (isRemembered) {
                casToken.setRememberMe(true);
            }
            // create simple authentication info
            List<Object> principals = CollectionUtils.asList(userId, attributes);
            PrincipalCollection principalCollection = new SimplePrincipalCollection(principals,getName());
            return new SimpleAuthenticationInfo(principalCollection, ticket);
        } catch (TicketValidationException e) {
            throw new CasAuthenticationException("Unable to validate ticket [" + ticket + "]", e);
        }
    }
    
}
View Code

 源码下载点击下载

原文地址:https://www.cnblogs.com/miskis/p/6393780.html