chk*_*chk 5 spring caching autowired shiro
在这个问题上花了 2 天后,我真的无法自己取得更多进展。我正在使用 Spring 开发用于依赖注入等的标准 Web 应用程序。我还使用 Spring 来缓存我经常使用的几种昂贵的方法。
在我为安全层引入 Apache Shiro 之后,我遇到了一个奇怪的问题,@Cacheable某个服务中的方法不再被缓存。到目前为止,我已经能够将问题剥离到其核心,但仍有很多代码需要您查看 - 抱歉……
首先,我配置所有相关的包(下面显示的所有类都在其中一个)。
@Configuration
@ComponentScan(basePackages = {
"my.package.config",
"my.package.controllers",
"my.package.security",
"my.package.services",
})
public class AppConfiguration {
}
Run Code Online (Sandbox Code Playgroud)
这是缓存的配置文件。
@Configuration
@EnableCaching
public class CacheConfiguration {
@Bean(name = "cacheManager")
public SimpleCacheManager cacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
simpleCacheManager.setCaches(Arrays.asList(
new ConcurrentMapCache("datetime")
));
return simpleCacheManager;
}
}
Run Code Online (Sandbox Code Playgroud)
对于我的最小示例,我使用了一个非常简单的服务,它只返回当前时间戳。这Impl门课和你想象的一样简单。
public interface DateService {
@Cacheable("datetime")
LocalDateTime getCurrent();
}
Run Code Online (Sandbox Code Playgroud)
我将此服务注入控制器。
@Controller
@RequestMapping("/v1/date")
public class DateController {
@Autowired
DateService dateService;
@RequestMapping(value = "/current", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<String> getCurrent() {
Subject s = SecurityUtils.getSubject();
s.login(new MyToken());
return new ResponseEntity<>(dateService.getCurrent().toString(), HttpStatus.OK);
}
}
Run Code Online (Sandbox Code Playgroud)
该应用程序是通过 Jetty 设置和启动的,到目前为止一切都按预期工作。<api-url>/v1/date/current第一次调用时返回当前时间戳,但之后总是收到缓存的结果。
现在,我用另一个配置文件介绍 Shiro。
@Configuration
public class ShiroSecurityConfiguration {
@Bean
@Autowired
public DefaultSecurityManager securityManager(MyRealm realm) {
List<Realm> realms = new ArrayList<>();
// MyToken is a static stub for this example
realm.setAuthenticationTokenClass(MyToken.class);
realms.add(realm);
DefaultSecurityManager manager = new DefaultSecurityManager(realms);
SecurityUtils.setSecurityManager(manager);
return manager;
}
// other Shiro related beans that are - at least to me - irrelevant here
// EDIT 2: I figured out that the described problem only occurs with this bean
// (transitively depending on DateService) in the application
// the bean is required for annotations such as @RequiresAuthentication to work
@Bean
@Autowired
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
Run Code Online (Sandbox Code Playgroud)
最后,这也取决于我的服务的领域来了。
@Component
public class MyRealm extends AuthenticatingRealm {
private static final String REALM_NAME = "MyRealm";
@Autowired
private DateService dateService;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("User authenticated at "+dateService.getCurrent());
return new SimpleAuthenticationInfo("",token.getCredentials(),REALM_NAME);
}
}
Run Code Online (Sandbox Code Playgroud)
这样,我的整个应用程序中的缓存就被破坏了。没有错误消息,它只是不再使用缓存。我能够实施一种解决方法,但我现在正在寻求更好的解决方案,也许还有一些建议以更好地理解我的问题的本质。所以,这里是解决方法。
@Component
public class MyRealm extends AuthenticatingRealm {
private static final String REALM_NAME = "MyRealm";
private DateService dateService;
@Autowired
private ApplicationContext applicationContext;
private void wireManually() {
if (dateService == null) {
dateService = applicationContext.getBean(DateService.class);
}
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
wireManually();
System.out.println("User authenticated at "+dateService.getCurrent());
return new SimpleAuthenticationInfo("",token.getCredentials(),REALM_NAME);
}
}
Run Code Online (Sandbox Code Playgroud)
现在它恢复工作了,我能够调试原因。Shiro 因此MyRealm很早就初始化了,甚至在我SimpleCacheManager和所有相关内容(cacheInterceptor 等)的整个缓存加载之前。因此,在使用@Autowired. 使用上面显示的解决方法,在一切设置正确并且服务第一个请求之前不会注入服务,然后就没有问题了。
简单地说,只要我MyRealm依赖DateService(注释MyRealmwith的最后一个版本@DependsOn("dateServiceImpl")就足以破坏应用程序),它就会过早地初始化(即在设置缓存之前)。
所以我需要推迟 的初始化MyRealm,但我不知道该怎么做。我试过了@DependsOn("cacheManager"),但这无济于事,因为缓存所需的其他 bean 稍后加载。或者 - 从另一个角度来看是相同的 - 我可以确保整个缓存基础设施(我还不够专家来详细描述它)提前初始化。不幸的是,我也不知道该怎么做......
提前感谢所有做到这一点的人。期待任何输入,无论是以更好的方式修复代码的想法,还是解释为什么 Spring 不能自己解决这个问题。
我终于弄清楚了问题所在,并且至少可以更详细地解释其原因,尽管我提出的解决方案仍然有点hacky。
在 Spring 中启用缓存方面引入了 a org.springframework.cache.interceptor.CacheInterceptor,它本质上是由实现 的org.aopalliance.aop.Advicea 使用的。org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisororg.springframework.aop.Advisor
org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor为 Shiro 引入的 I 是另一个传递Advisor依赖于DateServiceviaDefaultSecurityManager和 的I MyRealm。
所以我有两个Advisor用于两个不同方面的s - 缓存和安全性 - 其中安全性的部分首先被初始化。事实上,每当我引入任何Advisor依赖项时DateService(即使它只是一个虚拟实现,如下例所示),缓存就不再起作用,原因与添加 Shiro 时缓存被破坏的原因相同。这会导致DateService在缓存方面准备好之前加载,因此无法应用。
@Bean
@Autowired
public Advisor testAdvisor(DateService dateService) {
return new StaticMethodMatcherPointcutAdvisor() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
Run Code Online (Sandbox Code Playgroud)
因此,唯一正确的解决方法是更改方面初始化的顺序。我知道多个 s 适用于特定连接点的情况的@Order(Ordered.LOWEST_PRECEDENCE)相应注释,但对我来说不是这种情况,所以这没有帮助。由于其他原因,初始化的顺序也很重要。@Order(Ordered.HIGHEST_PRECEDENCE)Advisor
添加以下代码DateServiceImpl实际上可以解决问题:
@Autowired
BeanFactoryCacheOperationSourceAdvisor waitForCachingAspect;
Run Code Online (Sandbox Code Playgroud)
这样,服务始终会在初始化之前等待缓存,即使在实现中的任何地方都没有使用此依赖项。所以现在一切都正常工作,因为依赖关系树现在包含Shiro --> DateService --> Cache这使得 Shiro Advisor 等待足够长的时间。
它仍然没有我希望的那么好和干净,但是尽管如此,我认为这个解释有助于理解问题的核心,并且“如何更改在 Spring 中初始化 Advisor 的顺序”是一个单独的部分我在这里发布的问题。
| 归档时间: |
|
| 查看次数: |
4573 次 |
| 最近记录: |