Spring service with cached methods is initialized without a cache when preparing automatically in the Siro area

After spending 2 days on this issue, I really can’t make any more changes myself. I am working on a standard Spring web application for dependency injection and the like. I also use Spring to cache several expensive methods, which I use a lot.

After I introduced Apache Shiro for a level of security, I had a strange problem when methods @Cacheablein a specific service were no longer cached. At this point, I was able to remove the problem to my kernel, but there is still a lot of code for you to see - sorry for that ...

First I configure all the relevant packages (all the classes shown below are in one of them).

@Configuration
@ComponentScan(basePackages = {
        "my.package.config",
        "my.package.controllers",
        "my.package.security",
        "my.package.services",
})
public class AppConfiguration {

}

.

@Configuration
@EnableCaching
public class CacheConfiguration {
    @Bean(name = "cacheManager")
    public SimpleCacheManager cacheManager() {
        SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
        simpleCacheManager.setCaches(Arrays.asList(
                new ConcurrentMapCache("datetime")
        ));
        return simpleCacheManager;
    }
}

, . Impl , .

public interface DateService {
    @Cacheable("datetime")
    LocalDateTime getCurrent();
}

.

@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);
    }
}

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;
    }
}

, , .

@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);
    }
}

. , . , , , , . , .

@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);
    }
}

, . , , MyRealm , , SimpleCacheManager (cacheInterceptor ..). , @Autowired - , . , , , .

, MyRealm DateService ( MyRealm @DependsOn("dateServiceImpl") , ), (.. , ).

MyRealm, , . @DependsOn("cacheManager"), , beans, , , . - - , ( , ) . , , ...

, . , , , Spring .

+4
2

- , , , .

Spring org.springframework.cache.interceptor.CacheInterceptor, org.aopalliance.aop.Advice, org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor, org.springframework.aop.Advisor.

org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor Advisor, DateService DefaultSecurityManager MyRealm.

, Advisor - - . , , Advisor, DateService - , , , Shiro. DateService , , .

@Bean
@Autowired
public Advisor testAdvisor(DateService dateService) {
    return new StaticMethodMatcherPointcutAdvisor() {
        @Override
        public boolean matches(Method method, Class<?> targetClass) {
            return false;
        }
    };
}

, . @Order(Ordered.LOWEST_PRECEDENCE) @Order(Ordered.HIGHEST_PRECEDENCE) , Advisor , , . .

DateServiceImpl :

@Autowired
BeanFactoryCacheOperationSourceAdvisor waitForCachingAspect;

, , . , , , Shiro --> DateService --> Cache, Shiro Advisor .

, , , , , " , Advisors Spring" - .

+3

Spring 4, @Lazy , , (. Spring 4 JavaDoc ).

.

@Component
public class MyRealm extends AuthenticatingRealm {
    private static final String REALM_NAME = "MyRealm";
    @Autowired
    @Lazy
    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);
    }
}
+1

Source: https://habr.com/ru/post/1524947/


All Articles