内嵌系統登出,session統一登出,redis管理,單機與叢集
在項目中遇到了這麼一個問題,在這裡記錄當時解決的方式
出現的問題:系統内嵌系統,當時外系統登出使用者後發現内嵌iframe的另一個系統沒有登出,緩存仍然在,依然可以使用使用者身份通路,外系統登陸另一個使用者賬号時,内嵌系統任然是上一次或第一次登陸的使用者資訊,導緻使用者資訊不比對和其它操作的漏洞。
調試很多次,發現問題出在由于外系統和内嵌系統session不是一個session,沒有做session共享登出時CAS會通知外系統下的所有子系統退出,由于内嵌系統session沒有清除而緻。
解決過程:
配置檔案:位址根據自己項目配置
# spring.redis
spring.redis.cluster.nodes=localhost:7001,localhost:7002,localhost:7003,localhost:7004,localhost:7005,localhost:7006
spring.redis.pool.max-active=300
spring.redis.database=0
spring.redis.pool.max-wait=-1
spring.redis.pool.max-idle=100
spring.redis.pool.min-idle=20
spring.redis.timeout=60000
# redis session
spring.session.store-type=redis
# spring.cas
spring.cas.cas-server-login-url = https://localhost:7443/cas/login
spring.cas.cas-server-url-prefix = https://localhost:7443/cas
spring.cas.server-name = http://localhost/web/index
shiro-cas.casServerUrlPrefix = https://local:7443/cas
shiro-cas.casLoginUrl = ${shiro-cas.casServerUrlPrefix}/login
shiro-cas.casLogoutUrl = ${shiro-cas.casServerUrlPrefix}/logout
shiro-cas.shiroServerUrlPrefix = 内嵌的位址
shiro-cas.casFilterUrlPattern = /cas
shiro-cas.logout = ${shiro-cas.shiroServerUrlPrefix}/logout
shiro-cas.logoutFilter = /logout
shiro-cas.loginUrl = ${shiro-cas.casLoginUrl}?service=${shiro-cas.shiroServerUrlPrefix}${shiro-cas.casFilterUrlPattern}
shiro-cas.logoutUrl = ${shiro-cas.casLogoutUrl}?service=${shiro-cas.casLoginUrl}?service=${shiro-cas.shiroServerUrlPrefix}${shiro-cas.casFilterUrlPattern}
原項目沒接Shiro,隻用CAS做單點,修改後,将cas的配置注釋掉
//@Configuration
public class CasConfig {
重新使用shiro-cas。
pom檔案 注釋cas-client-core,新加shiro-spring,shiro-ehcache,shiro-cas
<!--cas -->
<!--<dependency>-->
<!--<groupId>org.jasig.cas.client</groupId>-->
<!--<artifactId>cas-client-core</artifactId>-->
<!--<version>3.4.1</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-cas</artifactId>
<version>1.2.4</version>
</dependency>
配置類:
1.CasShiroConfig CasShiro的配置檔案,常用配置,以及攔截的請求規則
package com.demo;
import org.apache.shiro.cas.CasFilter;
import org.apache.shiro.cas.CasSubjectFactory;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
*FILE : CasShiroConfig
*DATE :
*AUTHOR
*/
@Configuration
public class CasShiroConfig {
@Autowired
ShrioCasAutoconfig autoconfig;
private String casFilterName = "casFilter";
@Bean
public MyShiroCasRealm myShiroCasRealm() {
MyShiroCasRealm realm = new MyShiroCasRealm();
realm.setCasServerUrlPrefix(autoconfig.getCasServerUrlPrefix());
/**用戶端回調位址**/
realm.setCasService(autoconfig.getShiroServerUrlPrefix() + autoconfig.getCasFilterUrlPattern());
return realm;
}
/**
* 注冊單點登出listener
* @return
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public ServletListenerRegistrationBean singleSignOutHttpSessionListener(){
ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean();
bean.setListener(new SingleSignOutHttpSessionListener());
bean.setEnabled(true);
return bean;
}
/**
* 注冊單點登出filter
* @return
*/
@Bean
public FilterRegistrationBean singleSignOutFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setName("singleSignOutFilter");
bean.setFilter(new SingleSignOutFilterExt());
bean.addUrlPatterns("/*");
// bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
bean.setEnabled(true);
return bean;
}
/**
* 注冊DelegatingFilterProxy(Shiro)
*
* @return
* @author
* @create
*/
@Bean
public FilterRegistrationBean delegatingFilterProxy() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
// 該值預設為false,表示生命周期由SpringApplicationContext管理,設定為true則表示由ServletContainer管理
filterRegistration.addInitParameter("targetFilterLifecycle", "true");
filterRegistration.setEnabled(true);
filterRegistration.addUrlPatterns("/*");
// filterRegistration.setOrder(Integer.MAX_VALUE-10);
return filterRegistration;
}
@Bean(name = "lifecycleBeanPostProcessor")
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
daap.setProxyTargetClass(true);
return daap;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyShiroCasRealm myShiroCasRealm) {
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setRealm(myShiroCasRealm);
dwsm.setSubjectFactory(new CasSubjectFactory());
return dwsm;
}
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
aasa.setSecurityManager(securityManager);
return aasa;
}
/**
* CAS過濾器
*
* @return
* @author
* @create
*/
@Bean(name = "casFilter")
public CasFilter getCasFilter() {
CasFilter casFilter = new CasFilter();
casFilter.setName(casFilterName);
casFilter.setEnabled(true);
// 登入失敗後跳轉的URL,也就是 Shiro 執行 CasRealm 的 doGetAuthenticationInfo 方法向CasServer驗證tiket
// 我們選擇認證失敗後再打開登入頁面
casFilter.setFailureUrl(autoconfig.getCasLoginUrl());
return casFilter;
}
/**
* ShiroFilter
*
* 讀取資料庫相關配置,配置到 shiroFilterFactoryBean 的通路規則中。實際項目中,請使用自己的Service來處理業務邏輯。
*
* @param securityManager
* @param casFilter
* @return
* @author
* @create
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager, CasFilter casFilter) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必須設定 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面
shiroFilterFactoryBean.setLoginUrl(autoconfig.getLoginUrl());
// 登入成功後要跳轉的連接配接
shiroFilterFactoryBean.setSuccessUrl("/index");
// shiroFilterFactoryBean.setUnauthorizedUrl("/error");
// 添加casFilter到shiroFilter中
Map<String, Filter> filters = new HashMap<>();
filters.put("casFilter", casFilter);
// LogoutFilter logoutFilter = new LogoutFilter();
// logoutFilter.setRedirectUrl(autoconfig.getLogoutUrl());
// filters.put("logoutFilter",logoutFilter);
shiroFilterFactoryBean.setFilters(filters);
loadShiroFilterChain(shiroFilterFactoryBean);
return shiroFilterFactoryBean;
}
/**
* 加載shiroFilter權限控制規則(從資料庫讀取然後配置),角色/權限資訊由MyShiroCasRealm對象提供doGetAuthorizationInfo實作擷取來的
*
* @author
* @create
*/
private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean){
/// 下面這些規則配置最好配置到配置檔案中 ///
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// authc:該過濾器下的頁面必須登入後才能通路,它是Shiro内置的一個攔截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
// anon: 可以了解為不攔截
// user: 登入了就不攔截
// roles["admin"] 使用者擁有admin角色
// perms["permission1"] 使用者擁有permission1權限
// filter順序按照定義順序比對,比對到就驗證,驗證完畢結束。
// url比對通配符支援:? * **,分别表示比對1個,比對0-n個(不含子路徑),比對下級所有路徑
//1.shiro內建cas後,首先添加該規則
filterChainDefinitionMap.put("/cas", casFilterName);
// filterChainDefinitionMap.put("/logout","logoutFilter");
//2.不攔截的請求,根據自己兩目配置
filterChainDefinitionMap.put("/static/**","anon");
filterChainDefinitionMap.put("/css/**","anon");
filterChainDefinitionMap.put("/js/**","anon");
filterChainDefinitionMap.put("/login", "anon");
// filterChainDefinitionMap.put("/logout","anon");
filterChainDefinitionMap.put("/error","anon");
filterChainDefinitionMap.put("/api/**","anon");
//3.攔截的請求(從本地資料庫擷取或者從casserver擷取(webservice,http等遠端方式),看你的角色權限配置在哪裡)
//需要登入
filterChainDefinitionMap.put("/web/*", "authc");
//4.登入過的不攔截
filterChainDefinitionMap.put("/**", "user");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
}
}
2.自定義MyShiroCasRealm繼承CasRealm
package com.demo;
import com.demo.SessionUserUtil;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cas.CasRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Created by
*/
public class MyShiroCasRealm extends CasRealm {
// @Autowired
// protected UserService userService;
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 權限認證,為目前登入的Subject授予角色和權限
* @see :本例中該方法的調用時機為需授權資源被通路時
* @see :并且每次通路需授權資源時都會執行該方法中的邏輯,這表明本例中預設并未啟用AuthorizationCache
* @see :如果連續通路同一個URL(比如重新整理),該方法不會被重複調用,Shiro有一個時間間隔(也就是cache時間,在ehcache-shiro.xml中配置),超過這個時間間隔再重新整理頁面,該方法會被執行
*/
@SuppressWarnings("unchecked")
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
System.out.println(SessionUserUtil.getCurrentUsername());
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo authc = super.doGetAuthenticationInfo(token);
String userName = (String) authc.getPrincipals().getPrimaryPrincipal();
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
// 可在此處理session塞入一些需要的資訊
return authc;
}
}
3.RedisSessionConfig 裡面又CAS過期時間的配置
package com.demo;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 28800)
public class RedisSessionConfig {
}
4.ShrioCasAutoconfig shiro-cas的配置,配置檔案中讀取加載配置進config,CAS退出、登陸的Url
package com.demo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Created
*/
@Component
@ConfigurationProperties(prefix = "shiro-cas")
public class ShrioCasAutoconfig {
private String casServerUrlPrefix;
private String casLoginUrl;
private String casLogoutUrl;
private String shiroServerUrlPrefix;
private String casFilterUrlPattern;
private String loginUrl;
private String logoutUrl;
private String logout;
public String getCasServerUrlPrefix() {
return casServerUrlPrefix;
}
public void setCasServerUrlPrefix(String casServerUrlPrefix) {
this.casServerUrlPrefix = casServerUrlPrefix;
}
public String getCasLoginUrl() {
return casLoginUrl;
}
public void setCasLoginUrl(String casLoginUrl) {
this.casLoginUrl = casLoginUrl;
}
public String getCasLogoutUrl() {
return casLogoutUrl;
}
public void setCasLogoutUrl(String casLogoutUrl) {
this.casLogoutUrl = casLogoutUrl;
}
public String getShiroServerUrlPrefix() {
return shiroServerUrlPrefix;
}
public void setShiroServerUrlPrefix(String shiroServerUrlPrefix) {
this.shiroServerUrlPrefix = shiroServerUrlPrefix;
}
public String getCasFilterUrlPattern() {
return casFilterUrlPattern;
}
public void setCasFilterUrlPattern(String casFilterUrlPattern) {
this.casFilterUrlPattern = casFilterUrlPattern;
}
public String getLoginUrl() {
return loginUrl;
}
public void setLoginUrl(String loginUrl) {
this.loginUrl = loginUrl;
}
public String getLogoutUrl() {
return logoutUrl;
}
public void setLogoutUrl(String logoutUrl) {
this.logoutUrl = logoutUrl;
}
public String getLogout() {
return logout;
}
public void setLogout(String logout) {
this.logout = logout;
}
}
5.SingleSignOutExtHandler 登出過濾的處理
handler.isTokenRequest(request)方法:判斷參數中是否具有artifactParameterName屬性指定的參數名稱,預設是ticket在SingleSignOutFilterExt.java中配置 。如果存在,在redis中記錄session。
handler.isLogoutRequest(request):判斷是否具有logoutParameterName參數指定參數,預設logoutRequest在SingleSignOutFilterExt.java中配置。如果存在,則在redis中删除記錄,登出session。
recordSession: 儲存session,其中addRedisHash方法中,注釋掉的是單機redis方式
removeRedisLoginInfoByTicket方法:通過存儲的票據(Ticket)删除登陸時存入的資訊以及退出使用者的票據。相當于我們在登陸的時候,為了可以找到登陸的session,多存了一個redisHash,以項目名稱和ticket為key,session為value,退出的時候将兩個地方同時删除。
package com.demo;
import com.demo.JedisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.session.HashMapBackedSessionMappingStorage;
import org.jasig.cas.client.session.SessionMappingStorage;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.XmlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.JedisCluster;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* Performs CAS single sign-out operations in an API-agnostic fashion.
*
* @author Marvin S. Addison
* @version $Revision$ $Date$
* @since 3.1.12
*
*/
public final class SingleSignOutExtHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(SingleSignOutExtHandler.class);
/** Logger instance */
private final Log log = LogFactory.getLog(getClass());
/** Mapping of token IDs and session IDs to HTTP sessions */
private SessionMappingStorage sessionMappingStorage = new HashMapBackedSessionMappingStorage();
/** The name of the artifact parameter. This is used to capture the session identifier. */
private String artifactParameterName = "ticket";
/** Parameter name that stores logout request */
private String logoutParameterName = "logoutRequest";
public void setSessionMappingStorage(final SessionMappingStorage storage) {
this.sessionMappingStorage = storage;
}
public SessionMappingStorage getSessionMappingStorage() {
return this.sessionMappingStorage;
}
/**
* @param name Name of the authentication token parameter.
*/
public void setArtifactParameterName(final String name) {
this.artifactParameterName = name;
}
/**
* @param name Name of parameter containing CAS logout request message.
*/
public void setLogoutParameterName(final String name) {
this.logoutParameterName = name;
}
/**
* Initializes the component for use.
*/
public void init() {
CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
CommonUtils.assertNotNull(this.logoutParameterName, "logoutParameterName cannot be null.");
CommonUtils.assertNotNull(this.sessionMappingStorage, "sessionMappingStorage cannote be null.");
}
/**
* Determines whether the given request contains an authentication token.
*
* @param request HTTP reqest.
*
* @return True if request contains authentication token, false otherwise.
*/
public boolean isTokenRequest(final HttpServletRequest request) {
return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.artifactParameterName));
}
/**
* Determines whether the given request is a CAS logout request.
*
* @param request HTTP request.
*
* @return True if request is logout request, false otherwise.
*/
public boolean isLogoutRequest(final HttpServletRequest request) {
return "POST".equals(request.getMethod()) && !isMultipartRequest(request) &&
CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName));
}
/**
* Associates a token request with the current HTTP session by recording the mapping
* in the the configured {@link SessionMappingStorage} container.
*
* @param request HTTP request containing an authentication token.
*/
public void recordSession(final HttpServletRequest request) {
final HttpSession session = request.getSession(true);
final String token = CommonUtils.safeGetParameter(request, this.artifactParameterName);
if (log.isDebugEnabled()) {
log.debug("Recording session for token " + token);
}
try {
this.sessionMappingStorage.removeBySessionById(session.getId());
} catch (final Exception e) {
// ignore if the session is already marked as invalid. Nothing we can do!
}
sessionMappingStorage.addSessionById(token, session);
addRedisHash(token, token, session.getId());
}
/**
* Destroys the current HTTP session for the given CAS logout request.
*
* @param request HTTP request containing a CAS logout message.
*/
public void destroySession(final HttpServletRequest request) {
final String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName);
if (log.isTraceEnabled()) {
log.trace ("Logout request:\n" + logoutMessage);
}
final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");
if (CommonUtils.isNotBlank(token)) {
final HttpSession session = this.sessionMappingStorage.removeSessionByMappingId(token);
if (session != null) {
String sessionID = session.getId();
if (log.isDebugEnabled()) {
log.debug ("Invalidating session [" + sessionID + "] for token [" + token + "]");
}
try {
session.invalidate();
} catch (final IllegalStateException e) {
log.debug("Error invalidating session.", e);
}
}
}
//根據ticket,查找到對應的sessionID,并從redis中清除
removeRedisLoginInfoByTicket(token);
}
private boolean isMultipartRequest(final HttpServletRequest request) {
return request.getContentType() != null && request.getContentType().toLowerCase().startsWith("multipart");
}
private void addRedisHash(String hash, String key, String value) {
new Thread(new Runnable() {
//需要擷取項目名稱,以便在緩存中可以取到
String applicationName = SpringUtilExt.getPropertyValue("spring.application.name");
@Override
public void run() {
// JedisConnectionFactory jedisConnectionFactory = SpringUtilExt.getBean(JedisConnectionFactory.class);
// RedisConnection redisConnection = jedisConnectionFactory.getConnection();
// hash的名稱是類似bcs:ST-8142-Qy1cb554R9dJBXtbaEBV-cas01.example.org
// ((Jedis)redisConnection.getNativeConnection()).hset(applicationName + ":" + hash, key, value);
// LOGGER.info("add to redis hash {}:{}->{}", new Object[]{applicationName + ":" + hash, key, value});
// redisConnection.close();
try{
JedisUtil.setHashJedisCluster(applicationName + ":" + hash, key, value);
LOGGER.info("add to redis hash {}:{}->{}", new Object[]{applicationName + ":" + hash, key, value});
}catch (Exception e){
LOGGER.info("==========增加session異常",e);
}
}
}).start();
}
private void removeRedisLoginInfoByTicket(String token) {
new Thread(new Runnable() {
@Override
public void run() {
// JedisConnectionFactory jedisConnectionFactory = SpringUtilExt.getBean(JedisConnectionFactory.class);
// Jedis jedisConnection = (Jedis)jedisConnectionFactory.getConnection().getNativeConnection();
String applicationName = SpringUtilExt.getPropertyValue("spring.application.name");
// String sessionId = jedisConnection.hget(applicationName + ":" +token, token);
try{
JedisCluster jedisCluster = JedisUtil.getJedisClusterConnection();
String sessionId = jedisCluster.hget(applicationName + ":" +token, token);
LOGGER.info("擷取到自定義中ticket:{}對應的sessionId:{}", new Object[]{token, sessionId});
String casRedisHash = "spring:session:" + applicationName + ":sessions:" + sessionId;
jedisCluster.del(casRedisHash);
LOGGER.info("成功删除redis中cas存儲的hash{}", new Object[]{casRedisHash});
jedisCluster.del(applicationName + ":"+token);
LOGGER.info("成功删除自定義cas存儲的hash{}", new Object[]{applicationName + ":"+token});
jedisCluster.close();
}catch (Exception e){
LOGGER.info("清除session方法異常,{}",e);
}
}
}).start();
}
}
6.SingleSignOutFilterExt
doFilter方法中判斷分支判斷handler進入登陸時儲存或者時登出時登出,或者是已經登陸過,進行不同處理。
package com.demo;
import org.jasig.cas.client.session.SessionMappingStorage;
import org.jasig.cas.client.util.AbstractConfigurationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
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 java.io.IOException;
/**
* Implements the Single Sign Out protocol. It handles registering the session and destroying the session.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public final class SingleSignOutFilterExt extends AbstractConfigurationFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(SingleSignOutFilterExt.class);
private static final SingleSignOutExtHandler handler = new SingleSignOutExtHandler();
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
if (!isIgnoreInitConfiguration()) {
handler.setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket"));
handler.setLogoutParameterName(getPropertyFromInitParams(filterConfig, "logoutParameterName", "logoutRequest"));
}
handler.init();
}
public void setArtifactParameterName(final String name) {
handler.setArtifactParameterName(name);
}
public void setLogoutParameterName(final String name) {
handler.setLogoutParameterName(name);
}
public void setSessionMappingStorage(final SessionMappingStorage storage) {
handler.setSessionMappingStorage(storage);
}
@Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
if (handler.isTokenRequest(request)) {
handler.recordSession(request);
} else if (handler.isLogoutRequest(request)) {
handler.destroySession(request);
// Do not continue up filter chain
return;
} else {
log.trace("Ignoring URI " + request.getRequestURI());
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
// nothing to do
}
protected static SingleSignOutExtHandler getSingleSignOutHandler() {
return handler;
}
}
7.SpringCasAutoConfig spring.cas的配置
package com.demo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* Created with IntelliJ IDEA.
*
* @author:
* @date:
* Description:
*/
@Component
@ConfigurationProperties(prefix = "spring.cas")
public class SpringCasAutoConfig {
private static final String SEPARATOR = ",";
private String validateFilters;
private String signOutFilters;
private String authFilters;
private String assertionFilters;
private String requestWrapperFilters;
private String casServerUrlPrefix;
private String casServerLoginUrl;
private String serverName;
private boolean useSession = true;
private boolean redirectAfterValidation = true;
public List<String> getValidateFilters() {
return Arrays.asList(validateFilters.split(SEPARATOR));
}
public void setValidateFilters(String validateFilters) {
this.validateFilters = validateFilters;
}
public List<String> getSignOutFilters() {
return Arrays.asList(signOutFilters.split(SEPARATOR));
}
public void setSignOutFilters(String signOutFilters) {
this.signOutFilters = signOutFilters;
}
public List<String> getAuthFilters() {
return Arrays.asList(authFilters.split(SEPARATOR));
}
public void setAuthFilters(String authFilters) {
this.authFilters = authFilters;
}
public List<String> getAssertionFilters() {
return Arrays.asList(assertionFilters.split(SEPARATOR));
}
public void setAssertionFilters(String assertionFilters) {
this.assertionFilters = assertionFilters;
}
public List<String> getRequestWrapperFilters() {
return Arrays.asList(requestWrapperFilters.split(SEPARATOR));
}
public void setRequestWrapperFilters(String requestWrapperFilters) {
this.requestWrapperFilters = requestWrapperFilters;
}
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;
}
public boolean isRedirectAfterValidation() {
return redirectAfterValidation;
}
public void setRedirectAfterValidation(boolean redirectAfterValidation) {
this.redirectAfterValidation = redirectAfterValidation;
}
public boolean isUseSession() {
return useSession;
}
public void setUseSession(boolean useSession) {
this.useSession = useSession;
}
}
8.SpringUtilExt 工具類,可以取配置檔案資訊
package com.demo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
public class SpringUtilExt implements ApplicationContextAware {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringUtilExt.class);
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtilExt.applicationContext == null) {
SpringUtilExt.applicationContext = applicationContext;
}
}
//擷取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通過name擷取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通過class擷取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通過name,以及Clazz傳回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
// 通過屬性名稱擷取屬性值
public static String getPropertyValue(String propertyKey) {
Environment env = getApplicationContext().getBean(Environment.class);
String propertyValue = env.getProperty(propertyKey);
LOGGER.info("擷取到屬性名稱:【{}】 -> 【{}】", new Object[]{propertyKey, propertyValue});
return propertyValue;
}
}
9.redis叢集方式擷取jedis操作
package com.demo;
import com.demo.SpringUtilExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
*
*/
@Service
public class JedisUtil {
private static Logger logger = LoggerFactory.getLogger(JedisUtil.class);
private static String clusterNodes = SpringUtilExt.getPropertyValue("spring.redis.cluster.nodes");
public static String getClusterNodes() {
return clusterNodes;
}
/**
* 擷取叢集模式的redis連接配接資訊
* @return
*/
public static JedisCluster getJedisClusterConnection(){
JedisCluster cluster = null;
Set<HostAndPort> nodes = new LinkedHashSet<HostAndPort>();
for(String item :clusterNodes.split(",")){
nodes.add(new HostAndPort(item.split(":")[0],Integer.valueOf(item.split(":")[1])));
}
try {
cluster = new JedisCluster(nodes,10000);
} catch (Exception e) {
logger.error("系統異常!",e);
}
return cluster;
}
/**
* 擷取叢集模式的redis連接配接資訊
* @return
*/
public static void closeJedisClusterConnection(JedisCluster jedisCluster){
try {
jedisCluster.close();
} catch (IOException e) {
logger.error("jedis關閉異常!",e);
}
}
/**
* 從叢集模式的redis中擷取資料
* @param key
* @return
*/
public static String getStringFromJedisCluster(String key){
JedisCluster jedisCluster = getJedisClusterConnection();
return jedisCluster.get(key);
}
/**
* 向叢集模式的redis中塞值,如果逾時時間設定的<=0,認為不必設定有效期
* @param key
* @param value
* @return
*/
public static String setString2JedisCluster(String key,String value,int seconds){
JedisCluster jedisCluster = getJedisClusterConnection();
if(seconds <= 0){
return jedisCluster.set(key,value);
}else{
return jedisCluster.setex(key,seconds,value);
}
}
/**
* 向叢集模式的redis中塞值,如果逾時時間設定的<=0,認為不必設定有效期
* @param key
* @param value
* @return
*/
public static void setHashJedisCluster(String hash,String key,String value){
JedisCluster jedisCluster = getJedisClusterConnection();
jedisCluster.hset(hash,key,value);
try {
jedisCluster.close();
} catch (Exception e){
logger.error("系統異常!",e);
}
}
}
啟動類加注解
@EnableRedisHttpSession(redisNamespace=“項目名稱”,maxInactiveIntervalInSeconds = 28800)
項目名稱要與存session那邊一緻,否則找不到删除不掉。
到這裡本次問題就差不多解決了,可能有漏記的,再繼續學習補充,小白學習日記
參考了很多大佬的文章,得罪了😄,也忘了有哪些了,就不記錄了。