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 下src\main\resources 下新建包 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、在src\main\resources 下新建包 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、在 src\main\java 下新建包 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、在 src\main\java 下新建包 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

 源碼下載點擊下載


免責聲明!

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



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