若依微服务集成CAS

若依(RuoYi)微服务是一款基于Spring Cloud Alibaba开发的企业级微服务框架,采用前后端分离方式,使用了常用的微服务组件,如Feign、Nacos、Sentinel、Seata等,提供了丰富的微服务治理功能,如服务注册、发现、路由、负载均衡、熔断降级、限流等。借助若依微服务框架可以让我们快速构建起一个高效、可靠、可扩展的分布式系统,提高了开发效率和系统性能。

借助Spring Cloud Alibaba,若依微服务框架完成了后端的微服务改造,但是前端仍是一个单体服务,随着业务的增长,前端必然变的庞大、臃肿,不可避免的需要对前端进行拆分,然而前端拆分后面临的一个问题是登录信息如何同步?登录信息是以token存储在cookie中的,无法共享。

为了解决前端登录信息同步的问题,这里考虑通过集成CAS的方式,实现统一认证。

研究了若依微服务的认证功能后发现,若依微服务的认证并未使用Spring Security,仅仅使用了Spring Security的加密功能,无法直接套用若依分离版集成CAS的方式。所以通过结合分离版集成思路及CAS官方集成方法完成若依微服务集成CAS。集成方法如下:

1、添加CAS依赖

在auth模块中添加cas依赖:

<!-- Cas Core -->
<dependency>
  <groupId>org.jasig.cas.client</groupId>
  <artifactId>cas-client-core</artifactId>
  <version>3.6.4</version>
</dependency>

2、修改配置文件

在nacos中修改ruoyi-auth-dev.yml(或直接修改auth模块下bootstrap.yml文件),增加cas配置:

cas:
  enable: true
  server:
    url:
      prefix: http://127.0.0.1:8888/cas
      login: http://127.0.0.1:8888/cas/login
  client:
    url: http://127.0.0.1:8080/auth

3、修改Constants.java

修改common-core模块下com.ruoyi.common.core.constant.Constants.java,增加CAS认证成功标识:


/**
 * CAS登录成功后的后台标识
 */
public static final String CAS_TOKEN = "cas_token";

/**
 * CAS登录成功后的前台Cookie的Key
 */
public static final String WEB_TOKEN_KEY = "Cloud-Token";

/**
 * CAS登录成功后的前台Cookie的Expires-In
 */
public static final String WEB_TOKEN_EXPIRES = "Cloud-Expires-In";

4、添加CasProperties.java

在auth模块中添加CasProperties.java文件,获取CAS配置信息:


package com.ruoyi.auth.cas.config.properties;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;

/**
 * @author LuoFei
 * @className: CasProperty
 * @projectName RuoYi-Cloud-master
 * @description: cas配置参数
 * @date 2022/7/21 10:11
 */
@Configuration
@RefreshScope
public class CasProperties {

    @Value("${cas.enable}")
    private Boolean enabled;

    @Value("${cas.server.url.prefix}")
    private String casServerUrlPrefix;

    @Value("${cas.server.url.login}")
    private String casServerLoginUrl;

    @Value("${cas.client.url}")
    private String serverName;

    public Boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public String getCasServerUrlPrefix() {
        return casServerUrlPrefix;
    }

    public void setCasServerUrlPrefix(String casServerUrlPrefix) {
        this.casServerUrlPrefix = casServerUrlPrefix;
    }

    public String getCasServerLoginUrl() {
        return casServerLoginUrl;
    }

    public void setCasServerLoginUrl(String casServerLoginUrl) {
        this.casServerLoginUrl = casServerLoginUrl;
    }

    public String getServerName() {
        return serverName;
    }

    public void setServerName(String serverName) {
        this.serverName = serverName;
    }
}

5、添加NoCasFilter.java

在auth模块中添加NoCasFilter.java文件,在未启用CAS时直接放行:


package com.ruoyi.auth.cas.filter;

import javax.servlet.*;
import java.io.IOException;

/**
 * @author LuoFei
 * @className: NoCasFilter
 * @projectName RuoYi-Cloud-master
 * @description: 单点登录停用辅助过滤器
 * @date 2022/7/21 11:19
 */
public final class NoCasFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        filterChain.doFilter(request, response);
    }
}

6、 添加CustomSessionMappingStorage.java

在auth模块中添加CustomSessionMappingStorage.java文件,实现单点登出:


package com.ruoyi.auth.cas.storage;

import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.security.service.TokenService;
import org.apache.catalina.session.StandardSessionFacade;
import org.jasig.cas.client.session.SessionMappingStorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;

/**
 * @author LuoFei
 * @className: CustomSessionMappingStorage
 * @projectName RuoYi-Vue-master
 * @description: 单点登录-前后端分离-单点登出删除token
 * @date 2022/4/28 12:56
 */
@Component
public class CustomSessionMappingStorage implements SessionMappingStorage {
    private final Map<String, HttpSession> MANAGED_SESSIONS = new HashMap();
    private final Map<String, String> ID_TO_SESSION_KEY_MAPPING = new HashMap();
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private TokenService tokenService;

    public CustomSessionMappingStorage() {
    }

    @Override
    public synchronized void addSessionById(String mappingId, HttpSession session) {
        this.ID_TO_SESSION_KEY_MAPPING.put(session.getId(), mappingId);
        this.MANAGED_SESSIONS.put(mappingId, session);
    }

    @Override
    public synchronized void removeBySessionById(String sessionId) {
        this.logger.debug("Attempting to remove Session=[{}]", sessionId);
        String key = (String)this.ID_TO_SESSION_KEY_MAPPING.get(sessionId);
        if (this.logger.isDebugEnabled()) {
            if (key != null) {
                this.logger.debug("Found mapping for session.  Session Removed.");
            } else {
                this.logger.debug("No mapping for session found.  Ignoring.");
            }
        }

        this.MANAGED_SESSIONS.remove(key);
        this.ID_TO_SESSION_KEY_MAPPING.remove(sessionId);
    }

    @Override
    public synchronized HttpSession removeSessionByMappingId(String mappingId) {
        StandardSessionFacade session = (StandardSessionFacade) this.MANAGED_SESSIONS.get(mappingId);
        if (session != null) {
            this.removeBySessionById(session.getId());
            try {
                String token = (String) session.getAttribute(Constants.CAS_TOKEN);
                tokenService.delLoginUser(token);
            } catch (IllegalStateException e) {
                this.logger.error("已成功登出");
            }
        }
        return session;
    }
}

7、增加casLogin方法

在auth模块的TokenController.java文件中添加casLogin方法,实现登录功能:


/**
 * 单点登录成功创建token
 * @param request
 * @param response
 * @throws IOException
 * @throws ServletException
 */
@GetMapping("casLogin")
public void casLogin(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    String username = request.getRemoteUser();
    HttpSession httpSession = request.getSession();

    String url = request.getParameter("redirect");
    LoginUser userInfo = sysLoginService.login(username);
    Map<String, Object> token = tokenService.createToken(userInfo);

    Cookie tokenCookie = new Cookie(Constants.WEB_TOKEN_KEY, (String) token.get("access_token"));
    //必须设置path,否则获取不到cookie
    tokenCookie.setPath("/");
    response.addCookie(tokenCookie);
    Cookie expiresCookie = new Cookie(Constants.WEB_TOKEN_EXPIRES, ((Long) token.get("expires_in")).toString());
    expiresCookie.setPath("/");
    response.addCookie(expiresCookie);
    //设置后端认证成功标识

    httpSession.setAttribute(Constants.CAS_TOKEN, token.get("access_token"));
    //登录成功后跳转到前端访问页面
    response.sendRedirect(url);
}

8、添加CasConfig.java文件

在auth模块中添加CasConfig.java文件:


package com.traffic.auth.cas.config;

import com.traffic.auth.cas.config.properties.CasProperties;
import com.traffic.auth.cas.filter.CustomAuthenticationFilter;
import com.traffic.auth.cas.filter.CustomCas30ProxyReceivingTicketValidationFilter;
import com.traffic.auth.cas.filter.NoCasFilter;
import com.traffic.auth.cas.storage.CustomSessionMappingStorage;
import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * @author LuoFei
 * @className: CasConfig
 * @projectName traffic-Cloud-master
 * @description: cas配置文件
 * @date 2022/7/19 14:17
 */
@Configuration
public class CasConfig {

    @Autowired
    private CasProperties casProperties;

    @Autowired
    private CustomSessionMappingStorage customSessionMappingStorage;

