一、准备

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>

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);
} }

在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;
}
}

在 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);
} }

在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();
} }

修改 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>

打开如图所示的文件

在 <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>

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>

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>

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>

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());
}
}

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;
} }

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);
}
} }

源码下载:点击下载

cas 服务端、客服端详细配置的更多相关文章

  1. Amazon Connect 配置ccp端的soft phone配置(客服坐席工作站配置)

    本文参考:  Amazon Connect 配置ccp端的soft phone配置(客服坐席工作站配置) [官网]:https://aws.amazon.com/cn/connect/ 应用场景 在应 ...

  2. jetty 客服端 与服务端

    jetty 服务端,客服端有请求buffter 检查 默认4kb 4096 客服端 HttpClient client=new HttpClient(); client.setRequestBuffe ...

  3. python服务器端、客户端的模型,客服端发送请求,服务端进行响应(web.py)

    服务器端.客户端的模型,客服端发送的请求,服务端的响应 相当于启动了一个web server install web.py 接口框架用到的包 http://webpy.org/tutorial3.zh ...

  4. C# 向服务器上传文件(客服端winform、服务端web)

    转载 首先写客服端,winform模拟一个post提交: /// <summary> /// 将本地文件上传到指定的服务器(HttpWebRequest方法) /// </summa ...

  5. python socket 客服端服务端编程

    客服端编程 import socket try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error a ...

  6. java UDP 通信:服务端与客服端

    import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import j ...

  7. Spring Cloud 客服端负载均衡 Ribbon

    一.简介   Spring Cloud Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的.它不像服务注册中心.配置中心.API网关那样独立部署, ...

  8. .net core 和 WPF 开发升讯威在线客服与营销系统:使用 TCP协议 实现稳定的客服端

    本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf.shengxunwei.com 注意 ...

  9. TCP服务器端和客服端(一)

    就是一个客服端(Socket)和服务器(ServerSocket)端的链接间.我的理解是一个服务端可以链接多个客服端. 在客服端有输入流outPutStream. 用于发送数据 在服务器端有输出流.i ...

随机推荐

  1. SpringBoot在IntelliJ IDEA下for MAC 热加载

    说在前面 热加载:文件内容变更服务器自动运行最新代码.实则在IDEA环境进行热部署后,下述过程一气呵成. 1代码变更,文件自动保存(IDEA自动保存代码,用户无需使用COMMAND+SAVE快捷键): ...

  2. CentOS Openvpn搭建以及 linux&&windows客户端的连接

    本文参考:http://www.centoscn.com/CentosServer/test/2014/1120/4153.html 一. Server安装准备     (CentOS release ...

  3. 《安装ubuntu及VMware以及相关问题汇总》

    一.VMware Ubuntu安装详细过程 http://blog.csdn.net/u013142781/article/details/50529030 二.VMware Tools (ubunt ...

  4. windows编程常见数据类型

    windows编程常见数据类型, 总结一下方便查阅: 类型 对应指针 描述 ATOM . typedef WORD ATOM; BOOL LPBOOL 布尔类型,值要写成TRUE或FALSE,实际上是 ...

  5. 后端设置cookie写不到前端页面

    javax.servlet.http.Cookie cookie = new javax.servlet.http.Cookie("id",session.getId()); co ...

  6. 在Centos中,大容量,且读写频繁的目录

    1./根目录 2./usr目录 3./home目录 4./var目录 5./Swap目录     比较特殊,只要物理内存没使用完,就不会被启用 以上为鸟哥的linuxPDF中的学习心得

  7. pyHeatMap生成热力图

    库链接:https://pypi.org/project/pyheatmap/ 现在的linux系统默认都是安装好的py环境,直接用pip进行热力库安装 pip install pyheatmap 或 ...

  8. Hessian矩阵【转】

    http://blog.sina.com.cn/s/blog_7e1ecaf30100wgfw.html 在数学中,海塞矩阵是一个自变量为向量的实值函数的二阶偏导数组成的方块矩阵,一元函数就是二阶导, ...

  9. Python 日志输出中添加上下文信息

    Python日志输出中添加上下文信息 除了传递给日志记录函数的参数(如msg)外,有时候我们还想在日志输出中包含一些额外的上下文信息.比如,在一个网络应用中,可能希望在日志中记录客户端的特定信息,如: ...

  10. Andorid API Package --->android.animation

    包名: android.animation                                Added in API level 11 URL:http://developer.andr ...