Как лучше всего реализовать контроль авторизации/доступа в веб-приложении Spring 3 MVC?

Друзья,

Я собираюсь разработать веб-приложение, используя Spring 3 MVC в качестве веб-фреймворка, аннотации Hibernate в качестве фреймворка ORM. Однако я столкнулся с проблемой разработки хорошего контроля доступа на основе базы данных для этого приложения. В моей работе мы привыкли проектировать так:

  • (CompanyName)User.java — класс, означающий Пользователя в системе.

  • Profile.java — класс, который означает РОЛЬ в системе в отношениях NN с (CompanyName)User. . Под РОЛИ я подразумеваю группу пользователей, такую ​​как (АДМИН,АНОНИМНЫЙ,ПОЛЬЗОВАТЕЛЬ СЛУЖБЫ КЛИЕНТА и т. д.)

  • UserProfile.java — класс, означающий связь между Пользователем и Профилем. Он представляет собой JOIN TABLE для отношения N-N в базе данных.

  • Module.java — класс, означающий МОДУЛЬ в веб-приложении. Каждый модуль состоит из неограниченного количества функций, но каждая функция может быть связана только с одним МОДУЛЕМ. Например, функция АУТЕНТИФИКАЦИЯ ПОЛЬЗОВАТЕЛЯ будет связана с модулем БЕЗОПАСНОСТЬ или АУТЕНТИФИКАЦИЯ. Модули — это контроллеры в приложении, помеченные тегом @Controller.

  • Feature.java — класс, представляющий ФУНКЦИЮ в приложении. Каждая функция состоит из одной или нескольких операций. Например, УПРАВЛЕНИЕ ПОЛЬЗОВАТЕЛЯМИ — это ФУНКЦИЯ. Таким образом, он состоит из множества операций (например, СОЗДАНИЕ, ЧТЕНИЕ, ОБНОВЛЕНИЕ и УДАЛЕНИЕ ПОЛЬЗОВАТЕЛЯ). Кроме того, ФУНКЦИЯ имеет URL-адрес ВХОДА, который представляет собой URL-адрес этой функции (для перенаправления пользователя на эту функцию при нажатии кнопки/ссылки). Каждый URL-адрес сопоставляется с методом в модуле (контроллере).

  • Operation.java — класс, представляющий OPERATION в веб-приложении. Операция — это, по сути, одиночная/базовая операция, такая как РЕГИСТРАЦИЯ ПОЛЬЗОВАТЕЛЯ или УДАЛЕНИЕ ПОЛЬЗОВАТЕЛЯ, но не обязательно операция CRUD. У каждой операции есть URL-ВВОД (URL-адрес, показывающий страницу, с которой начинается операция). Например, для операции РЕГИСТРАЦИЯ ПОЛЬЗОВАТЕЛЯ запись URL будет иметь вид /webapplicationName/moduleName(USER)/featureName(USER MANAGEMENT)/operationName(REGISTER USER) . Но для операции может потребоваться поток страниц. Например, для операции РЕГИСТРАЦИЯ ПОЛЬЗОВАТЕЛЯ, вероятно, потребуется страница с регистрационной формой, URL-адрес (обычно сопоставленный с методом) в качестве действия для отправки формы и УСПЕХ/ОШИБКА. strong>, чтобы отобразить сообщение SUCCESS/ERROR.

  • Permission.java — класс, представляющий URL в системе. Каждое Разрешение связано с 1 или несколькими ОПЕРАЦИЯМИ (Operation.java) для создания ПОТОКА СТРАНИЦ. Например: операция РЕГИСТРАЦИЯ ПОЛЬЗОВАТЕЛЯ, вероятно, будет иметь следующие URL/РАЗРЕШЕНИЯ:

    • /webapplicationName/moduleName(USER)/featureName(USER MANAGEMENT)/operationName(USER REGISTRATION)/register — URL-адрес, сопоставленный с методом в (CompanyNameUser)Controller для отправки формы (действие формы) и для сохранения в базе данных, обычно вызывающей (CompanyNameUser)DAO

    • /webapplicationName/moduleName(USER)/featureName(USER MANAGEMENT)/operationName(REGISTER USER)/success/ — URL-адрес, сопоставленный с методом в контроллере для отображения СООБЩЕНИЯ ОБ УСПЕХЕ

    • /webapplicationName/moduleName(USER)/featureName(USER MANAGEMENT)/operationName(REGISTER USER)/error/ — URL-адрес, сопоставленный с методом в контроллере для отображения СООБЩЕНИЯ ОБ ОШИБКЕ

  • ProfilePermission.java — класс, представляющий JOIN TABLE для связи N-N между профилями и разрешениями.