    /**
     * 单点登出过滤器
     * @return
     */
    @Bean
    public FilterRegistrationBean logoutFilter() {
        FilterRegistrationBean authenticationFilter = new FilterRegistrationBean<>();
        SingleSignOutFilter signOutFilter = new SingleSignOutFilter();
        signOutFilter.setSessionMappingStorage(customSessionMappingStorage);
        signOutFilter.setIgnoreInitConfiguration(true);
        authenticationFilter.setFilter(signOutFilter);
        authenticationFilter.addUrlPatterns("/*");
        Map<String, String> initParameters = new HashMap<>();
        initParameters.put("casServerUrlPrefix", casProperties.getCasServerUrlPrefix());
        authenticationFilter.setInitParameters(initParameters);
        authenticationFilter.setOrder(1);
        if (casProperties.getEnabled()) {
            return authenticationFilter;
        }
        return new FilterRegistrationBean<>(new NoCasFilter());
    }

    /**
     * 单点登录认证入口
     * @return
     */
    @Bean
    public FilterRegistrationBean authenticationFilterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        AuthenticationFilter authenticationFilter = new AuthenticationFilter();
        authenticationFilter.setCasServerLoginUrl(casProperties.getCasServerLoginUrl());
        authenticationFilter.setServerName(casProperties.getServerName());
        CustomAuthenticationFilter casFilter = new CustomAuthenticationFilter();
        casFilter.setCasFilter(authenticationFilter);
        filterRegistrationBean.setFilter(casFilter);
//        Map<String, String> initParameters = new HashMap<>();
//        initParameters.put("casServerLoginUrl", casProperties.getCasServerLoginUrl());
//        initParameters.put("serverName", casProperties.getServerName());
//        filterRegistrationBean.setInitParameters(initParameters);
        filterRegistrationBean.setOrder(2);
        List<String> urlPatterns = new ArrayList<>();
        urlPatterns.add("/casLogin");
        filterRegistrationBean.setUrlPatterns(urlPatterns);
        if (casProperties.getEnabled()) {
            return filterRegistrationBean;
        }
        return new FilterRegistrationBean<>(new NoCasFilter());
    }

    /**
     * 单点登录验证入口
     * @return
     */
    @Bean
    public FilterRegistrationBean validationFilterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new CustomCas30ProxyReceivingTicketValidationFilter());
        Map<String, String> initParameters = new HashMap<>();
        initParameters.put("casServerUrlPrefix", casProperties.getCasServerUrlPrefix());
        initParameters.put("serverName", casProperties.getServerName());
        initParameters.put("encoding", "UTF-8");
        initParameters.put("useSession", "true");
        filterRegistrationBean.setInitParameters(initParameters);
        filterRegistrationBean.setOrder(3);
        List<String> urlPatterns = new ArrayList<>();
        urlPatterns.add("/*");
        filterRegistrationBean.setUrlPatterns(urlPatterns);
        if (casProperties.getEnabled()) {
            return filterRegistrationBean;
        }
        return new FilterRegistrationBean<>(new NoCasFilter());
    }

    /**
     * 单点登录获取登录信息
     * @return
     */
    @Bean
    public FilterRegistrationBean casHttpServletRequestWrapperFilter() {
        FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
        authenticationFilter.setFilter(new HttpServletRequestWrapperFilter());
        authenticationFilter.setOrder(4);
        List<String> urlPatterns = new ArrayList<>();
        urlPatterns.add("/*");
        authenticationFilter.setUrlPatterns(urlPatterns);
        if (casProperties.getEnabled()) {
            return authenticationFilter;
        }
        return new FilterRegistrationBean<>(new NoCasFilter());
    }


}

9、添加CustomCas20ProxyReceivingTicketValidationFilter文件

在auth模块中添加CustomCas20ProxyReceivingTicketValidationFilter.java文件:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.traffic.auth.cas.filter;

import java.io.IOException;
import java.security.PrivateKey;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
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 javax.servlet.http.HttpServletResponse;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.proxy.AbstractEncryptedProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.proxy.Cas20ProxyRetriever;
import org.jasig.cas.client.proxy.CleanUpTimerTask;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.PrivateKeyUtils;
import org.jasig.cas.client.util.ReflectUtils;
import org.jasig.cas.client.validation.AbstractTicketValidationFilter;
import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.jasig.cas.client.validation.TicketValidator;

public class CustomCas20ProxyReceivingTicketValidationFilter extends AbstractTicketValidationFilter {
    private static final String[] RESERVED_INIT_PARAMS;
    private String proxyReceptorUrl;
    private Timer timer;
    private TimerTask timerTask;
    private int millisBetweenCleanUps;
    protected Class<? extends Cas20ServiceTicketValidator> defaultServiceTicketValidatorClass;
    protected Class<? extends Cas20ProxyTicketValidator> defaultProxyTicketValidatorClass;
    private PrivateKey privateKey;
    private ProxyGrantingTicketStorage proxyGrantingTicketStorage;

