4

I've experience in Spring MVC, but first time using Cache. These are steps that I've done yet.

Step : 1

// In spring config

@Bean
public CacheManager cacheManager() {
    return new ConcurrentMapCacheManager("user");
}

// Cached Object

public class CachedUser {   
    private String username;
    private String token;
    // Public getter-setter
}

// AuthServiceImp

@Service
public class AuthServiceImp implements AuthService {

  @Override
  @Cacheable(value="user", key="#token")
  @Transactional
  public CachedUser loadUserDetailsFromDb(String username, String token) {
     // codes here
  }

  @Override
  @CacheEvict(value="user", key="#token")
  @Transactional
  public void removeUser(String username, String token) {
     // codes here
  }
}

// My Filter

public class AuthenticationTokenFilter extends UsernamePasswordAuthenticationFilter {
  AuthService authService = WebApplicationContextUtils
            .getRequiredWebApplicationContext(this.getServletContext())
            .getBean(AuthService.class);
  CachedUser user = this.authService.loadUserDetailsFromDb(username, authToken);
}

// Controller

@RestController
public class AuthenticationController {
  @Autowired
  private AuthService authService;
  @GetMapping("logout2")
  public ResponseModel logout(@RequestAttribute("username") String username,
        HttpServletRequest request) {
    String token = request.getHeader(tokenHeader);
    authService.removeUser(username, token);
    return new ResponseModel(200,"Success",null);
  }
}

Whenever calling loadUserDetailsFromDb from AuthenticationTokenFilter it returns cached object (except in first call obviously). That means @Cacheable(value="user", key="#token") is working fine.

But even after I logged out and called authService.removeUser(), calling loadUserDetailsFromDb() fetches the cached object. That means @CacheEvict(value="user", key="#token") is not working.

Step: 2

Referred this and moved removeUser() to another service ( say CacheServiceImp implements CacheService ), yet same problem.

Step: 3

Reffered this and , by my understanding, moved @Cache* annotation to interface AuthService, got following error.

java.lang.IllegalArgumentException: Null key returned for cache operation (maybe you are using named params on classes without debug info?)

Note : Is the problem of not evicting, because I'm calling @Cacheable and @CacheEvict methods from different classes. That is from AuthenticationTokenFilter and AuthenticationController

Community
  • 1
  • 1
Ramanujan R
  • 1,601
  • 2
  • 23
  • 43
  • I think your implementation looks correctly. Perhaps you have forgotten @EnableCaching annotation in your configuration class? – pDer666 Dec 04 '16 at 07:35
  • `@EnableCaching` is there in my configuration class. And `@Cacheable` is working fine. – Ramanujan R Dec 04 '16 at 08:46
  • Further searching brought me this "**JDK ConcurrentMap which is enough for simple use cases but does not support the persistence or eviction policy**" from this post [link](http://websystique.com/spring/spring-4-cache-tutorial-with-ehcache/). Is this have something to do with my problem? – Ramanujan R Dec 07 '16 at 05:35

1 Answers1

1

After playing with my code, head and internet, at last, I got this solved. It's a mistake in my Spring (Security) configuration, which I failed to post with the question.

Mistake 1 :

In SecurityInitializer class

public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {    
    public SecurityInitializer() {
        super(WebSecurityConfiguration.class);
    }

}

As the project includes Spring MVC configuration, the constructor must not be implemented. So removed the constructor. This class, then, simply registers the springSecurityFilterChain Filter for every URL.

Mistake 2: ( THE REAL CAUSE OF ABOVE PROBLEM )

I've added my AuthenticationTokenFilter in two ways:

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    // other overrides
    @Override
    protected Filter[] getServletFilters() {
        return new Filter[]{ new AuthenticationTokenFilter() };
    }
}

and

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    // Other config
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
       //Other config
       httpSecurity.addFilterBefore(authTokenFilter,
                    UsernamePasswordAuthenticationFilter.class);
    }
}

This made the filter to be called twice, one inside Spring context and the other as usual Servlet filter

So removed configuration inside WebAppInitializer

Additional change

Removed @ComponentScan from WebSecurityConfiguration because it's already in SpringMvcConfig. This requires both configurations to be loaded in same context. Done by following code.

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { SpringMvcConfig.class, WebSecurityConfiguration.class };
    }
    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
    // Removed filter registering from here (Mistake 2)
}

At last, everything working FINE :)

Ramanujan R
  • 1,601
  • 2
  • 23
  • 43