How to configure Jcache with Ecache as Provider in Spring application-context.xml? - spring

Spring documentation provides below information.
<bean id="cacheManager"
class="org.springframework.cache.jcache.JCacheCacheManager"
p:cache-manager-ref="jCacheManager"/>
<!-- JSR-107 cache manager setup -->
<bean id="jCacheManager" .../>
I want to know exactly how to configure this jcacheManager bean (with EhCache as provider) in spring application context xml.
I have already configured dependency, as below, in pom.xml which is fine.
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>jcache</artifactId>
<version>1.0.1</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>

It really depends how you want to configure it. If you're using Spring Boot 1.3, it will be automatically created for you. Maybe you could have a look to the source of JCacheCacheConfiguration?
You can retrieve the default javax.cache.CacheManager via Caching.getCachingProvider().getCacheManager()

It's not convenient for us to integrate Ehache3.x with Spring4.x now. Spring boot does it, and it rewrites some codes:
<bean id="cacheManager"
class="org.springframework.cache.jcache.JCacheCacheManager"
in Spring boot, it's:
#Bean
public JCacheCacheManager cacheManager(CacheManager jCacheCacheManager) {
return new JCacheCacheManager(jCacheCacheManager);
}
and it needs a javax.cache.CacheManager instance,
<!-- JSR-107 cache manager setup -->
<bean id="jCacheManager" .../>
Ehcache have no in-depth introduction for us.
Spring boot does like:
#Bean
#ConditionalOnMissingBean
public CacheManager jCacheCacheManager() throws IOException {
CacheManager jCacheCacheManager = createCacheManager();
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!CollectionUtils.isEmpty(cacheNames)) {
for (String cacheName : cacheNames) {
jCacheCacheManager.createCache(cacheName, getDefaultCacheConfiguration());
}
}
customize(jCacheCacheManager);
return jCacheCacheManager;
}
It's a normal operation to create javax.cache.CacheManager just follows the Ehcache document.

Related

Is using hibernate type property names when using spring boot a good approach

In my current codebase, I have a RestController, and I am using hibernate with it. As of now, I am using the same hibernate configuration which one would usually use
<?xml version="1.0" encoding="UTF-8" ?>
<hibernate-configuration xmlns="http://www.hibernate.org/xsd/orm/cfg">
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
Also, in my pom.xml, I have added:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
But to get this working I had to add the attribute exclude like this :
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
Otherwise, it was throwing some error. Another issue I noticed was even though
hbm2ddl.auto is set to true, which would have created the tables in a simple hibernate application. But it's not happening now. It throws an error if the table is not there but seems to be working fine if it's there.
I changed the MySQL dialect in the hibernate.cfg.xml to :
org.hibernate.dialect.MySQL8Dialect
And now it's automatically creating the table. That's weird when I was using the same hibernate code without spring boot the earlier Dialect was working
This is the main code that starts the application:
#SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class SpringServerMain {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringServerMain.class, args);
}
}

How to configure Springboot's 2.x actuator in Spring Application(not a SpringBoot Application)? [duplicate]

