CAS 5.3.x 单点登录实现集群搭建,快速入手(二)!!!!

前言:

该篇教程描述如何搭建CAS5.3.x集群操作,由于官方文档并没有贴出集群搭建方案,所以本博主根据源码剖析解决该问题。

文档并没有深入浅出说明原理,直接贴代码用于快速上手学习。

学习CAS5.x 推荐看 以下两个博主文章

此博主文章:https://blog.csdn.net/u010475041/article/category/7156505

此博主文章:https://blog.csdn.net/yelllowcong

全部基于springboot开发,请存在此基础再学习!

该教程由本博主(Garc)首发,所以转载请说明原处,谢谢!!

该教程分为两个部分,衔接第一篇

       1.TerminateSessionAction 自定义,解决集群部署随机出现不能退出登录问题

       2.解决集群部署密码找回 token在其他节点不生效问题

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

本博主在使用集群登录退出操作发现有时成功有时失败,后来跟源码定位问题发现是TerminateSessionAction获取TGT时

requestScope 当中不存在TGT_id,所以通过使用session解决问题,但是在解决过程中发现并不容易,所以将处理方式写出来。

请直接看代码

一、TerminateSessionAction 自定义,解决集群部署随机出现不能退出登录问题

maven 依赖版本:

                <dependency>
                    <groupId>org.apereo.cas</groupId>
                    <artifactId>cas-server-core</artifactId>
                    <version>${cas.version}</version>
                </dependency>

                <dependency>
                    <groupId>org.pac4j</groupId>
                    <artifactId>pac4j-core</artifactId>
                    <version>3.0.1</version>
                </dependency>

springboot 配置:

该配置是为了将源码的springboot 管理Bean的 config配置排除掉准备使用自定义的,

如果省去该操作,无法将自定义对象注入进spring

spring.autoconfigure.exclude=org.apereo.cas.web.config.CasSupportActionsConfiguration

自定义TerminateSessionAction:

import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.configuration.model.core.logout.LogoutProperties;
import org.apereo.cas.logout.LogoutRequest;
import org.apereo.cas.util.Pac4jUtils;
import org.apereo.cas.web.support.CookieRetrievingCookieGenerator;
import org.apereo.cas.web.support.WebUtils;
import org.pac4j.core.profile.ProfileManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.action.EventFactorySupport;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;

public class TerminateSessionAction extends AbstractAction {

    private static final Logger LOGGER = LoggerFactory.getLogger(TerminateSessionAction.class);
    public static final String REQUEST_PARAM_LOGOUT_REQUEST_CONFIRMED = "LogoutRequestConfirmed";
    private final EventFactorySupport eventFactorySupport = new EventFactorySupport();

    @Resource
    private CentralAuthenticationService centralAuthenticationService;

    @Resource
    private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator;

    @Resource
    private CookieRetrievingCookieGenerator warnCookieGenerator;

    private LogoutProperties logoutProperties;

    public TerminateSessionAction(LogoutProperties logout) {
        this.logoutProperties = logout;
    }

    public Event doExecute(RequestContext requestContext) {
        boolean terminateSession = true;
        if (this.logoutProperties.isConfirmLogout()) {
            terminateSession = isLogoutRequestConfirmed(requestContext);
        }

        return terminateSession ? this.terminate(requestContext) : this.eventFactorySupport.event(this, "warn");
    }