    public CustomCas20ProxyReceivingTicketValidationFilter() {
        this(Protocol.CAS2);
        this.defaultServiceTicketValidatorClass = Cas20ServiceTicketValidator.class;
        this.defaultProxyTicketValidatorClass = Cas20ProxyTicketValidator.class;
    }

    protected CustomCas20ProxyReceivingTicketValidationFilter(Protocol protocol) {
        super(protocol);
        this.proxyGrantingTicketStorage = new ProxyGrantingTicketStorageImpl();
    }

    @Override
    protected void initInternal(FilterConfig filterConfig) throws ServletException {
        this.setProxyReceptorUrl(this.getString(ConfigurationKeys.PROXY_RECEPTOR_URL));
        Class<? extends ProxyGrantingTicketStorage> proxyGrantingTicketStorageClass = this.getClass(ConfigurationKeys.PROXY_GRANTING_TICKET_STORAGE_CLASS);
        if (proxyGrantingTicketStorageClass != null) {
            this.proxyGrantingTicketStorage = (ProxyGrantingTicketStorage)ReflectUtils.newInstance(proxyGrantingTicketStorageClass, new Object[0]);
            if (this.proxyGrantingTicketStorage instanceof AbstractEncryptedProxyGrantingTicketStorageImpl) {
                AbstractEncryptedProxyGrantingTicketStorageImpl p = (AbstractEncryptedProxyGrantingTicketStorageImpl)this.proxyGrantingTicketStorage;
                String cipherAlgorithm = this.getString(ConfigurationKeys.CIPHER_ALGORITHM);
                String secretKey = this.getString(ConfigurationKeys.SECRET_KEY);
                p.setCipherAlgorithm(cipherAlgorithm);

                try {
                    if (secretKey != null) {
                        p.setSecretKey(secretKey);
                    }
                } catch (Exception var7) {
                    throw new RuntimeException(var7);
                }
            }
        }

        this.millisBetweenCleanUps = this.getInt(ConfigurationKeys.MILLIS_BETWEEN_CLEAN_UPS);
        this.privateKey = buildPrivateKey(this.getString(ConfigurationKeys.PRIVATE_KEY_PATH), this.getString(ConfigurationKeys.PRIVATE_KEY_ALGORITHM));
        super.initInternal(filterConfig);
    }

    @Override
    public void init() {
        super.init();
        CommonUtils.assertNotNull(this.proxyGrantingTicketStorage, "proxyGrantingTicketStorage cannot be null.");
        if (this.timer == null) {
            this.timer = new Timer(true);
        }

        if (this.timerTask == null) {
            this.timerTask = new CleanUpTimerTask(this.proxyGrantingTicketStorage);
        }

        this.timer.schedule(this.timerTask, (long)this.millisBetweenCleanUps, (long)this.millisBetweenCleanUps);
    }

    private Cas20ServiceTicketValidator createNewTicketValidator(Class<? extends Cas20ServiceTicketValidator> ticketValidatorClass, String casServerUrlPrefix, Class<? extends Cas20ServiceTicketValidator> clazz) {
        return ticketValidatorClass == null ? ReflectUtils.newInstance(clazz, new Object[]{casServerUrlPrefix}) : ReflectUtils.newInstance(ticketValidatorClass, new Object[]{casServerUrlPrefix});
    }

    public static PrivateKey buildPrivateKey(String keyPath, String keyAlgorithm) {
        return keyPath != null ? PrivateKeyUtils.createKey(keyPath, keyAlgorithm) : null;
    }