I've been working on a Spring/Spring MVC application and I'm looking to add performance metrics. I've come across Spring Boot Actuator and it looks like a great solution. However my application is not a Spring Boot application. My application is running in a traditional container Tomcat 8.
I added the following dependencies
// Spring Actuator
compile "org.springframework.boot:spring-boot-starter-actuator:1.2.3.RELEASE"
I created the following config class.
#EnableConfigurationProperties
#Configuration
#EnableAutoConfiguration
#Profile(value = {"dev", "test"})
#Import(EndpointAutoConfiguration.class)
public class SpringActuatorConfig {
}
I even went as far as adding #EnableConfigurationProperties on every configuration class as suggested on another post on StackOverflow. However that didn't do anything. The endpoints are still not being created and return 404s.
First let's clarify that you cannot use Spring Boot Actuator without using Spring Boot.
I was wrong about not being able to it without Spring Boot. See #stefaan-neyts
answer for an example of how to do it.
I created a sample project to show how you could convert a basic SpringMVC application using a minimal amount of Spring Boot auto-configuration.
Original source: http://www.mkyong.com/spring-mvc/gradle-spring-mvc-web-project-example
Converted source: https://github.com/Pytry/minimal-boot-actuator
I could have completely removed the dispatcher-servlet.xml and the web.xml files, but I kept them to show how to perform as minimal a change as possible and to simplify converting more complex projects.
Here is a list of steps I took to convert.
Conversion Process
Add a Java Configuration file annotated with #SpringBootApplication
Add the Application configuration file as a bean to the traditional xml configuration ( added it just after the context scan).
Move view resolvers into Application java configuration.
Alternatively, add the prefix and suffix to application.properties.
You can then inject them with #Value in your application, or delete it entirely and just use the provided spring boot view resolver.
I went with the former.
Removed Default context listener from the spring context xml.
This is important!
Since spring boot will provide one you will get an "Error listener Start" exception if you do not.
Add the spring boot plugin to your build script dependencies (I was using gradle)
Add a mainClassName property to the build file, and set to an empty String (indicates not to create an executable).
Modify dependencies for spring boot actuator
You can use actuator without spring boot.
Add this to pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
And then in your config class
#Configuration
#EnableWebMvc
#Import({
EndpointAutoConfiguration.class , PublicMetricsAutoConfiguration.class , HealthIndicatorAutoConfiguration.class
})
public class MyActuatorConfig {
#Bean
#Autowired
public EndpointHandlerMapping endpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
return new EndpointHandlerMapping(endpoints);
}
#Bean
#Autowired
public EndpointMvcAdapter metricsEndPoint(MetricsEndpoint delegate) {
return new EndpointMvcAdapter(delegate);
}
}
And then you can see the metrics in your application
http://localhost:8085/metrics
Allthough it is not a good idea to use Spring Boot features without Spring Boot, it is possible!
For example, this Java configuration makes Spring Boot Actuator Metrics available without using Spring Boot:
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
#Configuration
#Import({ EndpointAutoConfiguration.class, PublicMetricsAutoConfiguration.class })
public class SpringBootActuatorConfig {
#Bean
#Autowired
public EndpointHandlerMapping endpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
return new EndpointHandlerMapping(endpoints);
}
#Bean
#Autowired
public EndpointMvcAdapter metricsEndPoint(MetricsEndpoint delegate) {
return new EndpointMvcAdapter(delegate);
}
}
The Maven dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
Though the answer is already accepted, I thought of updating my experience. I did not want to convert my application to spring boot using #SpringBootApplication. Refer to another question where I have mentioned the bare minimum code required.
As we already have Spring Boot Actuator 2.x, a recipe to include actuator to an existing Spring MVC project can look like this:
#Configuration
#Import({
EndpointAutoConfiguration.class,
HealthIndicatorAutoConfiguration.class,
InfoEndpointAutoConfiguration.class,
HealthEndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class,
ManagementContextAutoConfiguration.class,
})
#EnableConfigurationProperties(CorsEndpointProperties.class)
class ActuatorConfiguration {
#Bean //taken from WebMvcEndpointManagementContextConfiguration.class
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier,
ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
EndpointMapping endpointMapping = new EndpointMapping(webEndpointProperties.getBasePath());
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes,
corsProperties.toCorsConfiguration(),
new EndpointLinksResolver(allEndpoints, webEndpointProperties.getBasePath()));
}
#Bean
DispatcherServletPath dispatcherServletPath() {
return () -> "/";
}
}
I did include
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<version>2.1.18.RELEASE</version>
</dependency>
for compatibility with the baseline Spring version I've been using (5.1.19.RELEASE)
If your objective is to create an endpoint with metrics for Prometheus a.k.a. OpenMetrics, you can use the Prometheus JVM client which is compatible with Spring framework.
Add dependency:
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_servlet</artifactId>
<version>0.16.0</version>
</dependency>
To collect metrics of requests, add as first filter in web-app/WEB-INF/web.xml:
<filter>
<filter-name>prometheusFilter</filter-name>
<filter-class>io.prometheus.client.filter.MetricsFilter</filter-class>
<init-param>
<param-name>metric-name</param-name>
<param-value>webapp_metrics_filter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>prometheusFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
To expose metrics as HTTP endpoint, add servlet:
<servlet>
<servlet-name>prometheus</servlet-name>
<servlet-class>io.prometheus.client.exporter.MetricsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>prometheus</servlet-name>
<url-pattern>/metrics</url-pattern>
</servlet-mapping>
After that you can see the metrics on the /metrics endpoint.
Time passes, we have Spring 6, SpringBoot 3, JakartaEE as a baseline, but people are still looking to add actuator to legacy spring applications. So a small update: spring + actuator without spring-boot. In fact not much changes (and the changes have already been pointed out).
The dependencies
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.3</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<version>3.0.1</version>
</dependency>
The actuator configuration
#Configuration
#ImportAutoConfiguration({
EndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class,
ManagementContextAutoConfiguration.class,
HealthContributorAutoConfiguration.class,
InfoEndpointAutoConfiguration.class,
HealthEndpointAutoConfiguration.class,
HeapDumpWebEndpointAutoConfiguration.class,
ThreadDumpEndpointAutoConfiguration.class,
LoggersEndpointAutoConfiguration.class,
PrometheusMetricsExportAutoConfiguration.class,
})
#EnableConfigurationProperties(CorsEndpointProperties.class)
class ActuatorConfiguration {
#Bean //taken from WebMvcEndpointManagementContextConfiguration.class
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier,
ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
EndpointMapping endpointMapping = new EndpointMapping(webEndpointProperties.getBasePath());
return new WebMvcEndpointHandlerMapping(endpointMapping,
webEndpoints,
endpointMediaTypes,
corsProperties.toCorsConfiguration(),
new EndpointLinksResolver(allEndpoints, webEndpointProperties.getBasePath()),
true);
}
#Bean
DispatcherServletPath dispatcherServletPath() {
return () -> WebInitializer.APPLICATION_ROOT;
}
}
The example is easy to run directly from maven jetty plugin (mvn jetty:run-war).
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>11.0.13</version>
</plugin>
you have made the mistake by not introducing the #springboot annotation in your code.When you add #springboot ot will consider as boot program by the compiler automatically and addd the required dependency file for it and your actuator dependency file

