Среда исполненияДля Spring Security 3.0 требуется среда исполнения Java 5.0 или выше. Т.к. Spring Security стремится работать автономно, то нет никакой необходимости размещать специальные файлы-конфигурации в Java Runtime Environment. В частности, нет необходимости специально настраивать файл политики Java Authentication and Authorization Service (JAAS) или помещать Spring Security в общий CLASSPATH. Аналогичным образом, если вы используете EJB контейнер или контейнер сервлета, то нет необходимости создавать где-либо специальные конфигурационные файлы или включать Spring Security в загрузчик классов сервера. Все необходимые файлы будут содержаться в вашем приложении. Такая конструкция обеспечивает максимальную гибкость во время развертывания, так как вы можете просто скопировать целевой артефакт (JAR, WAR или EAR) из одной системы в другую, и он будет работать. Ключевые компонентыВ Spring Security 3.0, содержимое jar-файла spring-security-core было урезано до минимума. Он не содержит никакого кода, связанного с безопасностью веб-приложений, LDAP или конфигурирования с помощью пространства имен. Здесь мы рассмотрм некоторые Java типы, которые можно найти в основном модуле. Они представляют собой строительные блоки каркаса. Так что если вам когда-нибудь понадобится выйти за рамки простой конфигурации пространства имен, то важно чтобы вы понимали что они из себя представляют, даже если вам не потребуется взаимодействовать с ними напрямую. Объекты SecurityContextHolder, SecurityContext и AuthenticationСамым фундаментальным явлется SecurityContextHolder. В нем мы храним информацию о текущем контексте безопасности приложения, который включает в себя подробную информацию о доверителе (принципале/пользователе) работающем в настоящее время с приложением. По умолчанию SecurityContextHolder использует ThreadLocal для хранения такой информации, что означает, что контекст безопасности всегда доступен для методов исполняющихся в том же самом потоке, даже если контекст безопасности явно не передается в качестве аргумента этих методов. Использование ThreadLocal таким образом, вполне безопасно, если принимаются меры для очистки потока после завершения обработки запроса текущего доверителя. Естественно Spring Security делает это за вас автоматически, поэтому нет необходимости беспокоиться об этом. В некоторых приложениях использование ThreadLocal не является удачным решением, в силу их специфики работы с потоками. Например, клиент Swing может захотеть чтобы все потоки Java-машины использовали один и тот же контекст безопасности.SecurityContextHolder может быть настроен во время запуска с помощью специальной стратегии, чтобы вы могли определить как будет храниться контекст безопасности. Для одиночного приложения вы можете использовать стратегиюSecurityContextHolder.MODE_GLOBAL. Другие приложения могут захотеть иметь потоки порожденные от одного защищенного потока, предполагая наличие аналогичной безопасности. Это достигается путем использованияSecurityContextHolder.MODE_INHERITABLETHREADLOCAL. Вы можете изменить режим по умолчаниюSecurityContextHolder.MODE_THREADLOCAL двумя способами. Первый, установить соответствующее свойство системы, второй, вызовать статический метод класса SecurityContextHolder. Большинству приложений не требуется менять значение по умолчанию, в противном случае, чтобы узнать больше, изучите Javadocs для SecurityContextHolder. Получение информации о текущем пользователеВнутри SecurityContextHolder мы храним информацию о доверителе, взаимодействующим в настоящее время с приложением. Spring Security использует объект Authentication для представления этой информации. Как правило нет необходимости создавать объект Authentication самостоятельно, но запросы к объекту Authentication довольно распространенное действие. Вы можете использовать следующий код в любом месте вашего приложения, чтобы получить имя текущего зарегистрированного пользователя, например: Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
Вызов getContext() возвращает экземпляр интерфейса SecurityContext. Это объект, который храниться в ThreadLocal. Как мы увидим ниже, большинство механизмов аутентификации в рамках Spring Security возвращают экземпляр UserDetails в качестве принципала. Сервис UserDetailsЭто еще один пункт на который стоит обратит внимание в приведенном выше фрагменте кода, в котором мы можем получить принципала из объекта Authentication. Принципал это просто Object. В большинстве случаев это может быть приведен к объекту UserDetails. UserDetails является одним из центральных интерфейсов Spring Security. Он представляет собой принципала, но в расширенном виде и с учетом специфики приложения. Думайте о UserDetails как об адаптере между вашей собственной БД пользователей и тем что требуется Spring Security внутри SecurityContextHolder. Будучи представлением чего-то из вашей БД пользователей, UserDetails довольно часто приводится к исходному объекту Вашего приложения, для того, чтобы вызвать бизнес-методы (такие как, getEmail(), getEmployeeNumber() и т. п.). Возможно вы удивлены, когда я предоставил UserDetails объект? Как я это сделал? Я думаю, вы сказали, это было сделано декларативным способом и мне не потребовалось писать никакого Java кода — в этом дело? Короткий ответ заключается в том, что существует специальный интерфейс, называемый UserDetailsService. Единственный метод этого интерфейса принимает имя пользователя в виде String и возвращает UserDetails: UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
Это наиболее общий подход к загрузке информации о пользователе в Spring Security и вы увидите, что это используется в каркасе когда требуется информация о пользователе. В случае успешной аутентификации, UserDetails используется для создания Authentication объекта, который хранится вSecurityContextHolder (подробнее об этом ниже). Хорошая новость заключается в том, что мы предлагаем целый ряд реализаций UserDetailsService, одна из них хранит данные в памяти (InMemoryDaoImpl), а другая использует JDBC (JdbcDaoImpl). Большинство пользователей, как правило пишут свои собственные реализации, поверх существующих объектов доступа к данным (DAO), который представляют их сотрудников, клиентов и других пользователей приложения. Помните то преимущество, что независимо от вашего UserDetailsService эти же данные можно получить из SecurityContextHolder с помощью приведенного выше фрагмента кода. GrantedAuthorityКроме принципала, еще одним важным методом, предоставляемым объектом Authentication, явлется getAuthorities(). Этот метод предоставляет массив объектов GrantedAuthority. Очевидно, что GrantedAuthority это полномочия, которые предоставляются доверителю. Такие полномочия (как правило называемые "роли"), как ROLE_ADMINISTRATOR илиROLE_HR_SUPERVISOR. Эти роли в дальнейшем настраиваются для веб-авторизации, авторизации методов и хранимых объектов. Другие части Spring Security способны интерпретировать эти полномочия, и ожидают их наличия. Объекты GrantedAuthority как правило загружаются с помощью UserDetailsService. Обычно областью видимости для разрешений, выдаваемых объектами GrantedAuthority, является все приложение, а не отдельная предметная область. Таким образом, вы не можете иметь GrantedAuthority, который будет представлять собой разрешение для объекта Employee номер 54, потому что, если будут существовать тысячи таких полномочий, то память быстро кончится (или, по крайней мере, приведет к тому что идентификация пользователя будет занимать слишком много времени). Конечно Spring Security спроектирована так, чтобы удовлетворять самым широким потребностям, но для этих целей лучше воспользоваться специальными возможностями, которые предоставляются для обеспечения безопасности бизнес-объектов. РезюмеПросто напомним, основными блоками Spring Security являются: SecurityContextHolder, чтобы обеспечить доступ к SecurityContext.SecurityContext, содержит объект Authentication и в случае необходимости информацию системы безопасности, связанную с запросом.Authentication представляет принципала с точки зрения Spring Security.GrantedAuthority отражает разрешения выданные доверителю в масштабе всего приложения.UserDetails предоставляет необходимую информацию для построения объекта Authentication из DAO объектов приложения или других источника данных системы безопасности.UserDetailsService, чтобы создать UserDetails, когда передано имя пользователя в виде String (или идентификатор сертификата или что-то подобное).
Теперь, когда у вас есть понимание многократно используемых компонентов, давайте внимательнее рассмотрим на процесс аутентификации. [править]АутентификацияSpring Security можно использовать в самых разных средах аутентификации. Хотя мы рекомендуем использовать Spring Security для аутентификации, а не интегрировать ее с существующей Container Managed Authentication, хотя этот механизм и поддерживается. То же касается и интеграции с вашей собственной системой аутентификации. Что представляет собой аутентификация в Spring SecurityРассмотрим стандартный сценарий аутентификации с которым все хорошо знакомы. - Пользователю будет предложено войти в систему с именем пользователя и паролем.
- Система (успешно) подтверждает, что введен правильный пароль для данного имени пользователя.
- Получается контекстная информация для данного пользователя (список ролей и т. д.).
- Для пользователя устанавливается контекст безопасности.
Пользователь перенаправляется на выполнение операций, которые могут быть защищены механизмом контроля доступа, который проверяет необходимые разрешения для выполнения операций на основании информации текущего контекста безопасности. Первые три пункта составляют непосредственно процесс аутентификации, поэтому мы рассмотрим, как это происходит в Spring Security. - Получаются имя пользователя и пароль и объединяются в экземпляр класса
UsernamePasswordAuthenticationToken(экземпляр интерфейса Authentication, который мы видели ранее). - Токен передается экземпляру
AuthenticationManager для проверки. AuthenticationManager возвращает полностью заполненный экземпляр Authentication в случае на успешной аутентификации.- Устанавливается контекст безопасности путем вызова
SecurityContextHolder.getContext().SetAuthentication(...), куда передается вернувшийся экземплярAuthentication.
С этого момента пользователь считается подлинным. Давайте рассмотрим код в качестве примера. import org.springframework.security.authentication.*;
import org.springframework.security.core.*;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContextHolder;
public class AuthenticationExample {
private static AuthenticationManager am = new SampleAuthenticationManager();
public static void main(String[] args) throws Exception {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while(true) {
System.out.println("Please enter your username:");
String name = in.readLine();
System.out.println("Please enter your password:");
String password = in.readLine();
try {
Authentication request = new UsernamePasswordAuthenticationToken(name, password);
Authentication result = am.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
break;
} catch(AuthenticationException e) {
System.out.println("Authentication failed: " + e.getMessage());
}
}
System.out.println("Successfully authenticated. Security context contains: " +
SecurityContextHolder.getContext().getAuthentication());
}
}
class SampleAuthenticationManager implements AuthenticationManager {
static final List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();
static {
AUTHORITIES.add(new GrantedAuthorityImpl("ROLE_USER"));
}
public Authentication authenticate(Authentication auth) throws AuthenticationException {
if (auth.getName().equals(auth.getCredentials())) {
return new UsernamePasswordAuthenticationToken(auth.getName(),
auth.getCredentials(), AUTHORITIES);
}
throw new BadCredentialsException("Bad Credentials");
}
}
Здесь мы написали маленькую программу, которая предлагает пользователю ввести имя и пароль, и выполняет выше описанные действия. AuthenticationManager, которые мы реализовали здесь, идентифицирует любого пользователя у которго имя совпадает с паролем. Для каждого пользователя назначается одна роль. Выше приведенный код выведет на экран нечто подобное: Please enter your username:
bob
Please enter your password:
password
Authentication failed: Bad Credentials
Please enter your username:
bob
Please enter your password:
bob
Successfully authenticated. Security context contains: \
org.springframework.security.authentication.UsernamePasswordAuthenticationToken@441d0230: \
Principal: bob; Password: [PROTECTED]; \
Authenticated: true; Details: null; \
Granted Authorities: ROLE_USER
Обратите внимание, что обычно не нужно писать код, подобный этому. Обычно этот процесс происходит где-то внутри, например в фильтре веб аутентификации. Мы включили этот код, только для того, чтобы показать, что вопрос что на самом деле представляет собой аутентификация в Spring Security имеет довольно простой ответ. Пользователь считается идетифицированным (подтвержденным) когда SecurityContextHolder содержит полностью заполненный объект Authentication.
Аутентификация в Веб-приложениях Теперь давайте исследуем ситуацию, где вы используете Spring Security в веб-приложении (система безопасности в web.xml выключена). Как аутентифицируется пользователь и устанавливается контекст безопасности приложения? Рассмотрим типичный процесс аутентификации веб-приложения - Вы заходите на главную страницу и нажимаете на ссылку.
- Запрос идет на сервер и сервер решает что вы запрашиваете защищенный ресурс.
- Т.к. Ваша подлинность в настоящее не подтверждена, то сервер отправляет ответ о том, что вы должны пройти процедуру аутентификации. Ответом будет либо код HTTP, либо перенаправление на особую веб-страницу.
- В зависимости от механизма аутентификации, браузер либо перенаправит вас на специальную веб-страницу, где можно будет заполнить форму, либо браузер каким-то другим образом получит данные подтверждающие подлинность Вашей личности (через базовый диалог аутентификации, куки, сертификат X.509 и т.п.).
- Браузер отошлет ответ на сервер. Это будет либо HTTP POST, с содержимым формы, которую вы заполнили, либо HTTP заголовок, содержащий ваши данные аутентификации.
- Далее сервер будет решать, является ли «удостоверение личности» действительными. Если оно действительно, то будет выполнен следующий шаг. Если же оно недействительным, то как правило браузер предложит попробовать идентифицироваться еще раз (т.е. вы вернетесь к шагу 2).
- Оригинальный запрос, который привел к запуску процедуры аутентификации, будет повторен. Надеемся, что вы имеете достаточно полномочий для доступа к защищенному ресурсу. Если вы имеете соответствующие права доступа, то запрос будет успешным. В противном случае вам будет возвращен код ошибки 403, что означает "запрещено".
Spring Security имеет явно реализованные классы отвечающие за большинство шагов, описанных выше. Основные участники (в порядке их использования)
var container = document.getElementById('nativeroll_video_cont');
if (container) {
var parent = container.parentElement;
if (parent) {
const wrapper = document.createElement('div');
wrapper.classList.add('js-teasers-wrapper');
parent.insertBefore(wrapper, container.nextSibling);
}
}
|