    @Override
    protected final TicketValidator getTicketValidator(FilterConfig filterConfig) {
        boolean allowAnyProxy = this.getBoolean(ConfigurationKeys.ACCEPT_ANY_PROXY);
        String allowedProxyChains = this.getString(ConfigurationKeys.ALLOWED_PROXY_CHAINS);
        String casServerUrlPrefix = this.getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX);
        Class<? extends Cas20ServiceTicketValidator> ticketValidatorClass = this.getClass(ConfigurationKeys.TICKET_VALIDATOR_CLASS);
        Object validator;
        if (!allowAnyProxy && !CommonUtils.isNotBlank(allowedProxyChains)) {
            validator = (Cas20ServiceTicketValidator)this.createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix, this.defaultServiceTicketValidatorClass);
        } else {
            Cas20ProxyTicketValidator v = (Cas20ProxyTicketValidator)this.createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix, this.defaultProxyTicketValidatorClass);
            v.setAcceptAnyProxy(allowAnyProxy);
            v.setAllowedProxyChains(CommonUtils.createProxyList(allowedProxyChains));
            validator = v;
        }

        ((Cas20ServiceTicketValidator)validator).setProxyCallbackUrl(this.getString(ConfigurationKeys.PROXY_CALLBACK_URL));
        ((Cas20ServiceTicketValidator)validator).setProxyGrantingTicketStorage(this.proxyGrantingTicketStorage);
        HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(this.getHostnameVerifier(), this.getSSLConfig());
        ((Cas20ServiceTicketValidator)validator).setURLConnectionFactory(factory);
        ((Cas20ServiceTicketValidator)validator).setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix, this.getString(ConfigurationKeys.ENCODING), factory));
        ((Cas20ServiceTicketValidator)validator).setRenew(this.getBoolean(ConfigurationKeys.RENEW));
        ((Cas20ServiceTicketValidator)validator).setEncoding(this.getString(ConfigurationKeys.ENCODING));
        Map<String, String> additionalParameters = new HashMap();
        List<String> params = Arrays.asList(RESERVED_INIT_PARAMS);
        Enumeration e = filterConfig.getInitParameterNames();

        while(e.hasMoreElements()) {
            String s = (String)e.nextElement();
            if (!params.contains(s)) {
                additionalParameters.put(s, filterConfig.getInitParameter(s));
            }
        }

        ((Cas20ServiceTicketValidator)validator).setPrivateKey(this.privateKey);
        ((Cas20ServiceTicketValidator)validator).setCustomParameters(additionalParameters);
        return (TicketValidator)validator;
    }

    @Override
    public void destroy() {
        super.destroy();
        this.timer.cancel();
    }

    @Override
    protected final boolean preFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        String requestUri = request.getRequestURI();
        //重设serverName
        String service = (String) request.getAttribute("service");
        this.setServerName(service);
        if (!CommonUtils.isEmpty(this.proxyReceptorUrl) && requestUri.endsWith(this.proxyReceptorUrl)) {
            try {
                CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
                return false;
            } catch (RuntimeException var8) {
                this.logger.error(var8.getMessage(), var8);
                throw var8;
            }
        } else {
            return true;
        }
    }

    public final void setProxyReceptorUrl(String proxyReceptorUrl) {
        this.proxyReceptorUrl = proxyReceptorUrl;
    }

    public void setProxyGrantingTicketStorage(ProxyGrantingTicketStorage storage) {
        this.proxyGrantingTicketStorage = storage;
    }

    public void setTimer(Timer timer) {
        this.timer = timer;
    }

    public void setTimerTask(TimerTask timerTask) {
        this.timerTask = timerTask;
    }

    public void setMillisBetweenCleanUps(int millisBetweenCleanUps) {
        this.millisBetweenCleanUps = millisBetweenCleanUps;
    }

    static {
        RESERVED_INIT_PARAMS = new String[]{ConfigurationKeys.ARTIFACT_PARAMETER_NAME.getName(), ConfigurationKeys.SERVER_NAME.getName(), ConfigurationKeys.SERVICE.getName(), ConfigurationKeys.RENEW.getName(), ConfigurationKeys.LOGOUT_PARAMETER_NAME.getName(), ConfigurationKeys.ARTIFACT_PARAMETER_OVER_POST.getName(), ConfigurationKeys.EAGERLY_CREATE_SESSIONS.getName(), ConfigurationKeys.ENCODE_SERVICE_URL.getName(), ConfigurationKeys.SSL_CONFIG_FILE.getName(), ConfigurationKeys.ROLE_ATTRIBUTE.getName(), ConfigurationKeys.IGNORE_CASE.getName(), ConfigurationKeys.CAS_SERVER_LOGIN_URL.getName(), ConfigurationKeys.GATEWAY.getName(), ConfigurationKeys.AUTHENTICATION_REDIRECT_STRATEGY_CLASS.getName(), ConfigurationKeys.GATEWAY_STORAGE_CLASS.getName(), ConfigurationKeys.CAS_SERVER_URL_PREFIX.getName(), ConfigurationKeys.ENCODING.getName(), ConfigurationKeys.TOLERANCE.getName(), ConfigurationKeys.IGNORE_PATTERN.getName(), ConfigurationKeys.IGNORE_URL_PATTERN_TYPE.getName(), ConfigurationKeys.HOSTNAME_VERIFIER.getName(), ConfigurationKeys.HOSTNAME_VERIFIER_CONFIG.getName(), ConfigurationKeys.EXCEPTION_ON_VALIDATION_FAILURE.getName(), ConfigurationKeys.REDIRECT_AFTER_VALIDATION.getName(), ConfigurationKeys.USE_SESSION.getName(), ConfigurationKeys.SECRET_KEY.getName(), ConfigurationKeys.CIPHER_ALGORITHM.getName(), ConfigurationKeys.PROXY_RECEPTOR_URL.getName(), ConfigurationKeys.PROXY_GRANTING_TICKET_STORAGE_CLASS.getName(), ConfigurationKeys.MILLIS_BETWEEN_CLEAN_UPS.getName(), ConfigurationKeys.ACCEPT_ANY_PROXY.getName(), ConfigurationKeys.ALLOWED_PROXY_CHAINS.getName(), ConfigurationKeys.TICKET_VALIDATOR_CLASS.getName(), ConfigurationKeys.PROXY_CALLBACK_URL.getName(), ConfigurationKeys.RELAY_STATE_PARAMETER_NAME.getName(), ConfigurationKeys.METHOD.getName(), ConfigurationKeys.PRIVATE_KEY_PATH.getName(), ConfigurationKeys.PRIVATE_KEY_ALGORITHM.getName()};
    }
}