Library for distributed spring config (outside springboot)

I am looking for solution for distributed spring configuration. I am thinking of storing it in zookeeper. https://github.com/spring-cloud/spring-cloud-zookeeper does have that functionality but apparently it requires to use spring-boot.
Is there any similar library that I can use outside spring-boot
Consul by HashiCorp
Consul is a popular option because it is:
Open Source
Includes Service Discovery & Configuration
Support Multi-Datacenter out of the box
Etc.
It doesn't require you to use Spring Boot, it just provides the auto-configurations in case you do decide to go with Spring Boot. In other words, if you're not using Spring Boot, none of the configurations will apply automatically, you'll have to provide the configuration yourself.
Zookeeper is a good option, go for it.
EDIT:
To use Zookeeper without Spring Boot, you'd need to register the appropriate beans either manually or by importing the auto-configuration classes that Spring Boot would import for you implicitly. This rule of thumb generally applies to all Spring Boot-enabled modules.
In your case, you'd most likely need to import just the ZookeeperConfigBootstrapConfiguration and ZookeeperConfigAutoConfiguration. The classes are to be found within spring-cloud-zookeeper-config module so no Spring Boot dependencies needed.
Alternatively, you should look at those classes and their #Imports and declare the beans manually.
I found a solution for using spring-cloud-zookeeper without Spring Boot, based on the idea provided here https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html
First, create a CloudEnvironement class that will create a PropertySource from Zookeeper :
CloudEnvironement.java
public class CloudEnvironment extends StandardServletEnvironment {
#Override
protected void customizePropertySources(MutablePropertySources propertySources) {
super.customizePropertySources(propertySources);
try {
propertySources.addLast(initConfigServicePropertySourceLocator(this));
}
catch (Exception ex) {
logger.warn("failed to initialize cloud config environment", ex);
}
}
private PropertySource<?> initConfigServicePropertySourceLocator(Environment environment) {
ZookeeperConfigProperties configProp = new ZookeeperConfigProperties();
ZookeeperProperties props = new ZookeeperProperties();
props.setConnectString("myzookeeper:2181");
CuratorFramework fwk = curatorFramework(exponentialBackoffRetry(props), props);
ZookeeperPropertySourceLocator propertySourceLocator = new ZookeeperPropertySourceLocator(fwk, configProp);
PropertySource<?> source= propertySourceLocator.locate(environment);
return source ;
}
private CuratorFramework curatorFramework(RetryPolicy retryPolicy, ZookeeperProperties properties) {
CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
builder.connectString(properties.getConnectString());
CuratorFramework curator = builder.retryPolicy(retryPolicy).build();
curator.start();
try {
curator.blockUntilConnected(properties.getBlockUntilConnectedWait(), properties.getBlockUntilConnectedUnit());
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
return curator;
}
private RetryPolicy exponentialBackoffRetry(ZookeeperProperties properties) {
return new ExponentialBackoffRetry(properties.getBaseSleepTimeMs(),
properties.getMaxRetries(),
properties.getMaxSleepMs());
}
}
Then create a custom XmlWebApplicationContext class : it will enable to load the PropertySource from Zookeeper when your webapplication start and replace the bootsrap magic of Spring Boot:
MyConfigurableWebApplicationContext.java
public class MyConfigurableWebApplicationContext extends XmlWebApplicationContext {
#Override
protected ConfigurableEnvironment createEnvironment() {
return new CloudEnvironment();
}
}
Last, in your web.xml file add the following context-param for using your MyConfigurableWebApplicationContext class and bootstraping your CloudEnvironement.
<context-param>
<param-name>contextClass</param-name>
<param-value>com.kiabi.config.MyConfigurableWebApplicationContext</param-value>
</context-param>
If you use a standard property file configurer, it should still be loaded so you can have properties in both a local file and Zookeeper.
For all this to work you need to have spring-cloud-starter-zookeeper-config and curator-framework jar in your classpath with their dependancy, if you use maven you can add the following to your pom.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper-dependencies</artifactId>
<version>1.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-config</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
</dependencies>

How do I get my Spring Aspect to kick in for a #Valid annotation on a service method?

We're using Spring 3.2.11.RELEASE and Maven 3.0.3. I'm trying to set up validation of a parameter being passed into a service method. The method is below. Notice the #Valid annotation.
package org.mainco.subco.mypck.service;
#Service
#RemoteProxy
#Transactional
public class MypckServiceImpl implements MypckService {
#RemoteMethod
#Override
public String myMethod(#Valid final MyObjectDto request) {
// ...
}
}
Here is the aspect I have set up to help validate the object:
#Aspect
#Component
public class MyObjectValidatingAspect extends AbstractDWRAspectValidator<MyObjectDto>
{
#Before("execution(* org.mainco.subco.mypck.service.MypckService.myMethod(..))")
public void validateBefore(JoinPoint jp)
{
errors = new ArrayList<String>();
final MyObjectDto request = validate(jp);
validateMyObject(request);
throwErrors();
} // validateBefore
This is in included in my application context file:
<global-method-security pre-post-annotations="enabled">
</global-method-security>
<aop:aspectj-autoproxy/>
And this is what I've included in the Maven pom.xml file:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.2</version>
</dependency>
Unfortunately when the method is invoked, the aspectj's validateBefore is never called. What else do I need to do so that this gets invoked?
Since Spring 3.1 there is the MethodValidationInterceptor which basically does what you want to achieve yourself. To have this interceptor applied the only thing you need to do is to register a MethodValidationPostProcessor in your application context.
By default it will check for the #Validated annotation from Spring but you can instruct it to scan for the #Valid annotation.
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="validatedAnnotationType" value="javax.validation.Valid" />
<property name="validator" ref="refToYOurLocalValidatorFactoryBean" />
</bean>
If you don't specify a validator the default JSR-303 validator mechanism will be used (or the more hibernate specific one if that is available). But I can imagine you want to reuse the already configured instance.

Shiro Authorization Permission check using Annotation not working

Platform: Shiro 1.1.0, Spring 3.0.5
I'm trying to secure the MVC Controller methods using Shiro annotation. However something is wrong with annotations. Regular calls are just working OK. There is nothing specific in Shiro debug also.
My shiro configuration:
<!-- Security Manager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="sessionMode" value="native" />
<property name="realm" ref="jdbcRealm" />
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- Caching -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManager" ref="ehCacheManager" />
</bean>
<bean id="ehCacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />
<bean id="sessionDAO"
class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO" />
<bean id="sessionManager"
class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="sessionDAO" ref="sessionDAO" />
</bean>
<!-- JDBC Realm Settings -->
<bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
<property name="name" value="jdbcRealm" />
<property name="dataSource" ref="dataSource" />
<property name="authenticationQuery"
value="SELECT password FROM system_user_accounts WHERE username=? and status=1" />
<property name="userRolesQuery"
value="SELECT role_name FROM system_roles r, system_user_accounts u, system_user_roles ur WHERE u.user_id=ur.user_id AND r.role_id=ur.role_id AND u.username=?" />
<property name="permissionsQuery"
value="SELECT permission_name FROM system_roles r, system_permissions p, system_role_permission rp WHERE r.role_id=rp.role_id AND p.permission_id=rp.permission_id AND r.role_name=?" />
<property name="permissionsLookupEnabled" value="true"></property>
</bean>
<!-- Spring Integration -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after
the lifecycleBeanProcessor has run: -->
<bean id="annotationProxy"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor" />
<bean
class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- Secure Spring remoting: Ensure any Spring Remoting method invocations
can be associated with a Subject for security checks. -->
<bean id="secureRemoteInvocationExecutor"
class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- Shiro filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/dashboard" />
<property name="unauthorizedUrl" value="/error" />
<property name="filterChainDefinitions">
<value>
<!-- !!! Order matters !!! -->
/authenticate = anon
/login = anon
/logout = anon
/error = anon
/** = authc
</value>
</property>
</bean>
I can get the following working correctly:
#RequestMapping(value="/form")
public String viewPatientForm(Model model, #RequestParam(value="patientId", required=false) Long patientId){
if (!SecurityUtils.getSubject().isPermitted("hc:viewPatient")){
logger.error("Operation not permitted");
throw new AuthorizationException("No Permission");
}
}
But the below doesn't work:
#RequiresPermissions("hc:patientView")
#RequestMapping(value="/form")
public String viewPatientForm(Model model, #RequestParam(value="patientId", required=false) Long patientId){
Am I missing something? Please help.
You were absolutely right. After seeing your comment, I started giving it a thought. Well then I found out that it was NOT an implementation problem with Shiro, but the jar dependecies were not properly configured. Shiro's pom.xml should have dependency for cglib2 too.
So the below changes worked for me :
Include all these four jar files.
aspectjrt-1.6.11.jar,
aspectjweaver-1.6.12.jar,
cglib-2.2.2.jar,
asm-3.3.1.jar,
If you are using maven then :
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
And finally placing the aop:aspectj-autoproxy in the webApplicationContext.xml
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- Annotation, so that it's easier to search controllers/components -->
<context:component-scan base-package="com.pepsey.soft.web.controller"/>
Note : The above two configuration should be placed together in the same spring-webApplicationContext.xml. Otherwise it won’t work. Moreover remove context:annotation-config if you have used it in your config. context:component-scan already scans all annotations.
Once you start testing , set your log4j to debug or (better) trace mode. Whenever you are starting your server you will find somewhere the following entry in your logs :
08:16:24,684 DEBUG AnnotationAwareAspectJAutoProxyCreator:537 -
Creating implicit proxy for bean 'userController' with 0 common
interceptor and 1 specific interceptors
Guess Shiro was built when Spring 2.0 was in place. Shiro’s annotations (RequiresRoles etc…) works well for the spring container managed beans (service layer), but it does not work with #Controller annotation. This is due to the fact that #Controller is being component scanned by spring framework. I used AOP to resolve the issue. Below is the solution which worked for me.
For the below solution to work you have to include the below four jars:
aspectjrt-1.6.11.jar
aspectjweaver-1.6.12.jar
cglib-2.2.2.jar
asm-3.3.1.jar
If you are using maven then below configuration would be helpful.
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
Below is a controller class
import org.apache.shiro.authz.annotation.RequiresRoles;
#Controller
public class PatientController {
#RequiresRoles(“admin,warden”)
#RequestMapping(value="/form")
public String viewPatientForm(Model model, #RequestParam(value="patientId", required=false) Long patientId){
return “somePatientFormJsp”;
}
}
Create the below Aspect for the annotation (RequiresRoles). You can use the same principle to create pointcuts for RequiresPermission.
import java.util.Arrays;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class WebAuthorizationAspect {
#Before("#target(org.springframework.stereotype.Controller) && #annotation(requiresRoles)")
public void assertAuthorized(JoinPoint jp, RequiresRoles requiresRoles) {
SecurityUtils.getSubject().checkRoles(Arrays.asList(requiresRoles.value()));
}
}
In your spring-webApplicationContext.xml wherever you have mentioned
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- Annotation, so that it's easier to search controllers/components -->
<context:component-scan base-package="com.example.controller"/>
Note : The above two configuration should be placed together in the same spring-webApplicationContext.xml. Otherwise it won’t work. Moreover remove context:annotation-config if you have used it in your config. context:component-scan already scans all annotations.
If you're avoiding Spring XML and using primarily Java and annotation configuration, the easiest way to fix this is to add
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
to all your #Controller classes. You need cglib on the classpath.
I have only used spring-hibernate example from sample. To use annotations like #RequiresPermissions and others I tried configuration from shiro manual, configuration from this post, but I was either unsuccessful to compile or run the valid urls. So I only commented all the #RequiresPermissions from ManageUserController and started to use it in service implementation. E.g In DefaultUserService in getAllUsers method I added the annotation #RequiresPermissions("user:manage"). Magically now the application works as expected. Whenever the url manageUsers is called it displays the list page if the user has role user:manage and throws the user to /unauthorized if the user don't have that permission.
I have even configured the application to use mysql instead. To make the permissions independent of roles according to new RBAC(http://www.stormpath.com/blog/new-rbac-resource-based-access-control) I have created a new class called Permission as
#Entity
#Table(name = "permissions")
#Cache(usage= CacheConcurrencyStrategy.READ_WRITE)
public class Permission {
#Id
#GeneratedValue
private Long id;
private String element;
private String description;
// setter and getter
Now Role class is configured as
#CollectionOfElements
#JoinTable(name="roles_permissions")
#Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public Set<Permission> getPermissions() {
return permissions;
}
And finally SampleRealm as
for (Role role : user.getRoles()) {
info.addRole(role.getName());
System.out.println("Roles " + role.getName());
// Get permissions first
Set<Permission> permissions = role.getPermissions();
Set<String> permissionsStrings = new HashSet<String>();
for (Permission permission : permissions) {
permissionsStrings.add(permission.getelement());
System.out
.println("Permissions " + permission.getelement());
}
info.addStringPermissions(permissionsStrings);
}
It creates five tables as
| permissions |
| roles |
| roles_permissions |
| users |
| users_roles |
And permissions is independent of any other. According to new RBAC you have both ways (explicit and implicit) way of authorising resources.
You need to write the AuthorizationAttributeSourceAdvisor to enable Shiro's annotations bean as per the Shiro documentation
If you have written ShiroConfiguration class, make sure you include this:
#Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
#Bean
#ConditionalOnMissingBean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager) {
// This is to enable Shiro's security annotations
AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();
sourceAdvisor.setSecurityManager(securityManager);
return sourceAdvisor;
}
#ConditionalOnMissingBean
#Bean(name = "defaultAdvisorAutoProxyCreator")
#DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(true);
return proxyCreator;
}
Example ShiroConfiguration on Github
I had the same problem. My fix was changing my jersey version from 2.2 to 2.22.2 and all #RequiresPermissions worked on my controllers.

Resources