diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java b/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java index 0d0c42d3..9e7c6387 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java @@ -4,16 +4,19 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.SessionManager; 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.config.ShiroFilterConfiguration; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.apache.shiro.web.mgt.WebSecurityManager; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Role; import jakarta.servlet.Filter; import xiaozhi.modules.security.oauth2.Oauth2Filter; @@ -39,7 +42,7 @@ public class ShiroConfig { } @Bean("securityManager") - public SecurityManager securityManager(Oauth2Realm oAuth2Realm, SessionManager sessionManager) { + public WebSecurityManager securityManager(Oauth2Realm oAuth2Realm, SessionManager sessionManager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(oAuth2Realm); securityManager.setSessionManager(sessionManager); @@ -48,7 +51,8 @@ public class ShiroConfig { } @Bean("shiroFilter") - public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager, SysParamsService sysParamsService) { + public static ShiroFilterFactoryBean shirFilter(@Lazy WebSecurityManager securityManager, + @Lazy SysParamsService sysParamsService) { ShiroFilterConfiguration config = new ShiroFilterConfiguration(); config.setFilterOncePerRequest(true); @@ -101,12 +105,14 @@ public class ShiroConfig { } @Bean("lifecycleBeanPostProcessor") - public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { + public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean - public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public static AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor( + @Lazy WebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; diff --git a/main/manager-api/src/test/java/xiaozhi/modules/security/config/ShiroConfigTest.java b/main/manager-api/src/test/java/xiaozhi/modules/security/config/ShiroConfigTest.java new file mode 100644 index 00000000..9b10be11 --- /dev/null +++ b/main/manager-api/src/test/java/xiaozhi/modules/security/config/ShiroConfigTest.java @@ -0,0 +1,60 @@ +package xiaozhi.modules.security.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.apache.shiro.session.mgt.SessionManager; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.mgt.WebSecurityManager; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Role; + +import xiaozhi.modules.security.oauth2.Oauth2Realm; +import xiaozhi.modules.sys.service.SysParamsService; + +class ShiroConfigTest { + + @Test + void beanPostProcessorFactoriesDoNotInstantiateShiroConfigOrBusinessDependenciesEarly() throws Exception { + Method lifecycleFactory = ShiroConfig.class.getDeclaredMethod("lifecycleBeanPostProcessor"); + Method filterFactory = ShiroConfig.class.getDeclaredMethod( + "shirFilter", WebSecurityManager.class, SysParamsService.class); + + assertTrue(Modifier.isStatic(lifecycleFactory.getModifiers())); + assertTrue(BeanPostProcessor.class.isAssignableFrom(ShiroFilterFactoryBean.class)); + assertTrue(Modifier.isStatic(filterFactory.getModifiers())); + Lazy securityManagerLazy = filterFactory.getParameters()[0].getAnnotation(Lazy.class); + Lazy sysParamsServiceLazy = filterFactory.getParameters()[1].getAnnotation(Lazy.class); + assertNotNull(securityManagerLazy); + assertNotNull(sysParamsServiceLazy); + assertTrue(securityManagerLazy.value()); + assertTrue(sysParamsServiceLazy.value()); + } + + @Test + void authorizationAdvisorIsInfrastructureAndDefersItsSecurityManager() throws Exception { + Method advisorFactory = ShiroConfig.class.getDeclaredMethod( + "authorizationAttributeSourceAdvisor", WebSecurityManager.class); + + assertTrue(Modifier.isStatic(advisorFactory.getModifiers())); + Lazy securityManagerLazy = advisorFactory.getParameters()[0].getAnnotation(Lazy.class); + assertNotNull(securityManagerLazy); + assertTrue(securityManagerLazy.value()); + assertEquals(BeanDefinition.ROLE_INFRASTRUCTURE, advisorFactory.getAnnotation(Role.class).value()); + } + + @Test + void securityManagerRetainsTheWebSecurityContractRequiredByShiroFilter() throws Exception { + Method securityManagerFactory = ShiroConfig.class.getDeclaredMethod( + "securityManager", Oauth2Realm.class, SessionManager.class); + + assertEquals(WebSecurityManager.class, securityManagerFactory.getReturnType()); + } +}