10、添加CustomCas30ProxyReceivingTicketValidationFilter文件

在auth模块中添加CustomCas30ProxyReceivingTicketValidationFilter.java文件:

package com.traffic.auth.cas.filter;

import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter;
import org.jasig.cas.client.validation.Cas30ProxyTicketValidator;
import org.jasig.cas.client.validation.Cas30ServiceTicketValidator;

import javax.servlet.FilterConfig;
import javax.servlet.ServletException;

/**
 * @author LuoFei
 * @className: CustomCas30ProxyReceivingTicketValidationFilter
 * @projectName Traffic-Cloud
 * @description: TODO
 * @date 2023/8/26 14:13
 */
public class CustomCas30ProxyReceivingTicketValidationFilter extends CustomCas20ProxyReceivingTicketValidationFilter {

    public CustomCas30ProxyReceivingTicketValidationFilter() {
        super(Protocol.CAS3);
        this.defaultServiceTicketValidatorClass = Cas30ServiceTicketValidator.class;
        this.defaultProxyTicketValidatorClass = Cas30ProxyTicketValidator.class;
    }
}

11、添加CustomAuthenticationFilter文件

在auth模块中添加CustomAuthenticationFilter.java文件:

package com.traffic.auth.cas.filter;

import com.traffic.common.core.utils.StringUtils;
import org.jasig.cas.client.util.AbstractCasFilter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author LuoFei
 * @className: CustomAuthenticationFilter
 * @projectName Traffic-Cloud
 * @description: TODO
 * @date 2023/8/25 14:27
 */
public final class CustomAuthenticationFilter implements Filter {

    private AbstractCasFilter casFilter;

    private String serviceName;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //重设serverName
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        String service = request.getParameter("service");
        if (StringUtils.isNotBlank(service)) {
            this.serviceName = service;
            casFilter.setServerName(this.serviceName);
        } else {
            request.setAttribute("service", this.serviceName);
        }
        casFilter.doFilter(servletRequest, servletResponse, filterChain);
    }

    public AbstractCasFilter getCasFilter() {
        return casFilter;
    }

    public void setCasFilter(AbstractCasFilter authenticationFilter) {
        this.casFilter = authenticationFilter;
    }
}

12、放行casLogin请求

修改common-security模块中的WebMvcConfig.java,放行casLogin请求:


/** 不需要拦截地址 */
public static final String[] excludeUrls = { "/casLogin", "/login", "/logout", "/refresh", "/register" };

修改nacos中ruoyi-gateway-dev.yml,在网关中放行casLogin请求:

 # 不校验白名单
  ignore:
    whites:
      - /auth/casLogin
      - /auth/getToken
      - /auth/logout
      - /auth/login
      - /auth/register
      - /auth/updatePassword
      - /*/v2/api-docs
      - /csrf

至此,即完成若依微服务后端CAS集成工作。

若依微服务前端集成与分离版相同,请参考若依分离版集成CAS。

RuoYi-Cloud的前端(Vue3 Element Plus Vite)版本:

https://github.com/yangzongzhuan/RuoYi-Cloud-Vue3

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值