Unable to get cache from cachemanager unless cachemanager.getCacheNames() is invoked - spring

My application uses Spring 4.3.x, EhCache 3.6 and javax Cache 1.1.0.
Here is how I've configured javax CacheManager in my application:
<bean id="jCacheManager" class="org.springframework.cache.jcache.JCacheCacheManager">
<property name="cacheManager" ref="appCacheManagerFactoryBean" />
</bean>
<bean id="appCacheManagerFactoryBean" class="com.example.AppCacheManagerFactoryBean"/>
The AppCacheManagerFactoryBean (which is just a customized version of JCacheManagerFactoryBean) helps me to configure a global persistence directory for my app. Here is how it looks:
public class AppCacheManagerFactoryBean implements FactoryBean<CacheManager>, InitializingBean,
DisposableBean {
#Value("${cache.persistenceDir}")
private String persistenceDir;
private CacheManager cacheManager;
#Override
public void afterPropertiesSet() {
this.cacheManager = buildCacheManager();
}
private CacheManager buildCacheManager()
{
EhcacheCachingProvider cachingProvider = (EhcacheCachingProvider) Caching.getCachingProvider();
DefaultConfiguration defaultConfiguration = new DefaultConfiguration(cachingProvider.getDefaultClassLoader(),
new DefaultPersistenceConfiguration(new File(persistenceDir)));
return cachingProvider.getCacheManager(cachingProvider.getDefaultURI(), defaultConfiguration);
}
#Override
public CacheManager getObject() {
return this.cacheManager;
}
#Override
public Class<?> getObjectType() {
return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class);
}
#Override
public boolean isSingleton() {
return true;
}
#Override
public void destroy() {
this.cacheManager.close();
}
}
Here's how I define caches. I use Ehcache API to create my caches as some of the features my caches need are not available through JCache API.
EhcacheManager ehcacheManager = jCacheCacheManager.getCacheManager().unwrap(EhcacheManager.class);
ehcacheManager.createCache("foo", CacheConfigurationBuilder.newCacheConfigurationBuilder(
String.class, Foo.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(1)
.offheap(1, MemoryUnit.GB)
.disk(5, MemoryUnit.GB)
));
When I try to retrieve a cache from the CacheManager elsewhere in my app, a null pointer exception is thrown.
Caching.getCachingProvider().getCacheManager().getCache("foo");
However, if I retrieve the cache after invoking the getCacheNames() method in CacheManager, the cache is fetched normally.
Caching.getCachingProvider().getCacheManager().getCacheNames();
Caching.getCachingProvider().getCacheManager().getCache("foo");
What have I missed? Please help me.

My first question would be: "Why not using the built-in support of Spring-cache?" It won't need to do that. The JCacheCacheManager will take care of everything.
Then, your problem is that the cache is created directly in Ehcache without passing through the JSR107 layer. Calling getCacheNames() causes a refresh of the cache list in JSR107 to make it work. However, I'm not sure this global behavior is intended. But it is the way it works.
The solution is to create a cache as expected which is through the JSR107 layer. It looks like this
CacheManager cacheManager = jcacheCacheManager.getCacheManager();
cacheManager.createCache("foo",
Eh107Configuration.fromEhcacheCacheConfiguration(
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Foo.class, ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(1)
.offheap(1, MemoryUnit.GB)
.disk(5, MemoryUnit.GB))
.build()));

Related

Clear Redis Cache on Spring Boot Application Startup

