若依(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)版本: