前言:
该篇教程描述如何搭建CAS5.3.x集群操作,由于官方文档并没有贴出集群搭建方案,所以本博主根据源码剖析解决该问题。
文档并没有深入浅出说明原理,直接贴代码用于快速上手学习。
gainward555指出官网有提供配置,本人确实没注意到该配置内容,以为只是其他与redis有关并没有详细看配置内容,仔细看后发现确实是集群配置(吐槽下:官网的文档说明写的跟猫屎一样),地址如下:
官方配置 https://apereo.github.io/cas/5.3.x/installation/Configuration-Properties-Common.html#redis-configuration
如果官网配置达不到想要的效果,可以重新回看本文章内容。
学习CAS5.x 推荐看 以下两个博主文章
此博主文章:https://blog.csdn.net/u010475041/article/category/7156505
此博主文章:https://blog.csdn.net/yelllowcong
全部基于springboot开发,请存在此基础再学习!
该教程由本博主(Garc)首发,所以转载请说明原处,谢谢!!
该教程分为三个部分
1.ticket redis 共享
2.session 共享
3.CAS action 源码覆盖
PS:请按顺序一步一步来
如果发现CAS不能打印info日志,增加一个AsyncLogger指定自己的工程package就好了
以下所有的 configuration 都需要配置spring ,使用spring aop 配置
配置文件目录:src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.hpay.sso.support.auth.config.RedisTicketRegistryConfiguration,\
com.hpay.sso.support.auth.config.CasSupportActionsConfiguration,\
com.hpay.sso.support.auth.config.JdbcPasswordManagementConfiguration,\
com.hpay.sso.config.RedisCacheConfig,\
com.hpay.sso.config.SpringSessionRedisConfig
maven 依赖
说明:该2.9版本必须对应服务器3.0版本或以上,否则后面的session共享会导致版本出错!
redis.clients
jedis
2.9.0
org.apache.commons
commons-pool2
org.springframework.data
spring-data-redis
1.8.11.RELEASE
代码configuration
将 RedisProperties 交给springboot容器,用于在properties 配置
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hpay.sso.support.auth.cache.RiskCacheService;
import com.hpay.sso.support.auth.cache.impl.RiskCacheServiceImpl;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
/**
* Created by Garc on 2018/4/20.
*/
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
@Bean
public RedisProperties redisProperties(){
return new RedisProperties();
}
@Bean
public JedisConnectionFactory jedisConnectionFactory(){
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setDatabase(redisProperties().getDatabase());
jedisConnectionFactory.setPassword(redisProperties().getPassword());
jedisConnectionFactory.setHostName(redisProperties().getHost());
jedisConnectionFactory.setPort(redisProperties().getPort());
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(redisProperties().getPool().getMaxActive());
jedisPoolConfig.setMaxWaitMillis(redisProperties().getPool().getMaxWait());
jedisPoolConfig.setMaxIdle(redisProperties().getPool().getMaxIdle());
jedisPoolConfig.setMinIdle(redisProperties().getPool().getMinIdle());
jedisConnectionFactory.setPoolConfig(jedisPoolConfig);
jedisConnectionFactory.setTimeout(redisProperties().getTimeout());
return jedisConnectionFactory;
}
@Bean
public RedisTemplate redisTemplate() {
StringRedisTemplate redisTemplate = new StringRedisTemplate(jedisConnectionFactory());
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public RiskCacheService riskCacheService(RedisTemplate redisTemplate){
RiskCacheServiceImpl riskCacheService = new RiskCacheServiceImpl();
riskCacheService.setRedisTemplate(redisTemplate);
return riskCacheService;
}
}
properties 配置
#==============Redis==================
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=${redis.database}
# Redis服务器地址
spring.redis.host=${redis.host}
# Redis服务器连接端口
spring.redis.port=${redis.port}
# Redis服务器连接密码(默认为空)
spring.redis.password=${redis.password}
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=${redis.maxActive}
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=${redis.maxWait}
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=${redis.maxIdle}
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=${redis.minIdle}
# 连接超时时间(毫秒)
spring.redis.timeout=${redis.timeout}
import java.util.List;
import java.util.Map;
/**
* Created by Garc on 2018/5/4.
* This class encoding of the default is UTF-8
*/
public interface RiskCacheService {
/**
* 是否存在key
* key 格式示例:
* risk:api:xxx,目录:二级目录:key
* @param key
* @return
*/
boolean hasKey(String key);
/**
* 存参数
* time 单位:秒
* @param key
* @param value
* @param time
*/
void set(String key, Object value, long time);
/**
* 存参数
* @param key
* @param value
*/
void set(String key, Object value);
/**
* 获取值
* @param key
* @return
*/
Object get(String key);
/**
* 参数增值+1
* @param key
* @param val 每次增值数
*/
void increment(String key, long val);
/**
* 同时存入多个key,val数据
* @param keys
*/
void multiSet(Map keys);
/**
* 同时获取多个key的数据
* @param keys
*/
List
import com.hpay.sso.support.auth.cache.RiskCacheService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Created by Garc on 2018/5/4.
* redisTemplate.opsForValue();//Operation String
* redisTemplate.opsForHash();//Operation hash
* redisTemplate.opsForList();//Operation list
* redisTemplate.opsForSet();//Operation set
* redisTemplate.opsForZSet();//Operation orderly set
*/
public class RiskCacheServiceImpl implements RiskCacheService {
private static final Logger log = LoggerFactory.getLogger(RiskCacheServiceImpl.class);
private RedisTemplate redisTemplate;
@Value("${redis.appcode}")
private String appcode;
public void setRedisTemplate(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
private String convert(String key){
return appcode + ":api:" + key;
}
private Object convertObject(Object obj){
if (obj instanceof Map){
Map keys = new HashMap<>();
Map params = (HashMap)obj;
params.forEach((key,val) ->{
keys.put(convert(key),val);
});
return keys;
}
if (obj instanceof List){
List keys = new ArrayList<>();
List params = (ArrayList)obj;
params.forEach(key ->{
keys.add(convert(key));
});
return keys;
}
return obj;
}
@Override
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(convert(key));
}catch (Exception e){
log.error("hasKey缓存异常,key:{}",key,e);
}
return false;
}
@Override
public void set(String key, Object value, long time) {
try {
redisTemplate.opsForValue().set(convert(key), value,time, TimeUnit.SECONDS);
}catch (Exception e){
log.error("set缓存异常,key:{}",key,e);
}
}
@Override
public void set(String key, Object value) {
try {
redisTemplate.opsForValue().set(convert(key), value);
}catch (Exception e){
log.error("set缓存异常,key:{}",key,e);
}
}
@Override
public Object get(String key) {
try {
return redisTemplate.opsForValue().get(convert(key));
}catch (Exception e){
log.error("get缓存异常,key:{}",key,e);
}
return null;
}
@Override
public void increment(String key, long val) {
try {
redisTemplate.boundValueOps(convert(key)).increment(val);
}catch (Exception e){
log.error("increment缓存异常,key:{}",key,e);
}
}
@Override
public void multiSet(Map keys) {
try {
redisTemplate.opsForValue().multiSet((Map)convertObject(keys));
}catch (Exception e){
log.error("multiSet缓存异常,key:{}",keys,e);
}
}
@Override
public List
maven 依赖
org.apereo.cas
cas-server-core-authentication-api
${cas.version}
org.apereo.cas
cas-server-core-api-configuration-model
${cas.version}
org.apereo.cas
cas-server-core-webflow
${cas.version}
org.apereo.cas
cas-server-core-webflow-api
${cas.version}
org.apereo.cas
cas-server-core-util-api
${cas.version}
org.apereo.cas
cas-server-core-api-util
${cas.version}
org.apereo.cas
cas-server-core-web-api
${cas.version}
org.apereo.cas
cas-server-core-tickets-api
${cas.version}
org.apereo.cas
cas-server-support-actions
${cas.version}
org.apereo.cas
cas-server-core
${cas.version}
javax.servlet
servlet-api
2.5
provided
redis ticket 注册代码,该代码用于改变CAS本身获取ticket的一种方式
import com.hpay.sso.support.auth.cache.RiskCacheService;
import org.apache.commons.lang.StringUtils;
import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.ticket.Ticket;
import org.apereo.cas.ticket.registry.AbstractTicketRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
/**
* Created by Garc on 2018/8/20.
* 注册redis 共享TGT
*/
public class RedisTicketRegistry extends AbstractTicketRegistry {
private final static String TICKET_KEY="maat:ticket";
private static final Logger log = LoggerFactory.getLogger(RedisTicketRegistry.class);
@Resource
private RiskCacheService riskCacheService;
@Resource
private CentralAuthenticationService centralAuthenticationService;
@Override
public void addTicket(Ticket ticket) {
if (ticket == null) {
throw new NullPointerException("ticket is marked @NonNull but is null");
} else {
riskCacheService.mapPut(TICKET_KEY,ticket.getId(),ticket);
log.info("add redis TGT:{}",getVal(ticket.getId()));
}
}
@Override
public Ticket getTicket(String ticketId) {
Ticket ticket = (Ticket) riskCacheService.getMapVal(TICKET_KEY,ticketId);
if (ticket==null){
log.info("get redis TGT is null:",getVal(ticketId));
}
log.info("get redis TGT:{}",getVal(ticketId));
return ticket;
}
@Override
public long deleteAll() {
riskCacheService.delete(TICKET_KEY);
return 1;
}
@Override
public Collection getTickets() {
return (List)riskCacheService.getValues(TICKET_KEY);
}
@Override
public Ticket updateTicket(Ticket ticket) {
if (ticket == null) {
throw new NullPointerException("ticket is marked @NonNull but is null");
} else {
this.addTicket(ticket);
}
return ticket;
}
/**
* TGT到期时才调用该方法
* @param ticketId
* @return
*/
@Override
public boolean deleteSingleTicket(String ticketId) {
riskCacheService.delMapkey(TICKET_KEY,ticketId);
if(riskCacheService.hasMapKey(TICKET_KEY,ticketId)){
return false;
}
//TGT到期主动销毁各个系统session
centralAuthenticationService.destroyTicketGrantingTicket(ticketId);
return true;
}
/**
* 星星显示
* @param val
* @return
*/
private String getVal(String val){
if(StringUtils.isBlank(val)){
return val;
}else{
return val.substring(0, 5)+ val.substring(5, val.length()-3).replaceAll("[0-9a-zA-Z]", "*") + val.substring(val.length()-10, val.length());
}
}
}
代码configuration ,用于覆盖原有CAS配置
action 配置也一并配在其中,action代码在下面
import com.hpay.sso.support.auth.RedisTicketRegistry;
import com.hpay.sso.support.auth.action.CreateTicketGrantingTicketAction;
import com.hpay.sso.support.auth.action.TicketGrantingTicketCheckAction;
import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.CipherExecutor;
import org.apereo.cas.DefaultCentralAuthenticationService;
import org.apereo.cas.audit.AuditableExecution;
import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan;
import org.apereo.cas.authentication.ContextualAuthenticationPolicyFactory;
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.logout.LogoutManager;
import org.apereo.cas.services.ServiceContext;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.ticket.factory.DefaultTicketFactory;
import org.apereo.cas.ticket.registry.DefaultTicketRegistrySupport;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.webflow.execution.Action;
import javax.annotation.Resource;
/**
* Created by Garc on 2018/7/24.
* redis集群配置
* ticket 集群配置,将CasCoreConfiguration.class源码颠覆使用自定义配置
* 该配置通过剖析源码得出,官方未给出文档,请不要随意更改。
*
*/
@Configuration("redisTicketRegistryConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
@EnableTransactionManagement(
proxyTargetClass = true
)
public class RedisTicketRegistryConfiguration {
@Resource
private ServicesManager servicesManager;
@Resource
private LogoutManager logoutManager;
@Resource
private DefaultTicketFactory defaultTicketFactory;
@Resource
private ContextualAuthenticationPolicyFactory authenticationPolicyFactory;
@Resource
private ApplicationEventPublisher applicationEventPublisher;
@Resource
private AuditableExecution registeredServiceAccessStrategyEnforcer;
@Autowired
@Qualifier("protocolTicketCipherExecutor")
private ObjectProvider cipherExecutor;
@Bean
public RedisTicketRegistry redisTicketRegistry(){
return new RedisTicketRegistry();
}
@Bean
public DefaultTicketRegistrySupport defaultTicketRegistrySupport(){
return new DefaultTicketRegistrySupport(redisTicketRegistry());
}
@Bean
@Autowired
@ConditionalOnMissingBean(
name = {"centralAuthenticationService"}
)
public CentralAuthenticationService centralAuthenticationService(@Qualifier("authenticationServiceSelectionPlan") AuthenticationServiceSelectionPlan authenticationServiceSelectionPlan) {
PrincipalFactory principalFactory = new DefaultPrincipalFactory();
return new DefaultCentralAuthenticationService(applicationEventPublisher, redisTicketRegistry(), servicesManager, logoutManager, defaultTicketFactory, authenticationServiceSelectionPlan, authenticationPolicyFactory, principalFactory, this.cipherExecutor.getIfAvailable(), this.registeredServiceAccessStrategyEnforcer);
}
}
以上是ticket redis 共享教程,下面是session共享
maven 依赖
org.springframework.session
spring-session-data-redis
1.3.2.RELEASE
perproties 配置
spring.session.store-type=redis
代码 configuration
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.data.redis.config.annotation.web.http
.RedisHttpSessionConfiguration;
import org.springframework.session.web.http.DefaultCookieSerializer;
/**
* Created by Garc on 2018/8/22.
* 配置spring-session redis共享
*/
@Configuration
@EnableRedisHttpSession
public class SpringSessionRedisConfig {
@Bean
public DefaultCookieSerializer defaultCookieSerializer(){
DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
defaultCookieSerializer.setCookiePath("/Maat");
defaultCookieSerializer.setCookieName("JSESSIONID");
return defaultCookieSerializer;
}
@Bean
public RedisHttpSessionConfiguration redisHttpSessionConfiguration(){
RedisHttpSessionConfiguration configuration = new RedisHttpSessionConfiguration();
configuration.setCookieSerializer(defaultCookieSerializer());
configuration.setMaxInactiveIntervalInSeconds(60*15);
return configuration;
}
}
说明:该 action源码是从CAS上直接修改的,请不要随意乱修改源码。
由于已经在刚才上面的redis ticket 配置中配置了Bean,接下来就直接上代码即可
创建 CreateTicket 动作 CreateTicketGrantingTicketAction.class
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.authentication.Authentication;
import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult;
import org.apereo.cas.authentication.AuthenticationResult;
import org.apereo.cas.authentication.AuthenticationResultBuilder;
import org.apereo.cas.authentication.AuthenticationSystemSupport;
import org.apereo.cas.authentication.MessageDescriptor;
import org.apereo.cas.authentication.principal.Service;
import org.apereo.cas.ticket.TicketGrantingTicket;
import org.apereo.cas.ticket.registry.TicketRegistrySupport;
import org.apereo.cas.web.support.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.action.EventFactorySupport;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
public class CreateTicketGrantingTicketAction extends AbstractAction {
private static final Logger LOGGER = LoggerFactory.getLogger(CreateTicketGrantingTicketAction.class);
@Resource
private CentralAuthenticationService centralAuthenticationService;
@Resource
private AuthenticationSystemSupport authenticationSystemSupport;
@Resource
private TicketRegistrySupport ticketRegistrySupport;
@Resource
private HttpServletRequest request;
public Event doExecute(RequestContext context) {
Service service = WebUtils.getService(context);
AuthenticationResultBuilder authenticationResultBuilder = WebUtils.getAuthenticationResultBuilder(context);
LOGGER.debug("Finalizing authentication transactions and issuing ticket-granting ticket");
AuthenticationResult authenticationResult = this.authenticationSystemSupport.finalizeAllAuthenticationTransactions(authenticationResultBuilder, service);
Authentication authentication = this.buildFinalAuthentication(authenticationResult);
String ticketGrantingTicket = WebUtils.getTicketGrantingTicketId(context);
TicketGrantingTicket tgt = this.createOrUpdateTicketGrantingTicket(authenticationResult, authentication, ticketGrantingTicket);
WebUtils.putTicketGrantingTicketInScopes(context, tgt);
//因为session已经共享,再将TGT存储进session中共享至其他节点,ticketCheck从session中获取
String ticketValue = tgt != null ? tgt.getId() : null;
request.getSession().setAttribute("ticketGrantingTicketId",ticketValue);
WebUtils.putAuthenticationResult(authenticationResult, context);
WebUtils.putAuthentication(tgt.getAuthentication(), context);
Collection warnings = calculateAuthenticationWarningMessages(tgt, context.getMessageContext());
if (!warnings.isEmpty()) {
LocalAttributeMap attributes = new LocalAttributeMap("authenticationWarnings", warnings);
return (new EventFactorySupport()).event(this, "successWithWarnings", attributes);
} else {
return this.success();
}
}
protected Authentication buildFinalAuthentication(AuthenticationResult authenticationResult) {
return authenticationResult.getAuthentication();
}
protected TicketGrantingTicket createOrUpdateTicketGrantingTicket(AuthenticationResult authenticationResult, Authentication authentication, String ticketGrantingTicket) {
TicketGrantingTicket tgt;
if (this.shouldIssueTicketGrantingTicket(authentication, ticketGrantingTicket)) {
tgt = this.centralAuthenticationService.createTicketGrantingTicket(authenticationResult);
} else {
tgt = (TicketGrantingTicket)this.centralAuthenticationService.getTicket(ticketGrantingTicket, TicketGrantingTicket.class);
tgt.getAuthentication().update(authentication);
this.centralAuthenticationService.updateTicket(tgt);
}
return tgt;
}
private boolean shouldIssueTicketGrantingTicket(Authentication authentication, String ticketGrantingTicket) {
boolean issueTicketGrantingTicket = true;
if (StringUtils.isNotBlank(ticketGrantingTicket)) {
LOGGER.debug("Located ticket-granting ticket in the context. Retrieving associated authentication");
Authentication authenticationFromTgt = this.ticketRegistrySupport.getAuthenticationFrom(ticketGrantingTicket);
if (authenticationFromTgt == null) {
LOGGER.debug("Authentication session associated with [{}] is no longer valid", ticketGrantingTicket);
this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicket);
} else if (this.areAuthenticationsEssentiallyEqual(authentication, authenticationFromTgt)) {
LOGGER.debug("Resulting authentication matches the authentication from context");
issueTicketGrantingTicket = false;
} else {
LOGGER.debug("Resulting authentication is different from the context");
}
}
return issueTicketGrantingTicket;
}
private boolean areAuthenticationsEssentiallyEqual(Authentication auth1, Authentication auth2) {
if ((auth1 != null || auth2 == null) && (auth1 == null || auth2 != null)) {
EqualsBuilder builder = new EqualsBuilder();
builder.append(auth1.getPrincipal(), auth2.getPrincipal());
builder.append(auth1.getCredentials(), auth2.getCredentials());
builder.append(auth1.getSuccesses(), auth2.getSuccesses());
builder.append(auth1.getAttributes(), auth2.getAttributes());
return builder.isEquals();
} else {
return false;
}
}
private static Collection calculateAuthenticationWarningMessages(TicketGrantingTicket tgtId, MessageContext messageContext) {
Set> entries = tgtId.getAuthentication().getSuccesses().entrySet();
return (Collection)entries.stream().map((entry) -> {
return ((AuthenticationHandlerExecutionResult)entry.getValue()).getWarnings();
}).flatMap(Collection::stream).map((message) -> {
addMessageDescriptorToMessageContext(messageContext, message);
return message;
}).collect(Collectors.toSet());
}
protected static void addMessageDescriptorToMessageContext(MessageContext context, MessageDescriptor warning) {
MessageBuilder builder = (new MessageBuilder()).warning().code(warning.getCode()).defaultText(warning.getDefaultMessage()).args((Object[])warning.getParams());
context.addMessage(builder.build());
}
}
创建 Ticket check action :TicketGrantingTicketCheckAction.class
5.3版本不知道是BUG还是必须要使用https才能将TGT存储在cookie。
该类自定义处理后,通过session共享已经处理好了该过程。
不适用https的话,TGT不会存储在cookie
import org.apache.commons.lang.StringUtils;
import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.ticket.AbstractTicketException;
import org.apereo.cas.ticket.Ticket;
import org.apereo.cas.web.support.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* Created by Garc on 2018/7/24.
* 因为配置了session集群和ticket集群
* 所以更改CAS源码并判断共享session信息
*
*/
public class TicketGrantingTicketCheckAction extends AbstractAction {
private static final Logger LOGGER = LoggerFactory.getLogger(TicketGrantingTicketCheckAction.class);
@Resource
private CentralAuthenticationService centralAuthenticationService;
@Resource
private HttpServletRequest request;
public Event doExecute(RequestContext requestContext) {
//请求判断requestScope和session是否过存在TGT
String scopeTgtId = WebUtils.getTicketGrantingTicketId(requestContext);
String sessionTgtId = (String) request.getSession().getAttribute("ticketGrantingTicketId");
if (StringUtils.isBlank(scopeTgtId)&&StringUtils.isBlank(sessionTgtId)) {
LOGGER.info("TGT not Exists");
return new Event(this, "notExists");
}
if(StringUtils.isBlank(sessionTgtId)){
LOGGER.info("session TGT not Exists");
request.getSession().setAttribute("ticketGrantingTicketId",scopeTgtId);
}
if (StringUtils.isBlank(scopeTgtId)){
LOGGER.info("scope TGT not Exists");
WebUtils.putTicketGrantingTicketInScopes(requestContext,sessionTgtId);
}
String tgtId = StringUtils.isNotBlank(scopeTgtId)?scopeTgtId:StringUtils.isNotBlank(sessionTgtId)?sessionTgtId:"";
try {
Ticket ticket = this.centralAuthenticationService.getTicket(tgtId, Ticket.class);
if (ticket != null && !ticket.isExpired()) {
return new Event(this, "valid");
}
} catch (AbstractTicketException var4) {
LOGGER.trace("Could not retrieve ticket id [{}] from registry.", var4.getMessage());
}
return new Event(this, "invalid");
}
}
action的Bean配置在第二篇
请继续看下篇!
第一篇教程内容到此已经结束了,只要根据此教程一步一步操作,就可以实现集群登录操作。
如果想了解更多,可以加QQ群 119170668