    public Event terminate(RequestContext context) {
        try {
            HttpServletRequest request = WebUtils.getHttpServletRequestFromExternalWebflowContext(context);
            HttpServletResponse response = WebUtils.getHttpServletResponseFromExternalWebflowContext(context);
            String tgtId = WebUtils.getTicketGrantingTicketId(context);

            if (StringUtils.isBlank(tgtId)) {
                tgtId = this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request);
            }

            //解决集群登出问题
            if (StringUtils.isBlank(tgtId)){
                tgtId = (String) request.getSession().getAttribute("ticketGrantingTicketId");
            }

            if (StringUtils.isNotBlank(tgtId)) {
                LOGGER.debug("Destroying SSO session linked to ticket-granting ticket [{}]", tgtId);
                List<LogoutRequest> logoutRequests = this.centralAuthenticationService.destroyTicketGrantingTicket(tgtId);
                WebUtils.putLogoutRequests(context, logoutRequests);
            }

            LOGGER.debug("Removing CAS cookies");
            this.ticketGrantingTicketCookieGenerator.removeCookie(response);
            this.warnCookieGenerator.removeCookie(response);
            this.destroyApplicationSession(request, response);
            LOGGER.debug("Terminated all CAS sessions successfully.");
            if (StringUtils.isNotBlank(this.logoutProperties.getRedirectUrl())) {
                WebUtils.putLogoutRedirectUrl(context, this.logoutProperties.getRedirectUrl());
                return this.eventFactorySupport.event(this, "redirect");
            } else {
                return this.eventFactorySupport.success(this);
            }
        } catch (Throwable var6) {
            throw var6;
        }
    }

    protected void destroyApplicationSession(HttpServletRequest request, HttpServletResponse response) {
        LOGGER.debug("Destroying application session");
        ProfileManager manager = Pac4jUtils.getPac4jProfileManager(request, response);
        manager.logout();
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object requestedUrl = session.getAttribute("pac4jRequestedUrl");
            session.invalidate();
            if (requestedUrl != null && !requestedUrl.equals("")) {
                request.getSession(true).setAttribute("pac4jRequestedUrl", requestedUrl);
            }
        }

    }

    private static boolean isLogoutRequestConfirmed(RequestContext requestContext) {
        HttpServletRequest request = WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext);
        return request.getParameterMap().containsKey("LogoutRequestConfirmed");
    }

}

使用自定义springboot config管理Bean

import com.hpay.sso.support.auth.action.CreateTicketGrantingTicketAction;
import com.hpay.sso.support.auth.action.TerminateSessionAction;
import com.hpay.sso.support.auth.action.TicketGrantingTicketCheckAction;
import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan;
import org.apereo.cas.authentication.AuthenticationSystemSupport;
import org.apereo.cas.authentication.PrincipalElectionStrategy;
import org.apereo.cas.authentication.adaptive.AdaptiveAuthenticationPolicy;
import org.apereo.cas.authentication.principal.ServiceFactory;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.logout.LogoutManager;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.ticket.registry.TicketRegistrySupport;
import org.apereo.cas.util.CollectionUtils;
import org.apereo.cas.web.FlowExecutionExceptionResolver;
import org.apereo.cas.web.flow.GatewayServicesManagementCheck;
import org.apereo.cas.web.flow.GenerateServiceTicketAction;
import org.apereo.cas.web.flow.ServiceAuthorizationCheck;
import org.apereo.cas.web.flow.SingleSignOnParticipationStrategy;
import org.apereo.cas.web.flow.actions.InitialAuthenticationAction;
import org.apereo.cas.web.flow.login.GenericSuccessViewAction;
import org.apereo.cas.web.flow.login.InitialAuthenticationRequestValidationAction;
import org.apereo.cas.web.flow.login.InitialFlowSetupAction;
import org.apereo.cas.web.flow.login.InitializeLoginAction;
import org.apereo.cas.web.flow.login.RedirectUnauthorizedServiceUrlAction;
import org.apereo.cas.web.flow.login.SendTicketGrantingTicketAction;
import org.apereo.cas.web.flow.login.ServiceWarningAction;
import org.apereo.cas.web.flow.logout.FrontChannelLogoutAction;
import org.apereo.cas.web.flow.logout.LogoutAction;
import org.apereo.cas.web.flow.logout.LogoutViewSetupAction;
import org.apereo.cas.web.flow.resolver.CasDelegatingWebflowEventResolver;
import org.apereo.cas.web.flow.resolver.CasWebflowEventResolver;
import org.apereo.cas.web.support.ArgumentExtractor;
import org.apereo.cas.web.support.CookieRetrievingCookieGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.util.CookieGenerator;
import org.springframework.webflow.execution.Action;

/**
 * 该配置覆盖源码配置,实现集群操作。
 * 因为有部分Bean无法直接被覆盖,所以才直接覆盖配置再使用自己的Bean
 */
