Статьи

Использование Spring Security для обеспечения аутентификации и авторизации в сервисах Spring Remoting, вызываемых из клиента Java SE

Среда Spring — одна из самых больших и всесторонних сред, которую может использовать сообщество Java, чтобы покрыть большую часть сквозных требований к программной системе, когда дело доходит до реализации.
Spring Security и Spring Remoting являются двумя важными частями каркаса, который охватывает безопасность описательным образом и позволяет нам удаленно вызывать методы Spring Bean с использованием локального прокси.

В этой записи я покажу вам, как мы можем использовать Spring Security для защиты Spring bean, предоставляемого по HTTP, и вызывать его защищенные методы из автономного клиента. В нашем примере мы разрабатываем сервис Spring, который возвращает список ролей, назначенных этому аутентифицированному пользователю. Я разработаю простое веб-приложение, связанное с защищенным сервисом Spring, а затем разработаю простой клиент Java SE для вызова этого защищенного сервиса.

Для разработки сервиса нам нужен интерфейс сервиса, который выглядит следующим образом:

package springhttp;
public interface SecurityServiceIF {
public String[] getRoles();
}

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

package springhttp;
public class SecurityService implements SecurityServiceIF {

public String[] getRoles() {
Collection<GrantedAuthority> col = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
String[] roles = new String[col.size()];
int i = 0;
for (Iterator<GrantedAuthority> it = col.iterator(); it.hasNext();) {

GrantedAuthority grantedAuthority = it.next();
roles[i] = grantedAuthority.getAuthority();
i++;
}
return roles;
}
}

Теперь мы должны определить этот удаленный сервис в файле дескриптора. Здесь мы будем использовать файл remote-servlet.xml для описания сервиса. Файл может быть размещен внутри WEB-INF веб-приложения. Содержимое файла выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

<bean name="/securityService"
class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="serviceInterface" value="springhttp.SecurityServiceIF" />
<property name="service" ref="securityService" />
</bean>

</beans>

Мы просто используем / securityService в качестве относительного URL для представления нашей реализации SecurityService .

Следующий файл конфигурации — это контекст приложения Spring, в котором мы определяем все наши bean-компоненты, ткачество bean-компонентов и конфигурации. Имя файла — applicationContext.xml, и оно находится в каталоге WEB-INF .

<?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"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd ">

<bean id="securityService" class="springhttp.SecurityService">
</bean>

<security:http realm="SecRemoting">
<security:http-basic/>
<security:intercept-url pattern="/securityService" access="ROLE_ADMIN" />
</security:http>

<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:user-service id="uds">
<security:user name="Jami" password="Jami"
authorities="ROLE_USER, ROLE_MANAGER" />
<security:user name="bob" password="bob"
authorities="ROLE_USER,ROLE_ADMIN" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>


<bean id="digestProcessingFilter"
class="org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
<property name="userDetailsService" ref="uds" />
<property name="authenticationEntryPoint"
ref="digestProcessingFilterEntryPoint" />
</bean>

<bean id="digestProcessingFilterEntryPoint"
class="org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
<property name="realmName" value="ThisIsTheDigestRealm" />
<property name="key" value="acegi" />
<property name="nonceValiditySeconds" value="10" />
</bean>

<bean id="springSecurityFilterChain"
class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map path-type="ant">
<security:filter-chain pattern="/**"
filters="httpSessionContextIntegrationFilter,digestProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor" />
</security:filter-chain-map>
</bean>

<bean id="httpSessionContextIntegrationFilter"
class="org.springframework.security.web.context.HttpSessionContextIntegrationFilter" />
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
<property name="decisionVoters">
<list>

<bean class="org.springframework.security.access.vote.RoleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</property>
</bean>

<bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint"
ref="digestProcessingFilterEntryPoint" />
</bean>
</beans>

Большая часть приведенного выше кода является конфигурацией пружин по умолчанию, за исключением следующих частей, которые я собираюсь объяснить более подробно. Первый фрагмент определяет сам компонент:

<bean id="securityService" class="springhttp.SecurityService">
</bean>

