Part 1: Functional Specs and the Application Database
Part 2: Spring Security Configuration
Part 3: Spring MVC Module
Part 4: Running the Application
Part 3: Spring MVC
The Spring MVC module development is quite straightforward. We'll be creating the following:1. domain objects 2. services 3. controllers 4. configuration files
The Domain Objects
In Part 1 we discussed in the Functional Specs section that our Bulletin application has three types of posts:AdminPost - contains an id, date, and message PersonalPost - contains an id, date, and message PublicPost - contains an id, date, and messageThese posts correspond to the domain objects of the system. Let's start by creating a common interface:
Post.java
package org.krams.tutorial.domain; import java.util.Date; /** * A simple interface for Post objects */ public interface Post { public Long getId(); public void setId(Long id); public Date getDate(); public void setDate(Date date); public String getMessage(); public void setMessage(String message); }The method signatures correspond to the properties we've discussed in the Functional Specs section.
Let's now create three concrete classes:
AdminPost.java
package org.krams.tutorial.domain; import java.util.Date; /** * A simple POJO representing admin posts */ public class AdminPost implements Post { private Long id; private Date date; private String message; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
PersonalPost.java
package org.krams.tutorial.domain; import java.util.Date; /** * A simple POJO representing personal posts */ public class PersonalPost implements Post { private Long id; private Date date; private String message; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
PublicPost.java
package org.krams.tutorial.domain; import java.util.Date; /** * A simple POJO representing public posts */ public class PublicPost implements Post { private Long id; private Date date; private String message; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }Our concrete classes are just simple POJOs.
The Services
We'll be declaring three services where each one will handle a specific domain object:AdminService - handles AdminPost PersonalService - handles PersonalPost PublicService - handles PublicPost
Our services will implement a common interface. It's worth noting this interface is the crux of successfully applying ACL in the application. Therefore, we've heavily placed comments within this interface to point out important information.
Here's the interface:
GenericService.java
package org.krams.tutorial.service; import java.util.List; import javax.sql.DataSource; import org.krams.tutorial.domain.Post; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PostFilter; import org.springframework.security.access.prepost.PreAuthorize; /** * A generic service for handling CRUD operations. * <p> * The method access-control expressions are specified in this interface. */ public interface GenericService { /** * Inject the datasource for the bulletingapplication */ public void setDataSource(DataSource dataSource); /** * Retrieves a single post. * <p> * Access-control will be evaluated after this method is invoked. * returnObject refers to the returned object. */ @PostAuthorize("hasPermission(returnObject, 'WRITE')") public Post getSingle(Long id); /** * Retrieves all posts. * <p> * Access-control will be evaluated after this method is invoked. * filterObject refers to the returned object list. */ @PostFilter("hasPermission(filterObject, 'READ')") public List<Post> getAll(); /** * Adds a new post. * <p> * We don't provide any access control here because * the new object doesn't have an id yet. * <p> * Instead we place the access control on the URL-level because * the Add page shouldn't be visible in the first place. * <p> * There are two places where we can place this restriction: * <pre> * 1. At the controller method * 2. At the external spring-security.xml file</pre> * <p> * */ public Boolean add(Post post); /** * Edits a post. * <p> * Access-control will be evaluated before this method is invoked. * <b>#post</b> refers to the current object in the method argument. */ @PreAuthorize("hasPermission(#post, 'WRITE')") public Boolean edit(Post post); /** * Deletes a post. * <p> * Access-control will be evaluated before this method is invoked. * <b>#post</b> refers to the current object in the method argument. */ @PreAuthorize("hasPermission(#post, 'WRITE')") public Boolean delete(Post post); }Notice the methods had been annotated with different types of Expression-based access controls, and because this is an interface all concrete classes will be secured with ACL.
Method Access-Control Expressions
Let's take an in-depth look at all the important annotations:@PostAuthorize
@PostAuthorize("hasPermission(returnObject, 'WRITE')") public Post getSingle(Long id);
Annotation for specifying a method access-control expression which will be evaluated after a method has been invoked - Source: Spring Security 3 APIThis annotation will be triggered after the getSingle() method has completed its execution. The rule that will be applied is inside the annotation:
"hasPermission(returnObject, 'WRITE')"This means whatever is the returned object (in our case, a Post object) make sure the current user has a WRITE access.
@PostFilter
@PostFilter("hasPermission(filterObject, 'READ')") public ListgetAll();
Annotation for specifying a method filtering expression which will be evaluated after a method has been invoked - Source: Spring Security 3 APIThis annotation will be triggered after the getAll() method has completed its execution. The rule that will be applied is inside the annotation:
"hasPermission(filterObject, 'READ')"This means whatever is the returned list of objects (in our case, Post objects) make sure to filter the list. Return only objects where the current user has READ access.
@PreAuthorize("hasPermission(#post, 'WRITE')") public Boolean edit(Post post);
Annotation for specifying a method access-control expression which will be evaluated to decide whether a method invocation is allowed or not - Source: Spring Security 3 APIThis annotation will be triggered before the edit() method is executed. The rule that will be applied is inside the annotation:
"hasPermission(#post, 'WRITE')"This means whatever is the object argument (in our case, a Post object) make sure the current user has WRITE access.
Extra Pointers
Notice all of the methods are annotated with method access-control expressions, except for two:1. public void setDataSource(DataSource dataSource); 2. public Boolean add(Post post);The method setDataSource() has nothing to do with Spring Security. It's just a reference to the application's datasource, the bulletinDataSource, which we'll discuss later.
The method add() is what's interesting. Remember the Functional Specs in Part 1:
1. Only users with ROLE_ADMIN can create AdminPost
2. Only users with ROLE_USER can create PersonalPost
3. Only users with ROLE_ADMIN or ROLE_USER can create PublicPost
4. Users with ROLE_VISITOR cannot create any post
Note: When we use the word 'create', we mean adding a new post.
Post creation is one of the crucial methods available in the application, but it's left unsecured! Here are the reasons why:
The object reference inside the hasPermission expression needs to have an existing id. But when creating a new Post, it doesn't have an id yet from the bulletin database!
Solution #1 (Bad)
We could use the @PostAuthorize expression to allow the user to add. Then check the returned object's id for valid permission. But this expression defeats the main purpose. The user has already created a record regardless of permission!
Solution #2 (Bad)
We could use the @PreAuthorize expression to verify if the current user has valid permissions before executing the add() method. But wait. That's wrong! The Post object doesn't have an existing id yet because it hasn't been created in the database!
There's another question: why does the object need an existing id? The simple answer is because the default PermissionEvaluator implementation and the default Acl implementation requires one. If we need to bypass this limitation, we can create our own implementations.
Solution #3 (Good)
Apply access control at the URL-level. In the first place, we don't really want to show the add new post page. Then it's better if we limit control at the URL-level. There are two ways where we can implement this:
a. add an intercept-url in the spring-security.xml b. apply method security control expression in the controllerWe'll choose option b because it's an interesting solution and allows us to explore method security control expressions at the controller level.
The Controllers
Our application controllers share the same implementation where the main difference only are the @RequestMapping values and the services.We'll be declaring four controllers:
AdminController - handles admin related requests PersonalController - handles user related requests PublicController - handles visitor related requests BulletinController - handles the general view all requests
AdminController.java
package org.krams.tutorial.controller; import java.util.Date; import org.apache.log4j.Logger; import org.krams.tutorial.domain.AdminPost; import org.krams.tutorial.domain.Post; import org.krams.tutorial.service.GenericService; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import javax.annotation.Resource; /** * Handles Admin-related requests */ @Controller @RequestMapping("/admin") public class AdminController { protected static Logger logger = Logger.getLogger("controller"); @Resource(name="adminService") private GenericService adminService; /** * Retrieves the Edit page */ @RequestMapping(value = "/edit", method = RequestMethod.GET) public String getEdit(@RequestParam(value="id", required=true) Long id, Model model) { logger.debug("Received request to show edit page"); // Retrieve existing post and add to model // This is the formBackingOBject model.addAttribute("postAttribute", adminService.getSingle(id)); // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Admin"); // This will resolve to /WEB-INF/jsp/crud-admin/editpage.jsp return "crud-admin/editpage"; } /** * Saves the edited post from the Edit page and returns a result page. */ @RequestMapping(value = "/edit", method = RequestMethod.POST) public String getEditPage(@ModelAttribute("postAttribute") AdminPost post, @RequestParam(value="id", required=true) Long id, Model model) { logger.debug("Received request to view edit page"); // Re-assign id post.setId(id); // Assign new date post.setDate(new Date()); // Delegate to service if (adminService.edit(post) == true) { // Add result to model model.addAttribute("result", "Entry has been edited successfully!"); } else { // Add result to model model.addAttribute("result", "You're not allowed to perform that action!"); } // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Admin"); // Add our current role and username model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities()); model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName()); // This will resolve to /WEB-INF/jsp/crud-admin/resultpage.jsp return "crud-admin/resultpage"; } /** * Retrieves the Add page * <p> * Access-control is placed here (instead in the service) because we don't want * to show this page if the client is unauthorized and because the new * object doesn't have an id. The hasPermission requires an existing id! */ @PreAuthorize("hasAuthority('ROLE_ADMIN')") @RequestMapping(value = "/add", method = RequestMethod.GET) public String getAdd(Model model) { logger.debug("Received request to show add page"); // Create new post and add to model // This is the formBackingOBject model.addAttribute("postAttribute", new AdminPost()); // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Admin"); // This will resolve to /WEB-INF/jsp/crud-admin/addpage.jsp return "crud-admin/addpage"; } /** * Saves a new post from the Add page and returns a result page. */ @RequestMapping(value = "/add", method = RequestMethod.POST) public String getAddPage(@ModelAttribute("postAttribute") AdminPost post, Model model) { logger.debug("Received request to view add page"); // Add date today post.setDate(new Date()); // Delegate to service if (adminService.add(post)) { // Success. Add result to model model.addAttribute("result", "Entry has been added successfully!"); } else { // Failure. Add result to model model.addAttribute("result", "You're not allowed to perform that action!"); } // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Admin"); // Add our current role and username model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities()); model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName()); // This will resolve to /WEB-INF/jsp/crud-admin/resultpage.jsp return "crud-admin/resultpage"; } /** * Deletes an existing post and returns a result page. */ @RequestMapping(value = "/delete", method = RequestMethod.GET) public String getDeletePage(@RequestParam(value="id", required=true) Long id, Model model) { logger.debug("Received request to view delete page"); // Create new post Post post = new AdminPost(); // Assign id post.setId(id); // Delegate to service if (adminService.delete(post)) { // Add result to model model.addAttribute("result", "Entry has been deleted successfully!"); } else { // Add result to model model.addAttribute("result", "You're not allowed to perform that action!"); } // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Admin"); // Add our current role and username model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities()); model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName()); // This will resolve to /WEB-INF/jsp/crud-admin/resultpage.jsp return "crud-admin/resultpage"; } }
PersonalController.java
package org.krams.tutorial.controller; import java.util.Date; import org.apache.log4j.Logger; import org.krams.tutorial.domain.PersonalPost; import org.krams.tutorial.domain.Post; import org.krams.tutorial.service.GenericService; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import javax.annotation.Resource; /** * Handles Personal-related requests */ @Controller @RequestMapping("/personal") public class PersonalController { protected static Logger logger = Logger.getLogger("controller"); @Resource(name="personalService") private GenericService personalService; /** * Retrieves the Edit page */ @RequestMapping(value = "/edit", method = RequestMethod.GET) public String getEdit(@RequestParam(value="id", required=true) Long id, Model model) { logger.debug("Received request to show edit page"); // Retrieve existing post and add to model // This is the formBackingOBject model.addAttribute("postAttribute", personalService.getSingle(id)); // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Personal"); // This will resolve to /WEB-INF/jsp/crud-personal/editpage.jsp return "crud-personal/editpage"; } /** * Saves the edited post from the Edit page and returns a result page. */ @RequestMapping(value = "/edit", method = RequestMethod.POST) public String getEditPage(@ModelAttribute("postAttribute") PersonalPost post, @RequestParam(value="id", required=true) Long id, Model model) { logger.debug("Received request to view edit page"); // Re-assign id post.setId(id); // Assign new date post.setDate(new Date()); // Delegate to service if (personalService.edit(post) == true) { // Add result to model model.addAttribute("result", "Entry has been edited successfully!"); } else { // Add result to model model.addAttribute("result", "You're not allowed to perform that action!"); } // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Personal"); // Add our current role and username model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities()); model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName()); // This will resolve to /WEB-INF/jsp/crud-personal/resultpage.jsp return "crud-personal/resultpage"; } /** * Retrieves the Add page * <p> * Access-control is placed here (instead in the service) because we don't want * to show this page if the client is unauthorized and because the new * object doesn't have an id. The hasPermission requires an existing id! */ @PreAuthorize("hasAuthority('ROLE_USER') and !hasAuthority('ROLE_ADMIN')") @RequestMapping(value = "/add", method = RequestMethod.GET) public String getAdd(Model model) { logger.debug("Received request to show add page"); // Create new post and add to model // This is the formBackingOBject model.addAttribute("postAttribute", new PersonalPost()); // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Personal"); // This will resolve to /WEB-INF/jsp/crud-personal/addpage.jsp return "crud-personal/addpage"; } /** * Saves a new post from the Add page and returns a result page. */ @RequestMapping(value = "/add", method = RequestMethod.POST) public String getAddPage(@ModelAttribute("postAttribute") PersonalPost post, Model model) { logger.debug("Received request to view add page"); // Add date today post.setDate(new Date()); // Delegate to service if (personalService.add(post)) { // Success. Add result to model model.addAttribute("result", "Entry has been added successfully!"); } else { // Failure. Add result to model model.addAttribute("result", "You're not allowed to perform that action!"); } // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Personal"); // Add our current role and username model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities()); model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName()); // This will resolve to /WEB-INF/jsp/crud-personal/resultpage.jsp return "crud-personal/resultpage"; } /** * Deletes an existing post and returns a result page. */ @RequestMapping(value = "/delete", method = RequestMethod.GET) public String getDeletePage(@RequestParam(value="id", required=true) Long id, Model model) { logger.debug("Received request to view delete page"); // Create new post Post post = new PersonalPost(); // Assign id post.setId(id); // Delegate to service if (personalService.delete(post)) { // Add result to model model.addAttribute("result", "Entry has been deleted successfully!"); } else { // Add result to model model.addAttribute("result", "You're not allowed to perform that action!"); } // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Personal"); // Add our current role and username model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities()); model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName()); // This will resolve to /WEB-INF/jsp/crud-personal/resultpage.jsp return "crud-personal/resultpage"; } }
PublicController.java
package org.krams.tutorial.controller; import java.util.Date; import org.apache.log4j.Logger; import org.krams.tutorial.domain.PublicPost; import org.krams.tutorial.domain.Post; import org.krams.tutorial.service.GenericService; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import javax.annotation.Resource; /** * Handles Public-related requests */ @Controller @RequestMapping("/public") public class PublicController { protected static Logger logger = Logger.getLogger("controller"); @Resource(name="publicService") private GenericService publicService; /** * Retrieves the Edit page */ @RequestMapping(value = "/edit", method = RequestMethod.GET) public String getEdit(@RequestParam(value="id", required=true) Long id, Model model) { logger.debug("Received request to show edit page"); // Retrieve existing post and add to model // This is the formBackingOBject model.addAttribute("postAttribute", publicService.getSingle(id)); // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Public"); // This will resolve to /WEB-INF/jsp/crud-public/editpage.jsp return "crud-public/editpage"; } /** * Saves the edited post from the Edit page and returns a result page. */ @RequestMapping(value = "/edit", method = RequestMethod.POST) public String getEditPage(@ModelAttribute("postAttribute") PublicPost post, @RequestParam(value="id", required=true) Long id, Model model) { logger.debug("Received request to view edit page"); // Re-assign id post.setId(id); // Assign new date post.setDate(new Date()); // Delegate to service if (publicService.edit(post) == true) { // Add result to model model.addAttribute("result", "Entry has been edited successfully!"); } else { // Add result to model model.addAttribute("result", "You're not allowed to perform that action!"); } // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Public"); // Add our current role and username model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities()); model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName()); // This will resolve to /WEB-INF/jsp/crud-public/resultpage.jsp return "crud-public/resultpage"; } /** * Retrieves the Add page * <p> * Access-control is placed here (instead in the service) because we don't want * to show this page if the client is unauthorized and because the new * object doesn't have an id. The hasPermission requires an existing id! */ @PreAuthorize("hasAuthority('ROLE_USER') or hasAuthority('ROLE_ADMIN')") @RequestMapping(value = "/add", method = RequestMethod.GET) public String getAdd(Model model) { logger.debug("Received request to show add page"); // Create new post and add to model // This is the formBackingOBject model.addAttribute("postAttribute", new PublicPost()); // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Public"); // This will resolve to /WEB-INF/jsp/crud-public/addpage.jsp return "crud-public/addpage"; } /** * Saves a new post from the Add page and returns a result page. */ @RequestMapping(value = "/add", method = RequestMethod.POST) public String getAddPage(@ModelAttribute("postAttribute") PublicPost post, Model model) { logger.debug("Received request to view add page"); // Add date today post.setDate(new Date()); // Delegate to service if (publicService.add(post)) { // Success. Add result to model model.addAttribute("result", "Entry has been added successfully!"); } else { // Failure. Add result to model model.addAttribute("result", "You're not allowed to perform that action!"); } // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Public"); // Add our current role and username model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities()); model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName()); // This will resolve to /WEB-INF/jsp/crud-public/resultpage.jsp return "crud-public/resultpage"; } /** * Deletes an existing post and returns a result page. */ @RequestMapping(value = "/delete", method = RequestMethod.GET) public String getDeletePage(@RequestParam(value="id", required=true) Long id, Model model) { logger.debug("Received request to view delete page"); // Create new post Post post = new PublicPost(); // Assign id post.setId(id); // Delegate to service if (publicService.delete(post)) { // Add result to model model.addAttribute("result", "Entry has been deleted successfully!"); } else { // Add result to model model.addAttribute("result", "You're not allowed to perform that action!"); } // Add source to model to help us determine the source of the JSP page model.addAttribute("source", "Public"); // Add our current role and username model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities()); model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName()); // This will resolve to /WEB-INF/jsp/crud-public/resultpage.jsp return "crud-public/resultpage"; } }
BulletinController.java
/** * */ package org.krams.tutorial.controller; import javax.annotation.Resource; import org.apache.log4j.Logger; import org.krams.tutorial.service.GenericService; import org.krams.tutorial.service.PersonalService; import org.krams.tutorial.service.PublicService; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Handles Bulletin related requests */ @Controller @RequestMapping("/bulletin") public class BulletinController{ protected static Logger logger = Logger.getLogger("controller"); @Resource(name="adminService") private GenericService adminService; @Resource(name="personalService") private GenericService personalService; @Resource(name="publicService") private GenericService publicService; /** * Retrieves the View page. * <p> * This loads all authorized posts. */ @RequestMapping(value = "/view", method = RequestMethod.GET) public String getViewAllPage(Model model) { logger.debug("Received request to view all page"); // Retrieve items from service and add to model model.addAttribute("adminposts", adminService.getAll()); model.addAttribute("personalposts", personalService.getAll()); model.addAttribute("publicposts", publicService.getAll()); // Add our current role and username model.addAttribute("role", SecurityContextHolder.getContext().getAuthentication().getAuthorities()); model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName()); // This will resolve to /WEB-INF/jsp/bulletinpage.jsp return "bulletinpage"; } }
Spring MVC Configuration
We've completed the Spring MVC classes of our application. Our next task is to declare the required configuration.web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring-security.xml /WEB-INF/applicationContext.xml </param-value> </context-param> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/krams/*</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
Take note of the URL pattern. When accessing any pages in our MVC application, the host name must be appended with
/kramsIn the web.xml we declared a servlet-name spring. By convention, we must declare a spring-servlet.xml as well.
spring-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- Declare a view resolver --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" /> </beans>This XML config declares a view resolver. All references to a JSP name in the controllers will map to a corresponding JSP in the /WEB-INF/jsp location.
By convention, we must declare an applicationContext.xml
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <!-- Activates various annotations to be detected in bean classes --> <context:annotation-config /> <!-- Scans the classpath for annotated components that will be auto-registered as Spring beans. For example @Controller and @Service. Make sure to set the correct base-package--> <context:component-scan base-package="org.krams.tutorial" /> <!-- Configures the annotation-driven Spring MVC Controller programming model. Note that, with Spring 3.0, this tag works in Servlet MVC only! --> <mvc:annotation-driven /> <!-- Loads bulletin related configuration--> <import resource="bulletin-context.xml" /> </beans>
Conclusion
We've completed the Spring MVC module of the Bulletin application. We've declared the required domain objects, services, and controllers. Our final task is to test and run the application. We'll also cover some of the unexpected problems within the application.Proceed to Part 4: Running the Application
Share the joy:
|
Subscribe by reader Subscribe by email Share
Pfeeeeeeeeee !! it was long but it's a very very very GOOD TUTORIAL ! Thx man for it ! (where is the code source ? :-( )
ReplyDelete@Anonymous, thanks for the comment. I think you forgot to look at part 4 :-) The source code is there.
ReplyDeleteExcellent! but why you used @PreAuthorize("hasAuthority('ROLE_ADMIN')") in admin controller this method is already secured using URL level security
ReplyDeletethanks for all these great tutorials
ReplyDeleteThese tutorials are holding up great. Thanks for the help. Just a quick note the object-reference in the @PreAuthorize annotation no longer works in interfaces. So this no longer works: "@PreAuthorize("hasPermission(#post, 'WRITE')")" You would have to annotate the method parameter with @Param("post"). Like this:
ReplyDelete@PreAuthorize("hasPermission(#post, 'WRITE')")
public Boolean edit(@Param("post") Post post);
I have read your blog its very attractive and impressive. I like it your blog.
ReplyDeleteSpring online training Spring online training Spring Hibernate online training Spring Hibernate online training Java online training
spring training in chennai spring hibernate training in chennai
Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging.
ReplyDeletecore java training in Electronic City
Hibernate Training in electronic city
spring training in electronic city
java j2ee training in electronic city
Nice Presentation and its hopefull words..
ReplyDeleteif you want a cheap web hosting in web
crm software development company in chennai
erp software development company in chennai
Professional webdesigning company in chennai
best seo company in chennai
thanks for ur valuable information,keep going touch with us
ReplyDeleteScaffolding dealers in chennai
Thanks for sharing! I like your blog and keep upadte us.
ReplyDeleteChatbot companies in india,
Chatbot development company,
Chatbot companies,
Messenger bot developer,
Facebook bot development,
Chatbot developer,
Bot developer,
yeezy boost 350
ReplyDeletebapesta shoes
Golden Goose Deluxe Brand
nike kyrie 7
fear of god essentials
off white nike
off white shoes
off white hoodie
yeezy 700
hermes handbags