Project

General

Profile

Sample-spring-basic » History » Revision 31

Revision 30 (Henning Blohm, 01.03.2020 22:02) → Revision 31/33 (Henning Blohm, 01.03.2020 22:18)

h1. A basic Spring with Z2 modularity sample 

 The sample contained in the repository "z2-samples.spring-basic":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic is a clean room example on how to use the Spring integration features provided by [[Spring_Add-on]] and described in [[how_to_spring]]. 

 {{include(Java Version Requirements)}} 

 There is no further pre-requisite to running this sample. 

 {{include(Install_sample_prefix)}} 

 Check out the sample 

 <pre><code class="ruby"> 
 git clone -b v2.8 https://www.z2-environment.net/git/z2-samples.spring-basic 
 </code></pre> 

 {{include(Install_sample_postfix)}} 

 *Notes:* 

 * If you run into read timeouts due to weak internet connection, simply keep repeating to start z2. 
 * If you are located behind an internet proxy, please check [[How_to_proxy_settings]] on how to configure your installation. 


 If you want to inspect the code using Eclipse, please create a workspace in install (i.e. *install/workspace*) and import the Git repositories and projects (see also [[Step_3_-_First_steps_with_Z2_on_Git|First steps]]). 

 There are three modules contained in this sample. For the moment only consider the following two: 

 h2. com.zfabrik.samples.spring-basic.services 

 This module has a classpath defined "applicationContext":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic/revisions/master/entry/com.zfabrik.samples.spring-basic.services/java/src.impl/META-INF/applicationContext.xml and exposes an annotation defined bean "ComputationServiceImpl":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic/revisions/master/entry/com.zfabrik.samples.spring-basic.services/java/src.impl/com/zfabrik/samples/impl/services/ComputationServiceImpl.java bean from it as a service to be consumed from another module: The *computations* bean. 

 The application context enables discovery of Spring beans that are marked by annotations such as @Service, @Component, @Repository: 

 <pre><code class="xml"> 
 <beans ...> 
	 <!-- Turn on annotation based config --> 
	 <context:annotation-config/> 
	 <!-- Turn on auto discovery --> 
	 <context:component-scan base-package="com.zfabrik.samples.impl"/> 
 </beans> 
 </code></pre> 

 The *computations" bean implementation implements the interface "IComputationService":https://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic/revisions/master/entry/com.zfabrik.samples.spring-basic.services/java/src.api/com/zfabrik/samples/services/IComputationService.java and is itself not visible to consumers. It's structure is roughly like this: 

 <pre><code class="java"> 
 @Service("computations") 
 public class ComputationServiceImpl implements IComputationService { 

 ... 

 } 
 </code></pre> 

 The *computations* bean is exposed via the Z2 component "com.zfabrik.samples.spring-basic.services/computations":https://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic/revisions/master/entry/com.zfabrik.samples.spring-basic.services/computations.properties: 

 <pre><code class="ruby"> 
 com.zfabrik.component.type=org.springframework.bean 

 # 
 # the context that defines the bean (more than one 
 # bean can be exposed like this) 
 # 
 bean.context=com.zfabrik.samples.spring-basic.services/applicationContext 

 # 
 # the bean name 
 # 
 bean.name=computations 
 </code></pre> 

 Note that the Z2 Bean component names its application context. At runtime this means that an attempt to retrieve the bean will make sure the application context is loaded - and not any earlier. 

 h2. com.zfabrik.samples.spring-basic.frontend 

 This module has a Web application with an application context defined in "WEB-INF/applicationContext":https://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic/revisions/master/entry/com.zfabrik.samples.spring-basic.frontend/web/WebContent/WEB-INF/applicationContext.xml. It uses Spring AspectJ based annotation driven configuration to inject dependencies into instances of "ControllerServlet":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic/revisions/master/entry/com.zfabrik.samples.spring-basic.frontend/java/src.impl/com/zfabrik/samples/impl/frontend/ControllerServlet.java. 

 Note the Java component descriptor that, apart from referencing the service module, holds the minimum declaration to make sure of Spring with AspectJ supported, compile-time-woven annotation based configuration: 

 <pre><code class="ruby"> 
 com.zfabrik.component.type=com.zfabrik.java 

 java.privateReferences=\ 
	 com.zfabrik.springframework.web,\ 
	 org.springframework.tx,\ 
	 org.springframework.orm,\ 
	 com.zfabrik.samples.spring-basic.services 

 java.privateIncludes=\ 
	 org.springframework.security:spring-security-core,\ 
	 org.springframework:spring-aspects 

 java.compile.order=java,spring_aspectj 
 </code></pre> 


 It's "application context":http://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic/revisions/master/entry/com.zfabrik.samples.spring-basic.frontend/web/WebContent/WEB-INF/applicationContext.xml imports the *computations* service from the other module above and enables the use of Spring configuration (note: Unlike above it does not enable discovery of Spring beans): 

 <pre><code class="xml"> 
 <beans ... > 
	 <!-- turn on @Configurable support --> 
	 <context:spring-configured/> 
	 <!-- Turn on annotation based config --> 
	 <context:annotation-config/> 
 	 <!-- application config: Bind the bean at samples.spring.simplemodules.services/computations -->  
	 <bean id="computations" class="com.zfabrik.springframework.ComponentFactoryBean"> 
		 <property name="componentName" value="com.zfabrik.samples.spring-basic.services/computations"/> 
		 <property name="className" value="com.zfabrik.samples.services.IComputationService"/> 
	 </bean> 
 </beans> 
 </code></pre> 

 h2. Finally 

 Open a browser and navigate to http://localhost:8080/spring-basic to verify you get this: 

 !frontend.png! 

 h1. An extended Spring with Z2 modularity and some Spring Security sample 

 The third module *com.zfabrik.samples.spring-basic.secured* contained in the sample repository implements a very similar basic frontend to the one above but illustrating in addition: 

 * How to use Spring Security to secure the access to a Web application 
 * How to use Spring Security to secure methods of a bean 
 * How to use Spring Security with Spring AspectJ weaving 

 More specifically, the contained Web application knows of two users *"user"* (password "user") and *"admin"* (password "admin") that have roles @ROLE_USER@ or @ROLE_USER@ and @ROLE_ADMIN@ respectively. 

 The controller servlet delegates operations to a _session facade_ implemented by the class "SomeFacadeImpl":https://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic/revisions/master/entry/com.zfabrik.samples.spring-basic.secured/java/src.impl/com/zfabrik/samples/impl/facades/SomeFacadeImpl.java that offers two methods, one requiring @ROLE_USER@, one requiring @ROLE_ADMIN@. 

 But let's rather have a step-by-step overview: The Java component declaration "com.zfabrik.samples.spring-basic.secured/java":https://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic/revisions/master/entry/com.zfabrik.samples.spring-basic.secured/java/z.properties, as compared to the one of the other frontend module has some noteworthy additions: 

 <pre><code class="ruby"> 
 com.zfabrik.component.type=com.zfabrik.java 

 java.privateReferences=\ 
	 com.zfabrik.samples.spring-basic.services,\ 
	 org.springframework.tx,\ 
	 org.springframework.orm,\ 
	 com.zfabrik.springframework.web 

 java.privateIncludes=\ 
	 org.springframework:spring-aspects,\ 
	 org.springframework.security:spring-security-core,\ 
	 org.springframework.security:spring-security-web,\ 
	 org.springframework.security:spring-security-config,\ 
	 org.springframework.security:spring-security-aspects 

 # 
 # this enables the processing of @Secured annotations 
 # 
 aspectj.privateAspectPathByClass=\ 
	 org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect 
	
 java.compile.order=java,spring_aspectj 
 </code></pre> 

 One the one it references the Spring Security module and it includes some Spring Security elements (that unfortunately must be included). Notably the support for Web apps, the *config* support, and finally the *aspect*, which is the pre-requisite for AspectJ support with Spring Security.  

 We prefer AspectJ compile-time weaving over Spring AOP as it is much more consistent (non-proxy-based AOP, see also [[how_to_spring]] and elsewhere) and, as the compilation part is automatic with Z2, no extra burden to configure.  

 However, to let the Spring AspectJ compiler, supported by Z2 know about the handling of Spring Security annotations used in the implementation, the corresponding _Aspect Implementation_ must be indicated. Hence the additional property "aspectj.privateAspectPathByClass". See also "AspectJCompiler":http://www.z2-environment.net/javadoc/com.zfabrik.springframework!2Fjava/impl/com/zfabrik/impl/springframework/AspectJCompiler.html for more details. 

 Fortunately this was the hardest part of our configuration tour.  

 The Web apps "application context":https://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic/revisions/master/entry/com.zfabrik.samples.spring-basic.secured/web/WebContent/WEB-INF/applicationContext.xml has a lot of similarities to the one of the other frontend but adds some Spring Security related configuration: 


 <pre><code class="xml"> 
 ... 

	 <!-- web security config: require basic authentication, hard-coded users "user" and "admin" --> 
	 <security:http auto-config='true' disable-url-rewriting="true"> 
		 <security:intercept-url pattern="/**" access="ROLE_ADMIN,ROLE_USER" /> 
		 <security:http-basic /> 
	 </security:http> 

	 <security:authentication-manager> 
		 <security:authentication-provider> 
			 <security:user-service> 
				 <security:user name="admin" password="admin" authorities="ROLE_ADMIN,ROLE_USER" /> 
				 <security:user name="user" password="user" authorities="ROLE_USER" /> 
			 </security:user-service> 
		 </security:authentication-provider> 
	 </security:authentication-manager> 

	 <!-- method security config (in this case, only Secured annotations enabled)--> 
	 <security:global-method-security 
		 secured-annotations="enabled" 
		 mode="aspectj" 
	 /> 

 ... 
 </code></pre> 

 Having users and passwords declared in the application context is is a sacrifice due to the sample nature. Other than that these declarations say that  

 # the Web app uses Basic Authentication (being simpler than a form-based login for the sample),  
 # that all requests require either @ROLE_USER@ or @ROLE_ADMIN@, and  
 # that it makes use of _Method Security_, expecting the support of @Secured annotations with AspectJ weaving. 

 The controller servlet ("ControllerServlet":https://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic/revisions/master/entry/com.zfabrik.samples.spring-basic.secured/java/src.impl/com/zfabrik/samples/impl/frontend/ControllerServlet.java) delegates all calls to the facade implementation "SomeFacadeImpl":https://redmine.z2-environment.net/projects/z2-samples/repository/z2-samples-spring-basic/revisions/master/entry/com.zfabrik.samples.spring-basic.secured/java/src.impl/com/zfabrik/samples/impl/facades/SomeFacadeImpl.java that has secured methods: 

 <pre><code class="java"> 
 @Component 
 public class SomeFacadeImpl { 
	 private final static Logger LOG = Logger.getLogger(SomeFacadeImpl.class.getName()); 
	
	 public SomeFacadeImpl() { 
		 LOG.info("Init of "+this); 
	 } 

	 @Autowired 
	 private IComputationService computations; 

	 @Secured("ROLE_USER") 
	 public String doSomethingWithAString(String in) { 
		 return computations.doSomethingWithAString(in); 
	 } 
	
	 @Secured("ROLE_ADMIN") 
	 public String doSomethingThatRequiresAdmin() { 
		 return "Yes, you can!"; 
	 } 	
 } 
 </code></pre> 

 It does use the re-use service *computations* from the other module and gets it injected as above. 

 Now, if you open a Web browser at http://localhost:8080/spring-basic-secured you will first be asked to log in (best try "user" with password "user" first) and next offered the following options: 

 !secured.png! 

 Pressing the button beneath "Try as admin" should give you an "Access denied" response, if you did not log on with the "admin" user, while it should give some affirmative response in case you did not log with admin permissions.