@Configuration("casSupportActionsConfiguration")
@EnableConfigurationProperties({CasConfigurationProperties.class})
@EnableTransactionManagement(
    proxyTargetClass = true
)
public class CasSupportActionsConfiguration {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(CasSupportActionsConfiguration.class);
    @Autowired
    @Qualifier("serviceTicketRequestWebflowEventResolver")
    private CasWebflowEventResolver serviceTicketRequestWebflowEventResolver;
    @Autowired
    @Qualifier("initialAuthenticationAttemptWebflowEventResolver")
    private CasDelegatingWebflowEventResolver initialAuthenticationAttemptWebflowEventResolver;
    @Autowired
    @Qualifier("servicesManager")
    private ServicesManager servicesManager;
    @Autowired
    @Qualifier("ticketGrantingTicketCookieGenerator")
    private ObjectProvider<CookieRetrievingCookieGenerator> ticketGrantingTicketCookieGenerator;
    @Autowired
    @Qualifier("warnCookieGenerator")
    private ObjectProvider<CookieRetrievingCookieGenerator> warnCookieGenerator;
    @Autowired
    private CasConfigurationProperties casProperties;
    @Autowired
    @Qualifier("webApplicationServiceFactory")
    private ServiceFactory webApplicationServiceFactory;
    @Autowired
    @Qualifier("adaptiveAuthenticationPolicy")
    private AdaptiveAuthenticationPolicy adaptiveAuthenticationPolicy;
    @Autowired
    @Qualifier("centralAuthenticationService")
    private CentralAuthenticationService centralAuthenticationService;
    @Autowired
    @Qualifier("defaultAuthenticationSystemSupport")
    private AuthenticationSystemSupport authenticationSystemSupport;
    @Autowired
    @Qualifier("logoutManager")
    private LogoutManager logoutManager;
    @Autowired
    @Qualifier("defaultTicketRegistrySupport")
    private TicketRegistrySupport ticketRegistrySupport;
    @Autowired
    @Qualifier("rankedAuthenticationProviderWebflowEventResolver")
    private CasWebflowEventResolver rankedAuthenticationProviderWebflowEventResolver;
    @Autowired
    @Qualifier("authenticationServiceSelectionPlan")
    private AuthenticationServiceSelectionPlan authenticationRequestServiceSelectionStrategies;
    @Autowired
    @Qualifier("singleSignOnParticipationStrategy")
    private SingleSignOnParticipationStrategy webflowSingleSignOnParticipationStrategy;
    @Autowired
    @Qualifier("principalElectionStrategy")
    private PrincipalElectionStrategy principalElectionStrategy;

    public CasSupportActionsConfiguration() {
    }

    @Bean
    @RefreshScope
    public HandlerExceptionResolver errorHandlerResolver() {
        return new FlowExecutionExceptionResolver();
    }

    @ConditionalOnMissingBean(
        name = {"authenticationViaFormAction"}
    )
    @Bean
    @RefreshScope
    public Action authenticationViaFormAction() {
        return new InitialAuthenticationAction(this.initialAuthenticationAttemptWebflowEventResolver, this.serviceTicketRequestWebflowEventResolver, this.adaptiveAuthenticationPolicy);
    }

    @RefreshScope
    @ConditionalOnMissingBean(
        name = {"serviceAuthorizationCheck"}
    )
    @Bean
    public Action serviceAuthorizationCheck() {
        return new ServiceAuthorizationCheck(this.servicesManager, this.authenticationRequestServiceSelectionStrategies);
    }

    @RefreshScope
    @ConditionalOnMissingBean(
        name = {"sendTicketGrantingTicketAction"}
    )
    @Bean
    public Action sendTicketGrantingTicketAction() {
        return new SendTicketGrantingTicketAction(this.centralAuthenticationService, (CookieRetrievingCookieGenerator)this.ticketGrantingTicketCookieGenerator.getIfAvailable(), this.webflowSingleSignOnParticipationStrategy);
    }

    @RefreshScope
    @Bean
    @ConditionalOnMissingBean(
        name = {"logoutAction"}
    )
    public Action logoutAction() {
        return new LogoutAction(this.webApplicationServiceFactory, this.servicesManager, this.casProperties.getLogout());
    }

    @ConditionalOnMissingBean(
        name = {"initializeLoginAction"}
    )
    @Bean
    @RefreshScope
    public Action initializeLoginAction() {
        return new InitializeLoginAction(this.servicesManager);
    }