Вторая часть — это когда мы указываем ограничения безопасности для самого приложения. Мы инструктируем весеннюю безопасность, чтобы позволить только определенной роли вызывать securityService

<security:http realm="SecRemoting">
<security:http-basic/>
<security:intercept-url pattern="/securityService" access="ROLE_ADMIN" />
</security:http>

Третья часть — это когда мы определяем хранилище идентификаторов, где хранятся наши пользователи и назначения ролей.

<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:user-service id="uds">
<security:user name="jimi" password="jimi"
authorities="ROLE_USER, ROLE_MANAGER" />
<security:user name="bob" password="bob"
authorities="ROLE_USER,ROLE_ADMIN" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>

Как вы видите, мы используем простую пользовательскую службу в памяти, вы можете настроить LDAP, JDBC или пользовательскую службу в производственной среде. Все остальные части applicationContext.xml являются определением фильтра безопасности Spring. Вы можете найти объяснение любой из них в Spring Security Documentation или по поиску. Чтобы обеспечить ограничения безопасности для SecurityService для любого локального вызова, мы можем просто изменить первый приведенный фрагмент кода, как показано ниже, чтобы ROLE_ADMIN и ROLE_MANAGER вызывали локальный сервис.

<bean id="securityService" class="springhttp.SecurityService">
<security:intercept-methods>
<security:protect
method="springhttp.SecurityService.getRoles" access="ROLE_MANAGER" />
</security:intercept-methods>
</bean>

Теперь, когда у нас есть все конфигурационные файлы Spring, нам нужно добавить некоторые элементы в web.xml , чтобы запустить Spring Framework. Следующие изменения должны быть включены в файл web.xml .

 

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<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>
<servlet>
<servlet-name>remote</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>remote</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

Теперь, когда серверная часть закончена, мы можем разработать клиентское приложение для вызова этой службы, которую мы разработали. Код для клиентского класса выглядит следующим образом:

package client;
public class Main {

public static void main(final String[] arguments) {
final ApplicationContext context =
new ClassPathXmlApplicationContext(
"client/spring-http-client-config.xml");
String user = "bob";
String pw = "bob";
SecurityContextImpl sc = new SecurityContextImpl();
Authentication auth = new UsernamePasswordAuthenticationToken(user,
pw);

sc.setAuthentication(auth);
SecurityContextHolder.setContext(sc);

String[] roles = ((SecurityServiceIF) context.getBean("securityService")).getRoles();
for (int i = 0; i < roles.length; i++) {
System.out.println("Role:" + roles[i]);
}

}
}

Как видите, мы инициализируем контекст приложения с помощью файла XML. Мы обсудим этот XML-файл через несколько минут. После инициализации контекста приложения мы создаем SecurityContextImpl и UsernamePasswordAuthenticationToken,   затем передаем токен в контекст безопасности и, наконец, используем этот контекст безопасности, чтобы передать его на сервер для аутентификации и дальнейшей авторизации.

Наконец, мы вызываем метод getRoles () нашего SecurityService, который возвращает набор ролей, назначенных текущему аутентифицированному пользователю, и печатает его роль в выходном потоке по умолчанию.

Теперь содержимое spring-http-client-config.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"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

<bean id="securityService"
class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl" value="http://localhost:8084/sremoting/securityService" />
<property name="serviceInterface" value="client.SecurityServiceIF" />
<property name="httpInvokerRequestExecutor">
<bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />
</property>
</bean>
</beans>

Как вы можете видеть, мы просто устанавливаем некоторые свойства, такие как имя компонента serviceURL , httpInvokerRequestExecutor и так далее. единственное, что вам может понадобиться изменить для запуска примера, это serviceUrl .
Вы можете скачать полную форму примера кода здесь . Это два проекта NetBeans , веб-приложение и Java SE. Возможно, вам придется добавить библиотеки Spring в проект перед любой другой попыткой.

Реализация SecurityService заимствована из блога Элвиса, доступного по адресу http://elvisfromhell.blogspot.com/2008/06/this-was-hard-one.html.

От http://weblogs.java.net/blog/kalali/archive/2010/03/18/using-spring-security-enforce-authentication-and-authorization-spring