Проблема здесь в том, что если я использую Spring Security для реализации контроля доступа, я обречен реализовать класс User.java (я не могу настроить имя), также мне понадобится класс для РОЛИ и другой для ПОЛНОМОЧИЙ. Итак, я не могу создать свой собственный поток управления доступом. Я подумал об использовании SERVLET FILTER для проверки разрешений на доступ/запрет доступа. Однако, когда я пытаюсь перенаправить на URL-адрес или просто выполнить chain.doFilter() внутри моего фильтра, он просто показывает ОШИБКУ 404. Я думаю, это потому, что я использую DefaultAnnotationHandlerMapping для обработки запросов. Тем не менее, моя конфигурация выглядит следующим образом:

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
<?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>
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>

контекст сервлета:

    <?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
}
 }

И что я должен делать? Должен ли я использовать Spring Security? Есть ли способ реализовать мой собственный процесс с помощью Spring Security? Должен ли я использовать перехватчик в моем отображении обработчика вместо использования фильтра? Буду признателен за любую помощь/предложения.

Заранее спасибо!


person jguilhermemv    schedule 01.04.2012    source источник


Ответы (2)


Взгляните на Apache Shiro. Это может быть лучше подходит для ваших требований.

person sourcedelica    schedule 01.04.2012
comment
Спасибо. Я посмотрю на это. Чтобы знать, я буду только помечать это как полезное. - person jguilhermemv; 02.04.2012

Это очень поздний ответ, но здесь речь идет о будущих поколениях.

Я согласен с sourcedelica, что Apache Shiro — хорошее место для поиска. Однако, кроме того, я бы порекомендовал вам сделать шаг назад и взглянуть на теорию, лежащую в основе вашей проблемы. По сути, вы пытаетесь реализовать модель управления доступом на основе ролей с пользователями, группами, ролями и разрешениями. Кроме того, из вашего вопроса следует, что у вас есть отношения между пользователями, запрашивающими доступ, и целевыми ресурсами.

Имея это в виду, вам нужен ABAC — управление доступом на основе атрибутов — как определено NIST в их отчете, опубликованном ранее в 2014 году. Вы можете прочитайте отчет здесь.

С помощью ABAC вы можете описывать своих пользователей с помощью атрибутов — любых атрибутов, таких как роль, местоположение, возраст, гражданство... Точно так же вы можете описывать таким образом ресурсы и объекты, а также предпринятые действия и контекст.

Это означает, что с помощью атрибутов и ABAC вы можете легко реализовать следующие требования авторизации:

  • пользователь с ролью==редактор может выполнять действие==редактирование ресурсов типа==документ тогда и только тогда, когда пользователь находится в том же отделе, что и ресурсы (user.department==resource.department)
  • пользователь может выполнить action==delete для ресурса type==document тогда и только тогда, когда пользователь владеет документом (user.id==document.owner.id) и статус==draft.

Чтобы внедрить ABAC, используйте XACML. XACML — это расширяемый язык разметки управления доступом, определенный OASIS. Он предоставляет вам:

  • схема запрос/ответ (как задавать вопросы и получать решения),
  • язык политики (конечно, основанный на атрибутах) и
  • архитектура.

Архитектура XACML

Гугли вокруг. Существует множество ресурсов XACML для Java.

Преимущества использования ABAC и XACML заключаются в том, что вы в конечном итоге отделяете свой контроль доступа от своей бизнес-логики, что означает, что вы можете изменять свое приложение независимо от своей логики авторизации и наоборот.

HTH

person David Brossard    schedule 19.11.2014