    @RefreshScope
    @Bean
    @Autowired
    @ConditionalOnMissingBean(
        name = {"initialFlowSetupAction"}
    )
    public Action initialFlowSetupAction(@Qualifier("argumentExtractor") ArgumentExtractor argumentExtractor) {
        return new InitialFlowSetupAction(CollectionUtils.wrap(argumentExtractor), this.servicesManager, this.authenticationRequestServiceSelectionStrategies, (CookieRetrievingCookieGenerator)this.ticketGrantingTicketCookieGenerator.getIfAvailable(), (CookieRetrievingCookieGenerator)this.warnCookieGenerator.getIfAvailable(), this.casProperties);
    }

    @RefreshScope
    @Bean
    @ConditionalOnMissingBean(
        name = {"initialAuthenticationRequestValidationAction"}
    )
    public Action initialAuthenticationRequestValidationAction() {
        return new InitialAuthenticationRequestValidationAction(this.rankedAuthenticationProviderWebflowEventResolver);
    }

    @RefreshScope
    @Bean
    @ConditionalOnMissingBean(
        name = {"genericSuccessViewAction"}
    )
    public Action genericSuccessViewAction() {
        return new GenericSuccessViewAction(this.centralAuthenticationService, this.servicesManager, this.webApplicationServiceFactory, this.casProperties.getView().getDefaultRedirectUrl());
    }

    @RefreshScope
    @Bean
    @ConditionalOnMissingBean(
        name = {"redirectUnauthorizedServiceUrlAction"}
    )
    public Action redirectUnauthorizedServiceUrlAction() {
        return new RedirectUnauthorizedServiceUrlAction(this.servicesManager);
    }

    @Bean
    @RefreshScope
    @ConditionalOnMissingBean(
        name = {"generateServiceTicketAction"}
    )
    public Action generateServiceTicketAction() {
        return new GenerateServiceTicketAction(this.authenticationSystemSupport, this.centralAuthenticationService, this.ticketRegistrySupport, this.authenticationRequestServiceSelectionStrategies, this.servicesManager, this.principalElectionStrategy);
    }

    @Bean
    @ConditionalOnMissingBean(
        name = {"gatewayServicesManagementCheck"}
    )
    @RefreshScope
    public Action gatewayServicesManagementCheck() {
        return new GatewayServicesManagementCheck(this.servicesManager);
    }

    @Bean
    @ConditionalOnMissingBean(
        name = {"frontChannelLogoutAction"}
    )
    public Action frontChannelLogoutAction() {
        return new FrontChannelLogoutAction(this.logoutManager);
    }


    @RefreshScope
    @Bean
    public Action terminateSessionAction() {
        return new TerminateSessionAction(this.casProperties.getLogout());
    }

    @Bean
    @ConditionalOnMissingBean(
      name = {"ticketGrantingTicketCheckAction"}
    )
    public Action ticketGrantingTicketCheckAction() {
        return new TicketGrantingTicketCheckAction();
    }

    @RefreshScope
    @ConditionalOnMissingBean(
      name = {"createTicketGrantingTicketAction"}
    )
    @Bean
    public Action createTicketGrantingTicketAction() {
        return new CreateTicketGrantingTicketAction();
    }

    @Bean
    public Action logoutViewSetupAction() {
        return new LogoutViewSetupAction(this.casProperties);
    }

    @Bean
    @ConditionalOnMissingBean(
        name = {"serviceWarningAction"}
    )
    @RefreshScope
    public Action serviceWarningAction() {
        return new ServiceWarningAction(this.centralAuthenticationService, this.authenticationSystemSupport, this.ticketRegistrySupport, (CookieGenerator)this.warnCookieGenerator.getIfAvailable(), this.principalElectionStrategy);
    }
}

二、解决集群部署密码找回 token在其他节点不生效问题

maven 依赖:

                <dependency>
                    <groupId>org.apereo.cas</groupId>
                    <artifactId>cas-server-support-pm</artifactId>
                    <version>${cas.version}</version>
                </dependency>

springboot application 配置:

该配置是为了将源码的springboot 管理Bean的 config配置排除掉准备使用自定义的,

如果省去该操作,无法将自定义对象注入进spring

spring.autoconfigure.exclude=org.apereo.cas.config.pm.JdbcPasswordManagementConfiguration