On application (spring boot service) startup, need to clear the Redis cache.
Redis is running in a different docker container with own volume mapping. Since it retains the old cache, the application picks the data from Redis cache instead of Database even after the application restarts
Tried #EventListener for ContextRefreshedEvent and it is never getting called.
Tried with #PostConstruct in ApplicationMain class, but it doesn't clear the cache.
Tried using #CacheEvict(allEntries = true), but still no luck
#Component
public class ApplicationStartUp {
#Autowired
private CacheManager cacheManager;
#EventListener()
public void onApplicationEvent(ContextStartedEvent event) {
cacheManager.getCacheNames()
.parallelStream()
.forEach(n -> cacheManager.getCache(n).clear());
}
}
I was successfully able to clear the cache with ApplicationReadyEvent. As the CacheManager bean is available by the time, the cache is getting cleared properly on startup
#Autowired
private CacheManager cacheManager;
#EventListener
public void onApplicationEvent(ApplicationReadyEvent event) {
cacheManager.getCacheNames()
.parallelStream()
.forEach(n -> cacheManager.getCache(n).clear());
}
For the Redis cache manager, if you want to clear the cache on boot, I think you will need to initialize the cache manager with a set of names. See the RedisCacheManagerBuilder docs
For example:
RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory)
.initialCacheNames(Set.of("cacheOne", "cacheTwo"))
.build();
Then you should be able to use #PostConstruct in you cache config class, for example.
#PostConstruct
public void clearCache() {
cacheManager.getCacheNames()
.parallelStream()
.forEach(n -> cacheManager.getCache(n).clear());
}
A simple practice to clear Redis DB at earlier life cycle stage. To config CacheManager #bean with argument RedisConnectionFactory passed in, calling flushDb() to delete all keys of the currently selected database before build up.
#Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
redisConnectionFactory.getConnection().flushDb(); //Delete all keys of the currently selected database
return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory)).cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()).build();
}

Spring Disable #Transactional from Configuration java file

I have a code base which is using for two different applications. some of my spring service classes has annotation #Transactional. On server start I would like to disable #Transactional based on some configuration.
The below is my configuration Class.
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:application.properties")
public class WebAppConfig {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
#Resource
private Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setUrl(url);
dataSource.setUsername(userId);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public PlatformTransactionManager txManager() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
if(appName.equqls("ABC")) {
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_NEVER);
}else {
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
}
CustomDataSourceTransactionManager txM=new CustomDataSourceTransactionManager(def);
txM.setDataSource(dataSource());
return txM;
}
#Bean
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
}
I am trying to ovveried methods in DataSourceTransactionManager to make the functionality. But still it is trying to commit/rollback the transaction at end of transaction. Since there is no database connection available it is throwing exception.
If I keep #Transactional(propagation=Propagation.NEVER), everything works perfectly, but I cannot modify it as another app is using the same code base and it is necessary in that case.
I would like to know if there is a to make transaction fully disable from configuration without modifying #Transactional annotation.
I'm not sure if it would work but you can try to implement custom TransactionInterceptor and override its method that wraps invocation into a transaction, by removing that transactional stuff. Something like this:
public class NoOpTransactionInterceptor extends TransactionInterceptor {
#Override
protected Object invokeWithinTransaction(
Method method,
Class<?> targetClass,
InvocationCallback invocation
) throws Throwable {
// Simply invoke the original unwrapped code
return invocation.proceedWithInvocation();
}
}
Then you declare a conditional bean in one of #Configuration classes
// assuming this property is stored in Spring application properties file
#ConditionalOnProperty(name = "turnOffTransactions", havingValue = "true"))
#Bean
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(
/* default bean would be injected here */
TransactionAttributeSource transactionAttributeSource
) {
TransactionInterceptor interceptor = new NoOpTransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
return interceptor;
}
Probably you gonna need additional configurations, I can't verify that right now

Ehcache local transactions with Spring #Transactional

I'm trying to setup a transactional ehcache, making use of Spring #Cacheable and #Transactional.
My caches work fine with #Cacheable, but as soon as i setup my cache to use a local transaction:
<cache name="currencyCodeMaps" maxElementsInMemory="100" overflowToDisk="false" timeToIdleSeconds="5" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU" transactionalMode="local"/>
When I access the cache i get error:
net.sf.ehcache.transaction.TransactionException: transaction not started
even though the same method is annotated #Transactional.
My Spring transaction manager is:
org.springframework.orm.jpa.JpaTransactionManager
The ehcache documentation says local transactions are controlled explicitly:
Local transactions are not controlled by a Transaction Manager.
Instead there is an explicit API where a reference is obtained to a
TransactionController for the CacheManager using
cacheManager.getTransactionController() and the steps in the
transaction are called explicitly
But this will be hard, as I want to sync my ehcache transactions with DB transactions, and DB transactions are controlled by #Transactional.
Is there a way to get local Ehcache transactions to work with Spring #Transactional?
Yes, there is a way to achieve you goal.
Because you have 2 transactional resources (JTA and Ehcache) and do not use JTA you have to use compound transaction manager likeorg.springframework.data.transaction.ChainedTransactionManager from spring-data project
#Bean
public PlatformTransactionManager transactionManager() {
return new ChainedTransactionManager(ehcacheTransactionManager(), jpaTransactionManager());
}
#Bean
public EhcacheTransactionManager ehcacheTransactionManager() {
return new EhcacheTransactionManager(ehcacheManager().getTransactionController());
}
#Bean
public PlatformTransactionManager jpaTransactionManager() {
return new JpaTransactionManager(entityManagerFactory());
}
You need to specify which transaction manager should be use by default:
#Configuration
public class Configuration implements TransactionManagementConfigurer {
...
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return transactionManager();
}
...
}
EhcacheTransactionManager implementation
import net.sf.ehcache.TransactionController;
import net.sf.ehcache.transaction.local.LocalTransactionContext;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
public class EhcacheTransactionManager extends AbstractPlatformTransactionManager {
private TransactionController transactionController;
public EhcacheTransactionManager(TransactionController transactionController) {
this.transactionController = transactionController;
}
#Override
protected Object doGetTransaction() throws TransactionException {
return new EhcacheTransactionObject(transactionController.getCurrentTransactionContext());
}
#Override
protected void doBegin(Object o, TransactionDefinition transactionDefinition) throws TransactionException {
int timeout = transactionDefinition.getTimeout();
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
transactionController.begin(timeout);
} else {
transactionController.begin();
}
}
#Override
protected void doCommit(DefaultTransactionStatus defaultTransactionStatus) throws TransactionException {
transactionController.commit();
}
#Override
protected void doRollback(DefaultTransactionStatus defaultTransactionStatus) throws TransactionException {
transactionController.rollback();
}
public class EhcacheTransactionObject {
private LocalTransactionContext currentTransactionContext;
public EhcacheTransactionObject(LocalTransactionContext currentTransactionContext) {
this.currentTransactionContext = currentTransactionContext;
}
}
}
source code and test case can be found here
This solution has a significant drawback transaction coordinator of ehcache does not support suspend/resume operations so inner transactions (PROPAGATION_REQUIRES_NEW) are not possible. That is why I had to find another one.
Another option is not to use local ehcache transactions at all and use org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager#setTransactionAware which decorates caches to postpone operations until the transaction end. But it has following drawbacks:
Evicted keys stay accessible inside transaction until transaction commit
putIfAbsent operation is not postponed
It was a problem for me, so I implemented this functionality in different way. Check 'me.qnox.springframework.cache.tx.TxAwareCacheManagerProxy', there problems described above was solved, in the same repository
You do not want local transactions, you want XA transactions, which are supported by Ehcache.
Have a look at the documentation for Ehcache 2.10.x or Ehcache 2.8.x.

Spring is ignoring #Transactional annotations in Apache Shiro Realm class

