Spring security 使用redis作为会话存储的KeyClope反向通道注销spring会话 我使用keydove作为(单点登录/注销)功能的中央身份验证服务 我有app1,app2,app3。app1和app2是单感官应用。app3使用spring会话(使用redis作为会话存储) 所有功能都很好。但我使用后台通道注销SSO(单点注销)功能,这对app1和app2都有效。但它不适用于此app3
我想知道当客户端用户向其发送注销请求时,如何支持使用spring会话的通道注销应用程序调用KeyDope管理员url。我发现对于addKeyDoppeAuthenticatorValve,使用KeyDoppeAutoConfiguration>GetKeyDoppeContainerCustomizer()injectWebServerFactoryCustomizer,那个阀门呢 使用CatalInUserSessionManagement,但它没有任何关于redis的信息作为其会话存储。因此,我添加了一个自定义项来增强阀门Spring security 使用redis作为会话存储的KeyClope反向通道注销spring会话 我使用keydove作为(单点登录/注销)功能的中央身份验证服务 我有app1,app2,app3。app1和app2是单感官应用。app3使用spring会话(使用redis作为会话存储) 所有功能都很好。但我使用后台通道注销SSO(单点注销)功能,这对app1和app2都有效。但它不适用于此app3,spring-security,redis,keycloak,spring-session,Spring Security,Redis,Keycloak,Spring Session,我想知道当客户端用户向其发送注销请求时,如何支持使用spring会话的通道注销应用程序调用KeyDope管理员url。我发现对于addKeyDoppeAuthenticatorValve,使用KeyDoppeAutoConfiguration>GetKeyDoppeContainerCustomizer()injectWebServerFactoryCustomizer,那个阀门呢 使用CatalInUserSessionManagement,但它没有任何关于redis的信息作为其会话存储。因此
@Slf4j
@组成部分
公共类BeanFactoryOrderWrapper实现DestructionAwareBeanPostProcessor{
@凌驾
public void PostProcessBeforeDestroyment(对象bean、字符串beanName)抛出BeanException{
}
@凌驾
公共布尔要求销毁(对象bean){
返回true;
}
@凌驾
公共对象后处理BeforeInitialization(对象bean、字符串beanName)抛出BeanException{
if(beanName.equals(“GetKeyDoveContainerCustomizer”)){
objectwrapres=this.wrapOrder(bean);
返回wrapRes;
}
返回豆;
}
@凌驾
公共对象后处理初始化后(对象bean、字符串beanName)抛出BeansException{
返回豆;
}
私有对象包装器(对象bean){
log.info(“为下一个定制重写KeyClope自动配置定制器订单”);
最终WebServerFactoryCustomizer源=(WebServerFactoryCustomizer)bean;
返回新的keydoveecontainercustomizerWithOrder(源代码);
}
}
类KeyCoverContainerCustomizerWithOrder实现WebServerFactoryCustomizer,Ordered{
私有最终WebServerFactoryCustomizer来源;
public KeyDopperContainerCustomizerWithOrder(WebServerFactoryCustomizer来源){
this.origin=origin;
}
@凌驾
public void自定义(ConfigurableServletWebServerFactory){
产地:定制(工厂);
}
@凌驾
public int getOrder(){
返回顺序。最低_优先级-1;
}
}
@Slf4j
@配置
@所需参数构造函数
类别容器配置{
私有最终RedisIndexedSessionRepository会话存储库;
@豆子
public WebServerFactoryCustomizer GetKeyDopperContainerCustomizerGai(){
返回configurableServletWebServerFactory->{
if(TomcatServletWebServerFactory的可配置ServletWebServerFactory实例){
TomcatServletWebServerFactory容器=(TomcatServletWebServerFactory)configurableServletWebServerFactory;
container.getContextValves().stream().filter(ele->ele.getClass()==keydeportAuthenticatorValve.class).findFirst().map(ele->(abstractkeydeportAuthenticatorValve)ele).ifPresent(valve->{
试一试{
final Field=AbstractKeyDoppeAuthenticatorValve.class.getDeclaredField(“userSessionManagement”);
字段。setAccessible(true);
最终CatalInUserSessionManagement原点=(CatalInUserSessionManagement)字段.get(阀门);
field.set(valve,新CatalInUserSessionManagementGai(origin,sessionRepository));
}捕获(例外e){
日志错误(“增强阀故障”);
}
});
}
};
}
}
@Slf4j
类catalinusersessionmanagementgai扩展了catalinusersessionmanagement{
私人会议管理起源;
私有最终RedisIndexedSessionRepository会话存储库;
公共CatalInUserSessionManagementGAI(CatalInUserSessionManagementOrigin,RediIndexedSessionRepository sessionRepository){
this.origin=origin;
this.sessionRepository=sessionRepository;
}
公共无效登录(会话){
登录(会话);
}
public void logoutAll(管理器会话管理器){
logoutAll(会话管理器);
}
public void logoutHttpSessions(Manager sessionManager,List sessionid){
for(字符串sessionId:sessionId){
注销会话(sessionManager,sessionId);
}
}
受保护的void logoutSession(管理器管理器,字符串httpSessionId){
试一试{
final方法Method=catalinusersessionmanagement.class.getDeclaredMethod(“logoutSession”,Manager.class,String.class);
方法setAccessible(true);
调用(origin、manager、httpSessionId);
}捕获(例外e){
log.error(“会话管理器代理调用错误”);
}
//增强部分
deleteById(httpSessionId);
}
受保护的无效注销会话(会话){
试一试{
最终方法=CatalInUserSessionManagement.class.getDeclaredMethod(“logoutSession”,Session.class);
方法setAccessible(true);
调用(源、会话);
}捕获(例外e){
log.error(“会话管理器代理调用错误”);
}
}
public void sessionEvent(sessionEvent事件){
来源:sessionEvent(事件);
}
}
那对我有用
@Slf4j
@Component
public class BeanFactoryOrderWrapper implements DestructionAwareBeanPostProcessor {
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
}
@Override
public boolean requiresDestruction(Object bean) {
return true;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("getKeycloakContainerCustomizer")) {
Object wrapRes = this.wrapOrder(bean);
return wrapRes;
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
private Object wrapOrder(Object bean) {
log.info("rewrite keycloak auto config customizer Order for next custom");
final WebServerFactoryCustomizer origin = (WebServerFactoryCustomizer) bean;
return new KeycloakContainerCustomizerWithOrder(origin);
}
}
class KeycloakContainerCustomizerWithOrder implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final WebServerFactoryCustomizer origin;
public KeycloakContainerCustomizerWithOrder(WebServerFactoryCustomizer origin) {
this.origin = origin;
}
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
origin.customize(factory);
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 1;
}
}
@Slf4j
@Configuration
@RequiredArgsConstructor
class ContainerConfig {
private final RedisIndexedSessionRepository sessionRepository;
@Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> getKeycloakContainerCustomizerGai() {
return configurableServletWebServerFactory -> {
if (configurableServletWebServerFactory instanceof TomcatServletWebServerFactory) {
TomcatServletWebServerFactory container = (TomcatServletWebServerFactory) configurableServletWebServerFactory;
container.getContextValves().stream().filter(ele -> ele.getClass() == KeycloakAuthenticatorValve.class).findFirst().map(ele -> (AbstractKeycloakAuthenticatorValve) ele).ifPresent(valve -> {
try {
final Field field = AbstractKeycloakAuthenticatorValve.class.getDeclaredField("userSessionManagement");
field.setAccessible(true);
final CatalinaUserSessionManagement origin = (CatalinaUserSessionManagement) field.get(valve);
field.set(valve, new CatalinaUserSessionManagementGai(origin, sessionRepository));
} catch (Exception e) {
log.error("enhence valve fail");
}
});
}
};
}
}
@Slf4j
class CatalinaUserSessionManagementGai extends CatalinaUserSessionManagement {
private final CatalinaUserSessionManagement origin;
private final RedisIndexedSessionRepository sessionRepository;
public CatalinaUserSessionManagementGai(CatalinaUserSessionManagement origin, RedisIndexedSessionRepository sessionRepository) {
this.origin = origin;
this.sessionRepository = sessionRepository;
}
public void login(Session session) {
origin.login(session);
}
public void logoutAll(Manager sessionManager) {
origin.logoutAll(sessionManager);
}
public void logoutHttpSessions(Manager sessionManager, List<String> sessionIds) {
for (String sessionId : sessionIds) {
logoutSession(sessionManager, sessionId);
}
}
protected void logoutSession(Manager manager, String httpSessionId) {
try {
final Method method = CatalinaUserSessionManagement.class.getDeclaredMethod("logoutSession", Manager.class, String.class);
method.setAccessible(true);
method.invoke(origin,manager,httpSessionId);
} catch (Exception e) {
log.error("session manager proxy invoke error");
}
// enhence part
sessionRepository.deleteById(httpSessionId);
}
protected void logoutSession(Session session) {
try {
final Method method = CatalinaUserSessionManagement.class.getDeclaredMethod("logoutSession", Session.class);
method.setAccessible(true);
method.invoke(origin,session);
} catch (Exception e) {
log.error("session manager proxy invoke error");
}
}
public void sessionEvent(SessionEvent event) {
origin.sessionEvent(event);
}
}