自定义JdbcPasswordManagementService:

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.validator.routines.EmailValidator;
import org.apereo.cas.CipherExecutor;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.UsernamePasswordCredential;
import org.apereo.cas.authentication.support.password.PasswordEncoderUtils;
import org.apereo.cas.configuration.model.support.pm.PasswordManagementProperties;
import org.apereo.cas.pm.PasswordChangeBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.sql.DataSource;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class JdbcPasswordManagementService extends BasePasswordManagementService {

    private static final Logger LOGGER = LoggerFactory.getLogger(JdbcPasswordManagementService.class);
    private final JdbcTemplate jdbcTemplate;

    public JdbcPasswordManagementService(CipherExecutor<Serializable, String> cipherExecutor, String issuer, PasswordManagementProperties passwordManagementProperties, DataSource dataSource) {
        super(passwordManagementProperties, cipherExecutor, issuer);
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public boolean changeInternal(Credential credential, PasswordChangeBean bean) {
        UsernamePasswordCredential c = (UsernamePasswordCredential)credential;
        PasswordEncoder encoder = PasswordEncoderUtils.newPasswordEncoder(this.properties.getJdbc().getPasswordEncoder());
        String password = encoder.encode(bean.getPassword());
        int count = this.jdbcTemplate.update(this.properties.getJdbc().getSqlChangePassword(), new Object[]{password, c.getId()});
        return count > 0;
    }

    public String findEmail(String username) {
        try {
            String email = (String)this.jdbcTemplate.queryForObject(this.properties.getJdbc().getSqlFindEmail(), String.class, new Object[]{username});
            if (StringUtils.isNotBlank(email) && EmailValidator.getInstance().isValid(email)) {
                return email;
            } else {
                LOGGER.debug("Username {} not found when searching for email", username);
                return null;
            }
        } catch (EmptyResultDataAccessException var3) {
            LOGGER.debug("Username {} not found when searching for email", username);
            return null;
        }
    }

    public Map<String, String> getSecurityQuestions(String username) {
        String sqlSecurityQuestions = this.properties.getJdbc().getSqlSecurityQuestions();
        Map<String, String> map = new LinkedHashMap();
        List<Map<String, Object>> results = this.jdbcTemplate.queryForList(sqlSecurityQuestions, new Object[]{username});
        results.forEach((row) -> {
            if (row.containsKey("question") && row.containsKey("answer")) {
                map.put(row.get("question").toString(), row.get("answer").toString());
            }

        });
        LOGGER.debug("Found [{}] security questions for [{}]", map.size(), username);
        return map;
    }
}

自定义BasePasswordManagementService:

当token解析时,其他节点无法正常解密,未找到原因,最后本博主只好替换成AES加密和解密算法。

AESCoder这段代码就不提供了,也可以用其他加密算法代替。

import com.hpay.sso.support.auth.Crypto.AESCoder;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.CipherExecutor;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.configuration.model.support.pm.PasswordManagementProperties;
import org.apereo.cas.pm.InvalidPasswordException;
import org.apereo.cas.pm.PasswordChangeBean;
import org.apereo.cas.pm.PasswordManagementService;
import org.apereo.inspektr.audit.annotation.Audit;
import org.apereo.inspektr.common.web.ClientInfo;
import org.apereo.inspektr.common.web.ClientInfoHolder;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.NumericDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class BasePasswordManagementService implements PasswordManagementService {
    private static final Logger LOGGER = LoggerFactory.getLogger(BasePasswordManagementService.class);
    protected final PasswordManagementProperties properties;
    private final CipherExecutor<Serializable, String> cipherExecutor;
    private final String issuer;

    public String parseToken(String token) {
        try {
            String json = AESCoder.decryptToString(token);
            if (json.equals(token)){
                json = (String)this.cipherExecutor.decode(token);
            }
            JwtClaims claims = JwtClaims.parse(json);
            if (!claims.getIssuer().equals(this.issuer)) {
                LOGGER.error("Token issuer does not match CAS");
                return null;
            } else if (!claims.getAudience().isEmpty() && ((String)claims.getAudience().get(0)).equals(this.issuer)) {
                if (StringUtils.isBlank(claims.getSubject())) {
                    LOGGER.error("Token has no subject identifier");
                    return null;
                } else {
                    ClientInfo holder = ClientInfoHolder.getClientInfo();
                    if (!claims.getStringClaimValue("origin").equals(holder.getServerIpAddress())) {
                        LOGGER.error("Token origin server IP address does not match CAS");
                        return null;
                    } else if (!claims.getStringClaimValue("client").equals(holder.getClientIpAddress())) {
                        LOGGER.error("Token client IP address does not match CAS");
                        return null;
                    } else if (claims.getExpirationTime().isBefore(NumericDate.now())) {
                        LOGGER.error("Token has expired.");
                        return null;
                    } else {
                        return claims.getSubject();
                    }
                }
            } else {
                LOGGER.error("Token audience does not match CAS");
                return null;
            }
        } catch (Exception var5) {
            LOGGER.error(var5.getMessage(), var5);
            return null;
        }
    }

    public String createToken(String to) {
        try {
            String token = UUID.randomUUID().toString();
            JwtClaims claims = new JwtClaims();
            claims.setJwtId(token);
            claims.setIssuer(this.issuer);
            claims.setAudience(this.issuer);
            claims.setExpirationTimeMinutesInTheFuture(this.properties.getReset().getExpirationMinutes());
            claims.setIssuedAtToNow();
            ClientInfo holder = ClientInfoHolder.getClientInfo();
            if (holder != null) {
                claims.setStringClaim("origin", holder.getServerIpAddress());
                claims.setStringClaim("client", holder.getClientIpAddress());
            }

            claims.setSubject(to);
            String json = claims.toJson();
            String cipher = AESCoder.encryptToString(json);
            if (cipher.equals(json)){
                cipher = (String)this.cipherExecutor.encode(json);
            }
            return cipher;
        } catch (Exception var6) {
            LOGGER.error(var6.getMessage(), var6);
            return null;
        }
    }

    @Audit(
        action = "CHANGE_PASSWORD",
        actionResolverName = "CHANGE_PASSWORD_ACTION_RESOLVER",
        resourceResolverName = "CHANGE_PASSWORD_RESOURCE_RESOLVER"
    )
    public boolean change(Credential c, PasswordChangeBean bean) throws InvalidPasswordException {
        return this.changeInternal(c, bean);
    }

    public boolean changeInternal(Credential c, PasswordChangeBean bean) throws InvalidPasswordException {
        return false;
    }

    public static List<String> canonicalizeSecurityQuestions(Map<String, String> questionMap) {
        List<String> keys = new ArrayList(questionMap.keySet());
        keys.sort(String.CASE_INSENSITIVE_ORDER);
        return keys;
    }

    public BasePasswordManagementService(PasswordManagementProperties properties, CipherExecutor<Serializable, String> cipherExecutor, String issuer) {
        this.properties = properties;
        this.cipherExecutor = cipherExecutor;
        this.issuer = issuer;
    }
}

代码configuration ,用于覆盖原有CAS配置:

import com.hpay.sso.support.auth.pm.JdbcPasswordManagementService;
import org.apereo.cas.CipherExecutor;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.support.JpaBeans;
import org.apereo.cas.pm.PasswordManagementService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration("jdbcPasswordManagementConfiguration")
@EnableConfigurationProperties({CasConfigurationProperties.class})
public class JdbcPasswordManagementConfiguration {

    private static final Logger LOGGER = LoggerFactory.getLogger(JdbcPasswordManagementConfiguration.class);
    @Autowired
    private CasConfigurationProperties casProperties;
    @Autowired
    @Qualifier("passwordManagementCipherExecutor")
    private CipherExecutor passwordManagementCipherExecutor;

    public JdbcPasswordManagementConfiguration() {
    }

    @Bean
    public DataSource jdbcPasswordManagementDataSource() {
        return JpaBeans.newDataSource(this.casProperties.getAuthn().getPm().getJdbc());
    }

    @RefreshScope
    @Bean
    public PasswordManagementService passwordChangeService() {
        return new JdbcPasswordManagementService(this.passwordManagementCipherExecutor, this.casProperties.getServer().getPrefix(), this.casProperties.getAuthn().getPm(), this.jdbcPasswordManagementDataSource());
    }
}

请继续看下篇!

第二篇教程内容到此已经结束了,只要根据此教程一步一步操作,就可以实现集群登录操作。

如果想了解更多,可以加QQ群 119170668

猜你喜欢

转载自blog.csdn.net/Qensq/article/details/82501198