I am using Spring for IOC and transaction management, and am planning to use Apache Shiro as the security library.
Whenever I want to check a user's permissions, I call subject.isPermitted("right"), whereupon Shiro checks for the permission using a datastore. Within these calls, a database connection is established, and I have annotated the method with #Transactional. However, I always receive an error that there is no Hibernate session bound to the thread whenever I execute the permission check.
The method is in the Realm class. I defined a custom Shiro Realm class:
#Component
public class MainRealm extends AuthorizingRealm {
#Autowired
protected SysUserDao userDao;
#Transactional
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
...
final SysUser user = this.userDao.findByUsername(un);
...
return authInfo;
}
#Transactional
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
...
permissions = this.userDao.getAccessRights(un);
...
return authInfo;
}
}
Apache Shiro uses a Servlet Filter, so I have the following defined in web.xml:
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
I am using programmatic configuration for Spring. Here is my App Config class:
#Configuration //Replaces Spring XML configuration
#ComponentScan(basePackages = "com.mycompany")
#EnableTransactionManagement //Enables declarative Transaction annotations
public class SpringAppConfig {
#Bean
public DataSource sqlServerDataSource() throws Exception {...}
#Bean
#Autowired
public PlatformTransactionManager transactionManager(SessionFactory sessionFactory) {...}
#Bean
public AnnotationSessionFactoryBean getSessionFactory() throws Exception {...}
#Bean
public static PersistenceExceptionTranslationPostProcessor exceptionTranslation() {...}
#Bean
#Autowired
public DefaultWebSecurityManager securityManager(MainRealm mainRealm) {
final HashedCredentialsMatcher hcm = new HashedCredentialsMatcher(shiroHash);
hcm.setHashIterations(shiroIter);
hcm.setStoredCredentialsHexEncoded(shiroHexEncoded);
mainRealm.setCredentialsMatcher(hcm);
final DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
sm.setRealm(mainRealm);
return sm;
}
#Bean
#Autowired
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
final ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
filter.setSecurityManager(securityManager);
return filter;
}
/**
* This method needs to be static due to issues defined here:<br>
* https://issues.apache.org/jira/browse/SHIRO-222
*/
#Bean
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
LifecycleBeanPostProcessor lbpp = new LifecycleBeanPostProcessor();
return lbpp;
}
#Bean
#DependsOn("lifecycleBeanPostProcessor")
public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
#Bean
#Autowired
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager secMan) {
AuthorizationAttributeSourceAdvisor advBean = new AuthorizationAttributeSourceAdvisor();
advBean.setSecurityManager(secMan);
return advBean;
}
}
To summarize the issue, I believe my MainRealm class is being wired properly (it has an #Autowired dependency to a DAO object and I verified that it is not null) with the exception of the #Transactional annotation. Due to this, I cannot directly call user.isPermitted("") as it prompts an error: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here.
Would like to ask for help in checking whether I missed anything in my Spring configuration.
In the meantime I have hacked over this issue by calling the user.isPermitted("") function within a method in my Service class that's correctly bound by a #Transactional.
EDIT When I checked the logs for Spring initialization I can see this:
Bean 'mainRealm' of type [class com.x.security.MainRealm] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
According to this SO answer it means MainRealm isn't being postprocessed by the transaction manager therefore any #Transactional annotations get ignored. If so, how can I correct this?
EDIT 2 According to this SO question: "In other words, if I write my own BeanPostProcessor, and that class directly references other beans in the context, then those referenced beans will not be eligible for auto-proxying, and a message is logged to that effect." I just checked ShiroFilterFactoryBean and it is in fact a BeanPostProcessor. And the problem is it requires a SecurityManager instance that in turn requires a MainRealm instance. So both beans are autowired and are thus ineligible for proxying. I feel like I'm closer to the solution but I still can't resolve it.
The root cause of the issue is in fact due to the following:
All BeanPostProcessors and their directly referenced beans will be instantiated on startup... Since AOP auto-proxying is implemented as a BeanPostProcessor itself, no BeanPostProcessors or directly referenced beans are eligible for auto-proxying (and thus will not have aspects 'woven' into them.
Referenced SO question is here.
I have resolved this issue by decoupling the Realm bean creation from the SecurityManager bean creation.
The relevant change is from the following code:
#Bean
#Autowired
public DefaultWebSecurityManager securityManager(MainRealm mainRealm) {
final HashedCredentialsMatcher hcm = new HashedCredentialsMatcher(shiroHash);
hcm.setHashIterations(shiroIter);
hcm.setStoredCredentialsHexEncoded(shiroHexEncoded);
mainRealm.setCredentialsMatcher(hcm);
final DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
sm.setRealm(mainRealm);
return sm;
}
to the following code:
#Bean
public DefaultWebSecurityManager securityManager() {
final DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
//sm.setRealm(mainRealm); -> set this AFTER Spring initialization so no dependencies
return sm;
}
Then I use a ServletContextListener which listens to when the Spring context initialization completes and I have the both the MainRealm and SecurityManager beans. Then I just plug one bean inside another.
#Override
public void contextInitialized(ServletContextEvent sce) {
try {
//Initialize realms
final MainRealm mainRealm = (MainRealm)ctx.getBean("mainRealm");
final DefaultWebSecurityManager sm = (DefaultWebSecurityManager)ctx.getBean("securityManager");
sm.setRealm(mainRealm);
} catch (Exception e) {
System.out.println("Error loading: " + e.getMessage());
throw new Error("Critical system error", e);
}
}
#Transactional annotation can be used before :
An interface def
An interface method
A class def
A PUBLIC method of a class
As explained in the documentation
The fact that your method is protected must be the reason of your problem, and your service method was maybe declared as public which would explained why it worked in that case

Dynamic Spring Security using SQL Query

Hello I want to make an intercept url pattern and access dynamically by using sql query in spring security.
Generally we use this type of notation in XML and I want to take these values (/add-role and ROLE_ADMIN) from database.
<intercept-url pattern="/add-role*" access="ROLE_ADMIN" />
Is it possible to do this dynamically?
Disclaimer
As the Spring Security FAQ mentions, the first thing you should do is ask should I really do this? Security is complicated and the configuration should be tested extensively. Allowing the configuration to change dynamically only further complicates things making the application that much more vulnerable. If you really want to do this, the FAQ outlines a basic method to accomplish this. I have expanded upon the FAQ's answer below.
Implement Custom FilterInvocationSecurityMetadataSource
To obtain the security URL mappings dynamically you can implement your own FilterInvocationSecurityMetadataSource. An example implementation is given below.
NOTE: Keep in mind that getAttributes will be invoked for every request that Spring Security intercepts so you will most likely want some sort of caching.
public class JdbcFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
FilterInvocation fi = (FilterInvocation) object;
String url = fi.getRequestUrl();
HttpServletRequest request = fi.getHttpRequest();
// Instead of hard coding the roles lookup the roles from the database using the url and/or HttpServletRequest
// Do not forget to add caching of the lookup
String[] roles = new String[] { "ROLE_ADMIN", "ROLE_USER" };
return SecurityConfig.createList(roles);
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
Create a BeanPostProcessor
You cannot use the namespace to wire it up, so taking another tip from the FAQ you can use a BeanPostProcessor which might look like:
public class FilterInvocationSecurityMetadataSourcePostProcessor implements BeanPostProcessor, InitializingBean {
private FilterInvocationSecurityMetadataSource securityMetadataSource;
public Object postProcessAfterInitialization(Object bean, String name) {
if (bean instanceof FilterSecurityInterceptor) {
((FilterSecurityInterceptor)bean).setSecurityMetadataSource(securityMetadataSource);
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String name) {
return bean;
}
public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(securityMetadataSource,"securityMetadataSource cannot be null");
}
}
XML Configuration
Then, assuming both of the above beans are in the package sample, you would add the following configuration
<bean class="sample.FilterInvocationSecurityMetadataSourcePostProcessor">
<property name="securityMetadataSource">
<bean class="sample.JdbcFilterInvocationSecurityMetadataSource"/>
</property>
</bean>
Possible Problems
If you end up getting a ClassCastException, you are likely running into SEC-1957 which was fixed in Spring Security 3.1.1+ Try updating to the latest version to resolve this.
You cant really get those values from the databse, but you can write a custom code called DecisionManager that evaluates if the resource is allowed to execute. With that code you can even read data from the database.
<bean id="MyDecisionManagerBean" class="org.springframework.security.vote.UnanimousBased">
<property name="decisionVoters">
<list>
<!-- <bean class="org.springframework.security.vote.RoleVoter"/> -->
<bean class="org.springframework.security.vote.RoleHierarchyVoter" >
<constructor-arg>
<bean class="org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyImpl" factory-bean="roleHierarchyImplFactory" factory-method="createRoleHierarchyImpl"/>
</constructor-arg>
</bean>
<bean class="com.mycompany.RoleDenyVoter"/>
<bean class="com.mycompany.RoleAllowVoter"/>
</list>
</property>
</bean>
Your class will be like this :
public class RoleDenyVoter implements AccessDecisionVoter {
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
//read from the DB and decide if access is granted
the process is documented here :
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/authz-arch.html#authz-voting-based
I have created this entry for update purpose
Implement Custom FilterInvocationSecurityMetadataSource
This class only obtains the URL in every request and lookup their permissions from the database or third party applications
public class CommonFilterSecurityMetaDataSource implements FilterInvocationSecurityMetadataSource {
private final Map<String, UrlRequestModel> permissions;
#Autowired
private UrlRequestDao urlRequestDao;
public CommonFilterSecurityMetaDataSource() {
permissions = new Hashtable<>();
}
public List<ConfigAttribute> getAttributes(Object object) {
final FilterInvocation fi = (FilterInvocation) object;
final String url = fi.getRequestUrl();
final String httpMethod = fi.getRequest().getMethod();
final String key = String.format("%s %s", httpMethod, url);
final UrlRequestModel urlRequestModel;
List<ConfigAttribute> attributes = null;
// Lookup your database (or other source) using this information and populate the
// list of attributes
if(permissions.containsKey(key)) {
urlRequestModel= permissions.get(key);
} else {
urlRequestModel= catRequestDao.findByUrl(url);
if(catRequestMapModel != null) {
permissions.put(key, urlRequestModel);
}
}
if (catRequestMapModel != null) {
List<RoleModel> roles = ulrRequestModel.getRoleList();
if(!roles.isEmpty()) {
attributes = new ArrayList<>(roles.size());
for (RoleModel role : roles) {
attributes.add(new SecurityConfig(role.getDescription()));
}
}
}
return attributes;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
Java configuration
For java configuration only add this to your class wich extends from WebSecurityConfigurerAdapter
#Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();
http.authorizeRequests().
antMatchers( "/javax.faces.resource/**").permitAll().
and()
.exceptionHandling().accessDeniedPage("/accessDenied.jsf").
and().formLogin().
loginPage("/login.jsf").
loginProcessingUrl("/loginAction").
usernameParameter("app_username").
passwordParameter("app_password").
defaultSuccessUrl("/secure/index.jsf").
and().logout().
logoutUrl("/appLogout").
logoutSuccessUrl("/login.jsf").logoutRequestMatcher(new AntPathRequestMatcher("/appLogout")).
and().addFilterAfter(filterSecurityInterceptor(), FilterSecurityInterceptor.class);
http.csrf().disable();
}
#Bean
public FilterSecurityInterceptor filterSecurityInterceptor() throws Exception {
FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
filterSecurityInterceptor.setSecurityMetadataSource(securityMetadataSource());
filterSecurityInterceptor.setAuthenticationManager(authenticationManager());
filterSecurityInterceptor.setAccessDecisionManager(accessDecisionManager());
filterSecurityInterceptor.setPublishAuthorizationSuccess(true);
return filterSecurityInterceptor;
}
#Bean
public AccessDecisionManager accessDecisionManager() {
AuthenticatedVoter authenticatedVoter = new AuthenticatedVoter();
RoleVoter roleVoter = new RoleVoter();
List<AccessDecisionVoter<? extends Object>> voters = new ArrayList<>();
voters.add(authenticatedVoter);
voters.add(roleVoter);
return new AffirmativeBased(voters);
}
#Bean
public FilterInvocationSecurityMetadataSource securityMetadataSource() {
return new CommonFilterSecurityMetaDataSource();
}
I tested it using Spring security 5.0.8

Resources