Buddies,
I'm about to develop a web application using Spring 3 MVC as web framework, Hibernate Annotations as ORM framework However, I'm facing a problem to design a good database based access control for this application. In my work we are used to design this way:
(CompanyName)User.java - A class that means a User in the system
Profile.java - A class that means a ROLE in the system in a N-N relationship with (CompanyName)User. With ROLE, I mean an user group such as (ADMIN,ANONYMOUS,CUSTOMER SERVICE USER, etc.)
UserProfile.java - A class that means a relationship between an User and a Profile. It represents a JOIN TABLE for a N-N relationship in the database.
Module.java - A class that means a MODULE in the web application. Each module is made of unlimited features, but each features can only be related to one MODULE. For example, an USER AUTHENTICATION feature would be related to a SECURITY or AUTHENTICATION module. Modules are controllers in the application tagged with @Controller.
Feature.java - A class that represents a FEATURE in the application. Each feature is made of one or many operations. For example, USER MANAGEMENT is a FEATURE. And as such, it is made of many operation (e.g CREATE, READ, UPDATE and DELETE USER). Also, a FEATURE has an ENTRY URL that represents an URL to that feature (to redirect an user to that feature when clicking a button/link). Each URL is mapped to a method in a Module(Controller).
Operation.java - A class that represents an OPERATION in the web application. An operation is basically a single/basic operation such as REGISTER USER or REMOVE USER, but not necessarily a CRUD operation. Each operation has an ENTRY URL(an URL that shows the page to begin the operation). For example, for an USER REGISTRATION operation, the entry URL would be /webapplicationName/moduleName(USER)/featureName(USER MANAGEMENT)/operationName(REGISTER USER). But an operation may need a page flow to be done. For example, an USER REGISTRATION operation would probably need a page with the registration form, an URL (usually mapped to a method) as an action to submit the form and a SUCCESS/ERROR page, to show a SUCCESS/ERROR message.
Permission.java - A class that represents an URL in the system. Each Permission is related to a 1 or many OPERATIONS(Operation.java) to compose a PAGE FLOW. For example: an USER REGISTRATION operation would probably have the following URLs/PERMISSIONS:
/webapplicationName/moduleName(USER)/featureName(USER MANAGEMENT)/operationName(USER REGISTRATION)/register - An URL mapped to a method in a (CompanyNameUser)Controller to submit the form (form action) and to persist in a database usually calling a (CompanyNameUser)DAO
/webapplicationName/moduleName(USER)/featureName(USER MANAGEMENT)/operationName(REGISTER USER)/success/ - an URL mapped to a method in a Controller to show a SUCCESS MESSAGE
/webapplicationName/moduleName(USER)/featureName(USER MANAGEMENT)/operationName(REGISTER USER)/error/ - an URL mapped to a method in a Controller to show an ERROR MESSAGE
ProfilePermission.java - A class that represents a JOIN TABLE for a N-N relationship between Profiles and Permissions.
The problem here is that if I use Spring Security to implement access control I'm doomed to implement an User.java class (I cannot customize the name), also I would need a class for ROLES and other for AUTHORITIES. So, I can't build my own access control flow. I thought of using a SERVLET FILTER to check permissions accept/deny access. However, when I try to redirecting to a URL or just execute chain.doFilter() inside my filter, it just shows ERROR 404. I think that is because I'm using DefaultAnnotationHandlerMapping to handle requests. That said, my config is as following:
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">`<display-name>cheapig</display-name>
<!-- ROOT CONTEXT DEFINITIONS -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>`
<listener>
<listener- class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- The filter to implement my access control -->
<filter>
<filter-name>securityFilter</filter-name>
<filter-class>org.cheapig.security.SecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>securityFilter</filter-name>
<url-pattern>/**</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>cheapig</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet- class> <init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/cheapig/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>cheapig</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping></web-app>
root-context.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
<property name="defaultEncoding" value="latin1"/>
</bean>
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="defaultLocale" value="pt"/>
</bean>
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<ref bean="localeChangeInterceptor" />
</property>
</bean></beans>
servlet-context:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Imports user-defined @Controller beans that process client requests -->
<beans:import resource="controllers.xml" />
<beans:import resource="hibernateMySQL5.xml"/>
<context:component-scan base-package="br.com.cheapig" />
</beans:beans>
SecurityFilter.java:
package br.com.cheapig.security;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class SecurityFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
So, what should I do? Should I use Spring Security? There is any way to implement my own custom process with Spring Security? Should I use an interceptor in my handler mapping instead of using a filter? I would appreciate any help/suggestions.
Thanks in advance!