Перейти к содержимому

Spring security что это

  • автор:

Spring security что это

Spring Security — это мощный высоконастраиваемый фреймворк аутентификации и контроля доступа. Он является де-факто стандартом для обеспечения безопасности в Spring-приложениях.

Введение

Spring Security — это фреймворк, который сфокусирован на обеспечение как аутентификации, так и авторизации в Java-приложениях. Как и все Spring проекты, настоящая сила Spring Security в том, что он может быть легко дополнен нужным функционалом.

Возможности

  • Комплексная и расширяемая поддержка как аутентификации, так и авторизации
  • Защита от атак типа фиксация сессии, кликджекинг, межсайтовая подделка запроса и др.
  • Интеграция с Servlet API
  • Интеграция с Spring Web MVC при необходимости
  • Многое другое.

Быстрый старт

Рекомендованный путь для начального использования spring-security в вашем проекте с использованием системы управления зависимостями – скопировать фрагмент кода ниже и вставить в вашу конфигурацию сборки. Нужна помощь? Ознакомьтесь с нашими руководствами по созданию приложений с использованием Maven и Gradle.

  org.springframework.security spring-security-web 3.2.5.RELEASE   

Spring Security

Spring Security — это фреймворк, который сфокусирован на обеспечение как аутентификации, так и авторизации в Java-приложениях. Как и все Spring проекты, настоящая сила Spring Security в том, что он может быть легко дополнен нужным функционалом.

  • Комплексная и расширяемая поддержка как аутентификации, так и авторизации
  • Защита от атак типа фиксация сессии, кликджекинг, межсайтовая подделка запроса и др.
  • Интеграция с Servlet API
  • Интеграция с Spring Web MVC при необходимости
  • Многое другое…
Общий раздел Spring Security
Быстрый старт в Spring Security
Подключение базы данных пользователей, настройка групп ролей пользователей в Spring Security
Аутентификация пользователя с помощью функции «Remember me» (запомнить меня)
Защита методов method security

Spring Security – защита методов (method security) — пример защиты методов с помощью аннотаций @Secured , @PreAuthorize .

Хеширование паролей в Spring Security
Вопросы для прохождения интервью на позицию Java разработчик
  • Собеседование по Java EE — Spring Framework (вопросы и ответы). Часть 1
  • Собеседование по Java EE — Spring Framework (вопросы и ответы). Часть 2
Официальная документация Spring Security

Share Button

24472 Total Views 5 Views Today
Views: 21 694

Добавить комментарий Отменить ответ

Для отправки комментария вам необходимо авторизоваться.

Подписывайтесь

Нашли ошибку?

Выделите и нажмите CTRL+ENTER 🙂

Copyright © JavaStudy — руководство для java разработчика All Rights Reserved.

Powered by WordPress & Lightning Theme by Vektor,Inc. technology.

Краткий обзор Spring Security, создание проекта и подключение зависимостей

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

Детям из Мариуполя нужно 120 ноутбуков для обучения — подари старое «железо», пусть оно работает на будущее Украины

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

Spring Security

1. Основные возможности

Шифрование паролей

Spring Security не ограничивается аутентификацией. Фреймворк также помогает решить проблему с безопасным хранением паролей.

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

Курс Аналітик даних.

Протягом 4 місяців ви вивчите повний набір інструментів для аналізу даних та отримаєте можливість працевлаштування в Laba Group.

Аутентификация In-Memory

Для сохранения информации о пользователях и выполнения аутентификации можно использовать временную базу данных, которая остается в оперативной памяти приложения.

Это полезно при разработке и тестировании. Реальная база данных при таком подходе остается нетронутой.

LDAP-аутентификация

Lightweight Directory Access Protocol (LDAP) — протокол аутентификации учетных записей пользователей в организациях. Позволяет определять структуру пользователей и групп пользователей, назначать им права доступа.

Дополнительную информацию вы можете получить в официальном руководстве по использованию аутентификации LDAP.

Управление сессией

Spring Security предоставляет механизмы для управления сеансом пользователя. Он создает эти механизмы контроля при входе в систему и уничтожает при выходе.

Для обеспечения безопасности доступны дополнительные средства, которые помогают, например, избежать отслеживания сеансов.

Remember Me Authentication

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

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

OAuth 2.0

Open Authorization 2.0 — открытый стандарт проверки прав пользователя с помощью сервиса авторизации. Он также используется для реализации таких функций, как вход через учетные записи Facebook, Google и других крупных площадок.

Курс Англійської.
Подолай мовний бар’єр та розширюй коло друзів на розмовних клубах.

Настройка сервера авторизации и внедрение OAuth сопряжены с высоким риском и могут отнять много времени. В этом процессе легко ошибиться и создать уязвимость. Spring Security предлагает защитить сайт с помощью готовых инструментов — например, Auth0.

Это лишь некоторые возможности аутентификации и авторизации через Spring Security. Если хотите узнать больше, смотрите документацию Spring Boot.

Ознакомьтесь с курсами от наших партнеров, которые помогут вам повысить свой уровень в среде разработки приложений, от Mate Academy и Hillel. Практикующие преподаватели и единомышленники, это именно то что вам нужно для развития.

2. Пример подключения Spring Security

Посмотрим на практике, какие настройки включаются по умолчанию при добавлении в проект Spring Security.

Подготовка

Сгенерируйте на Spring Initializr приложение с зависимостью Web :

 org.springframework.boot spring-boot-starter-web  

Напишите REST -контроллер:

@RestController public class HelloController < @GetMapping("/api/hello") public String hello()< return "Highload"; >>

Проверьте приложение в браузере. Сейчас доступ к нему имеют все пользователи.

Подключение Spring Security

Добавьте Maven -зависимость, чтобы подключить Spring Security:

 org.springframework.boot spring-boot-starter-security  

Проверьте приложение снова. Теперь при переходе по первоначальному адресу http://localhost:8080/api/hello мы попадем на страницу для ввода логина и пароля — http://localhost:8080/login . Здесь размещена форма входа. Введите в нее любые данные и нажмите Sign in — появится сообщение об ошибке.

Приложение уже выполняет проверку пользователей, хотя вы еще даже ничего не настраивали.

Курс Управління командою в бізнесі.

Онлайн-курс для ефективного управління командою, спрямований на створення проактивних та самостійних команд, де мікроменеджмент не потрібний.

Проверка изменений

Spring Security после подключения добавляет своего пользователя. Его имя user, пароль генерируется при запуске. Кроме того, в проекте при добавлении зависимости:

  1. формируется страница, на которой размещена форма для ввода имени и пароля;
  2. сразу начинает работать проверка имени и пароля;
  3. все URL недоступны, пока вы не зайдете в систему под именем зарегистрированного пользователя.

Кроме страницы для входа создается страница для выхода из аккаунта. Она находится по адресу /logout .

Настройка аутентификации

Spring Security использует Form-Based-аутентификацию. Пользователь отправляет имя и пароль через форму. Сервер берет эти данные из запроса как POST-параметры.

Теперь посмотрим, как сервер хранит данные пользователей. Интересно, что они находятся не в базе, не на LDAP-сервере, а в оперативной памяти приложения. И хранятся там до тех пор, пока оно запущено. Такая аутентификация называется In-Memory authentication.

Чтобы отредактировать пользователя, необходимо заново запустить приложение. Для продакшена такой вариант не подходит, но для экспериментов в процессе разработки это простой и полезный метод.

Приложение хранит имя и пароль в памяти. Вы можете посмотреть эти данные через консоль. Чтобы каждый раз не лезть в консоль, воспользуйтесь файлом конфигурации application.yml . Зафиксируйте в нем имя и пароль пользователя.

spring: security: user: name: highload password: myhighload

Теперь пароль не будет каждый раз генерироваться заново. Аналогичным образом можно задать других пользователей. Это полезно для будущей проверки разграничения прав доступа.

Чтобы настроить In-Memory-аутентификацию, создайте класс SecurityConfig , который расширяет класс @EnableWebSecurity . Вот полный код:

@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter < @Bean public PasswordEncoder passwordEncoder() < return NoOpPasswordEncoder.getInstance(); >@Override public void configure(AuthenticationManagerBuilder auth) throws Exception < auth.inMemoryAuthentication() .withUser("highload") .password("myhighload") .authorities("ROLE_USER") .and() .withUser("highload2") .password("myhighload2") .authorities("ROLE_USER"); >>

Теперь — пояснения. Здесь вы переопределяете метод configure AuthenticationManagerBuilder auth класса WebSecurityConfigurerAdapter, получаете доступ к билдеру AuthenticationManagerBuilde и уже через него настраиваете нужный AuthenticationManager.

В билдере нужно задать тип аутентификации. В этом примере используется In-Memory — auth.inMemoryAuthentication() . Но может быть другой вариант — Jdbc, LDAP, кастомная аутентификация. Тип аутентификации определяет, где хранится пользователь.

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

AuthenticationManager достает не только имя и пароль, но и разрешения пользователя — authorities( «ROLE_USER» ).

Что в итоге делает приведенный выше фрагмент кода:

  1. Сравнивает переданные имя и пароль со значениями имени и пароля, которые хранятся в оперативной памяти.
  2. При совпадении данные аутентификация проходит успешно.

Обратите также внимание на бин PasswordEncoder. В нем указано, как шифровать пароль. В приведенном выше примере пароль не шифруется. Так можно делать только на учебных проектах для наглядности. В реальном приложении NoOpPasswordEncoder не используется.

3. Создание проекта и подключение зависимостей

Итак, мы готовы, чтобы написать обещанное простое приложение с регистрацией и входом в учетную запись пользователя. Используемые технологии: Spring Boot, Spring Security, Spring Data JPA, Hibernate, MySQL, JSP, Bootstrap и Docker Compose.

Создание проекта

Структура проекта должна выглядеть так:

├── src │ └── main │ ├── java │ │ └── com │ │ └── hellokoding │ │ └── auth │ │ ├── model │ │ │ ├── Role.java │ │ │ └── User.java │ │ ├── repository │ │ │ ├── RoleRepository.java │ │ │ └── UserRepository.java │ │ ├── service │ │ │ ├── SecurityServiceImpl.java │ │ │ ├── SecurityService.java │ │ │ ├── UserDetailsServiceImpl.java │ │ │ ├── UserServiceImpl.java │ │ │ └── UserService.java │ │ ├── validator │ │ │ └── UserValidator.java │ │ ├── web │ │ │ └── UserController.java │ │ ├── WebApplication.java │ │ └── WebSecurityConfig.java │ ├── resources │ │ ├── application.properties │ │ └── validation.properties │ └── webapp │ ├── resources │ │ ├── css │ │ │ ├── bootstrap.min.css │ │ │ └── common.css │ │ └── js │ │ └── bootstrap.min.js │ ├── login.jsp │ ├── registration.jsp │ └── welcome.jsp ├── Dockerfile ├── docker-compose.yml └── pom.xml

Добавление зависимостей

Содержимое файла pom.xml :

  4.0.0 com.hellokoding.springboot springboot-registration-login 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent 2.2.3.RELEASE  UTF-8 1.8   org.springframework.boot spring-boot-starter-web  org.apache.tomcat.embed tomcat-embed-jasper  javax.servlet jstl  org.springframework.boot spring-boot-starter-data-jpa  org.springframework.boot spring-boot-starter-security  mysql mysql-connector-java runtime     org.springframework.boot spring-boot-maven-plugin     

Определение объектов JPA и Hibernate

Содержимое файла User.java :

package com.hellokoding.auth.model; import javax.persistence.*; import java.util.Set; @Entity @Table(name = "user") public class User < @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; @Transient private String passwordConfirm; @ManyToMany private Setroles; public Long getId() < return id; >public void setId(Long id) < this.id = id; >public String getUsername() < return username; >public void setUsername(String username) < this.username = username; >public String getPassword() < return password; >public void setPassword(String password) < this.password = password; >public String getPasswordConfirm() < return passwordConfirm; >public void setPasswordConfirm(String passwordConfirm) < this.passwordConfirm = passwordConfirm; >public Set getRoles() < return roles; >public void setRoles(Set roles) < this.roles = roles; >>

Содержимое файла Role.java :

package com.hellokoding.auth.model; import javax.persistence.*; import java.util.Set; @Entity @Table(name = "role") public class Role < @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @ManyToMany(mappedBy = "roles") private Setusers; public Long getId() < return id; >public void setId(Long id) < this.id = id; >public String getName() < return name; >public void setName(String name) < this.name = name; >public Set getUsers() < return users; >public void setUsers(Set users) < this.users = users; >>

Использование репозиториев Spring Data JPA

Репозитории помогают сократить шаблонный код, который необходим для реализации уровней доступа к данным в разных хранилищах — MySQL, PostgreSQL и других. В них описываются некоторые функции для создания, чтения, обновления и удаления данных из БД — например, findAll , findById , save , saveAll , delete и deleteAll .

Файл UserRepository.java :

package com.hellokoding.auth.repository; import com.hellokoding.auth.model.User; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository

Файл RoleRepository.java :

package com.hellokoding.auth.repository; import com.hellokoding.auth.model.Role; import org.springframework.data.jpa.repository.JpaRepository; public interface RoleRepository extends JpaRepository

Определение UserDetailsService Spring Security

Для использования аутентификации с помощью Spring Security нужно реализовать интерфейс org.springframework.security.core.userdetails.UserDetailsService .

Файл UserDetailsServiceImpl.java :

package com.hellokoding.auth.service; import com.hellokoding.auth.model.Role; import com.hellokoding.auth.model.User; import com.hellokoding.auth.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.HashSet; import java.util.Set; @Service public class UserDetailsServiceImpl implements UserDetailsService < @Autowired private UserRepository userRepository; @Override @Transactional(readOnly = true) public UserDetails loadUserByUsername(String username) < User user = userRepository.findByUsername(username); if (user == null) throw new UsernameNotFoundException(username); SetgrantedAuthorities = new HashSet<>(); for (Role role : user.getRoles()) < grantedAuthorities.add(new SimpleGrantedAuthority(role.getName())); >return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities); > >

Создание сервиса безопасности

Сервис безопасности нужен для предоставления текущего авторизованного пользователя и автоматического входа в систему после регистрации.

SecurityService.java: package com.hellokoding.auth.service; public interface SecurityService < String findLoggedInUsername(); void autoLogin(String username, String password); >SecurityServiceImpl.java: package com.hellokoding.auth.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Service; @Service public class SecurityServiceImpl implements SecurityService < @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; private static final Logger logger = LoggerFactory.getLogger(SecurityServiceImpl.class); @Override public String findLoggedInUsername() < Object userDetails = SecurityContextHolder.getContext().getAuthentication().getDetails(); if (userDetails instanceof UserDetails) < return ((UserDetails)userDetails).getUsername(); >return null; > @Override public void autoLogin(String username, String password) < UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities()); authenticationManager.authenticate(usernamePasswordAuthenticationToken); if (usernamePasswordAuthenticationToken.isAuthenticated()) < SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); logger.debug(String.format("Auto login %s successfully!", username)); >> >

Создание службы пользователя

Также нужно создать службу для регистрации пользователей.

UserService.java: package com.hellokoding.auth.service; import com.hellokoding.auth.model.User; public interface UserService < void save(User user); User findByUsername(String username); >UserServiceImpl.java: package com.hellokoding.auth.service; import com.hellokoding.auth.model.User; import com.hellokoding.auth.repository.RoleRepository; import com.hellokoding.auth.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import java.util.HashSet; @Service public class UserServiceImpl implements UserService < @Autowired private UserRepository userRepository; @Autowired private RoleRepository roleRepository; @Autowired private BCryptPasswordEncoder bCryptPasswordEncoder; @Override public void save(User user) < user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); user.setRoles(new HashSet<>(roleRepository.findAll())); userRepository.save(user); > @Override public User findByUsername(String username) < return userRepository.findByUsername(username); >>

Определение валидатора

Входные данные будет проверять валидатор. Коды ошибок определяются в validation.properties .

UserValidator.java: package com.hellokoding.auth.validator; import com.hellokoding.auth.model.User; import com.hellokoding.auth.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; @Component public class UserValidator implements Validator < @Autowired private UserService userService; @Override public boolean supports(ClassaClass) < return User.class.equals(aClass); >@Override public void validate(Object o, Errors errors) < User user = (User) o; ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "NotEmpty"); if (user.getUsername().length() < 6 || user.getUsername().length() >32) < errors.rejectValue("username", "Size.userForm.username"); >if (userService.findByUsername(user.getUsername()) != null) < errors.rejectValue("username", "Duplicate.userForm.username"); >ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "NotEmpty"); if (user.getPassword().length() < 8 || user.getPassword().length() >32) < errors.rejectValue("password", "Size.userForm.password"); >if (!user.getPasswordConfirm().equals(user.getPassword())) < errors.rejectValue("passwordConfirm", "Diff.userForm.passwordConfirm"); >> >

Настройка контроллеров

UserController.java: package com.hellokoding.auth.web; import com.hellokoding.auth.model.User; import com.hellokoding.auth.service.SecurityService; import com.hellokoding.auth.service.UserService; import com.hellokoding.auth.validator.UserValidator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; @Controller public class UserController < @Autowired private UserService userService; @Autowired private SecurityService securityService; @Autowired private UserValidator userValidator; @GetMapping("/registration") public String registration(Model model) < model.addAttribute("userForm", new User()); return "registration"; >@PostMapping("/registration") public String registration(@ModelAttribute("userForm") User userForm, BindingResult bindingResult) < userValidator.validate(userForm, bindingResult); if (bindingResult.hasErrors()) < return "registration"; >userService.save(userForm); securityService.autoLogin(userForm.getUsername(), userForm.getPasswordConfirm()); return "redirect:/welcome"; > @GetMapping("/login") public String login(Model model, String error, String logout) < if (error != null) model.addAttribute("error", "Your username and password is invalid."); if (logout != null) model.addAttribute("message", "You have been logged out successfully."); return "login"; >@GetMapping() public String welcome(Model model) < return "welcome"; >>

POST-контроллер /login определять не нужно. Его по умолчанию предоставляет Spring Security.

Шаблоны просмотра JSP (Bootstrap)

Здесь достаточно трех файлов: для регистрации, входа и приветствия.

Registration.jsp:   "/>    Create an account /resources/css/bootstrap.min.css" rel="stylesheet"> /resources/css/common.css" rel="stylesheet">  

Create your account

Login.jsp: "/> Log in with your account /resources/css/bootstrap.min.css" rel="stylesheet"> /resources/css/common.css" rel="stylesheet">

Log in

$ $ " value="$"/>

/registration">Create an account

Welcome.jsp: "/> Create an account /resources/css/bootstrap.min.css" rel="stylesheet">
" value="$"/>

Welcome $ | Logout

Определение свойств

Validation.properties: NotEmpty=This field is required. Size.userForm.username=Please use between 6 and 32 characters. Duplicate.userForm.username=Someone already has that username. Size.userForm.password=Try one with at least 8 characters. Diff.userForm.passwordConfirm=These passwords don't match. Application.properties: spring.datasource.url=jdbc:mysql://hk-mysql:3306/test?useSSL=false spring.datasource.username=root spring.datasource.password=hellokoding spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.jpa.hibernate.ddl-auto=create spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect spring.jpa.generate-ddl=true spring.jpa.show-sql=true spring.mvc.view.prefix: / spring.mvc.view.suffix: .jsp spring.messages.basename=validation

Конфигурация веб-безопасности

WebSecurityConfig.java: package com.hellokoding.auth; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter < @Qualifier("userDetailsServiceImpl") @Autowired private UserDetailsService userDetailsService; @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() < return new BCryptPasswordEncoder(); >@Override protected void configure(HttpSecurity http) throws Exception < http .authorizeRequests() .antMatchers("/resources/**", "/registration").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); >@Bean public AuthenticationManager customAuthenticationManager() throws Exception < return authenticationManager(); >@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception < auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); >>

Конфигурация приложения

WebApplication.java: package com.hellokoding.auth; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; @SpringBootApplication public class WebApplication < public static void main(String[] args) throws Exception < SpringApplication.run(WebApplication.class, args); >>

Запуск Docker-контейнера

Подготовьте Dockerfile для приложения Java / Spring Boot и файл docker-compose.yaml для MySQL Server.

Содержимое Dockerfile:

FROM maven:3.5-jdk-8

Содержимое docker-compose.yml :

version: '3' services: hk-mysql: container_name: hk-mysql image: mysql/mysql-server:5.7 environment: MYSQL_DATABASE: test MYSQL_ROOT_PASSWORD: hellokoding MYSQL_ROOT_HOST: '%' ports: - "3306:3306" restart: always registration-login: build: . volumes: - .:/app - ~/.m2:/root/.m2 working_dir: /app ports: - 8080:8080 command: mvn clean spring-boot:run depends_on: - hk-mysql

Перейдите в корень проекта и запустите Docker:

docker-compose up

Тестирование

Чтобы запустить приложение на локальном сервере MySQL, измените в application.properties « hk-mysql » на « localhost », перейдите в корневой каталог проекта и выполните команду:

mvn clean spring-boot:run

Перейдите в браузере по адресу localhost: 8080 и протестируйте приложение. Попробуйте заходить под разными пользователями, выходить, добавлять новые функции и изменять права доступа.

Исходный код нашего проекта — в репозитории на GitHub.

Итоги

В этой статье мы познакомились с основами Spring Security и разобрали основные возможности этого фреймворка. Не ограничивайтесь теорией. Попробуйте собрать собственное простое приложение с регистрацией и аутентификацией. Если у вас что-то не получится, то не огорчайтесь, вам поможет менторство практикующих специалистов.

Spring Security/Технический обзор Spring Security

Эта статья представляет собой перевод Spring Security Reference Documentation, Ben Alex, Luke Taylor 3.0.2.RELEASE, Глава 5, Технический обзор.

Среда исполнения [ править ]

Для 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 [ править ]

Рассмотрим стандартный сценарий аутентификации с которым все хорошо знакомы.

  1. Пользователю будет предложено войти в систему с именем пользователя и паролем.
  2. Система (успешно) подтверждает, что введен правильный пароль для данного имени пользователя.
  3. Получается контекстная информация для данного пользователя (список ролей и т. д.).
  4. Для пользователя устанавливается контекст безопасности.

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

Первые три пункта составляют непосредственно процесс аутентификации, поэтому мы рассмотрим, как это происходит в Spring Security.

  1. Получаются имя пользователя и пароль и объединяются в экземпляр класса UsernamePasswordAuthenticationToken (экземпляр интерфейса Authentication , который мы видели ранее).
  2. Токен передается экземпляру AuthenticationManager для проверки.
  3. AuthenticationManager возвращает полностью заполненный экземпляр Authentication в случае успешной аутентификации.
  4. Устанавливается контекст безопасности путем вызова 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 ListGrantedAuthority> AUTHORITIES = new ArrayListGrantedAuthority>(); 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 .

Установка содержимого SecurityContextHolder напрямую [ править ]

На самом деле Spring Security не задумывается о том, каким образом объект Authentication попадет внутрь SecurityContextHolder . Существует только одно критическое требование, оно состоит в том что SecurityContextHolder должен содержать в себе объект Authentication , который представляет принципала до того момента как AbstractSecurityInterceptor (чуть позже, мы рассмотрим его подробней) потребуется авторизовать пользовательскую операцию.

Вы можете (и многие пользователи так и делают) написать собственные фильтры или MVC контроллеры для обеспечения взаимодействия с системами аутентификации, которые не основаны на Spring Security. Например, вы могли бы использовать Container-Managed аутентификацию, которая получает текущего пользователя из ThreadLocal или JNDI. Или вы можете работать на компанию, которая имеет собственную унаследованную систему аутентификации, которая является корпоративным «стандартом» и над которой у вас мало контроля. В таких ситуациях очень легко «запрячь» Spring Security в работу, и использовать старую систему аутентификации. Все что нужно сделать, это написать фильтр (или его эквивалент), который считывает информацию о пользователях из нужного места, строить Spring Security объект Authentication и помещает его в SecurityContextHolder .

Если вам интересно, как в реальности реализован менеджер AuthenticationManager , то мы рассмотрим его в главе «Ключевые сервисы».

Аутентификация в Веб-приложениях [ править ]

Теперь давайте исследуем ситуацию, где вы используете Spring Security в веб-приложении (система безопасности в web.xml выключена). Как аутентифицируется пользователь и устанавливается контекст безопасности приложения?

Рассмотрим типичный процесс аутентификации веб-приложения

  1. Вы заходите на главную страницу и нажимаете на ссылку.
  2. Запрос идет на сервер и сервер решает что вы запрашиваете защищенный ресурс.
  3. Т.к. Ваша подлинность в настоящее время не подтверждена, то сервер отправляет ответ о том, что вы должны пройти процедуру аутентификации. Ответом будет либо код HTTP, либо перенаправление на особую веб-страницу.
  4. В зависимости от механизма аутентификации, браузер либо перенаправит вас на специальную веб-страницу, где можно будет заполнить форму, либо браузер каким-то другим образом получит данные подтверждающие подлинность Вашей личности (через базовый диалог аутентификации, куки, сертификат X.509 и т.п.).
  5. Браузер отошлет ответ на сервер. Это будет либо HTTP POST, с содержимым формы, которую вы заполнили, либо HTTP заголовок, содержащий ваши данные аутентификации.
  6. Далее сервер будет решать, является ли «удостоверение личности» действительными. Если оно действительно, то будет выполнен следующий шаг. Если же оно недействительно, то как правило браузер предложит попробовать идентифицироваться еще раз (т.е. вы вернетесь к шагу 2).
  7. Оригинальный запрос, который привел к запуску процедуры аутентификации, будет повторен. Надеемся, что вы имеете достаточно полномочий для доступа к защищенному ресурсу. Если вы имеете соответствующие права доступа, то запрос будет успешным. В противном случае вам будет возвращен код ошибки 403, что означает «запрещено».

Spring Security имеет явно реализованные классы отвечающие за большинство шагов, описанных выше. Основные участники (в порядке их использования) ExceptionTranslationFilter , AuthenticationEntryPoint и «механизм аутентификации», который отвечает за вызов AuthenticationManager который мы видели в предыдущем разделе.

ExceptionTranslationFilter [ править ]

ExceptionTranslationFilter это фильтр Spring Security, который отвечает за обнаружение пробросов каких-либо исключений Spring Security. Такие исключения, как правило пробрасываются AbstractSecurityInterceptor , который является основным поставщиком сервисов авторизации. Мы будем обсуждать AbstractSecurityInterceptor в следующем разделе, а сейчас нам просто необходимо знать, что он производит Java исключения, и ничего не знает о HTTP или о том как проходит аутентификация принципала. Вместо него этот сервис предлагает ExceptionTranslationFilter , отвечая либо за возврат кода ошибки 403 (если принципал уже прошел проверку подлинности и следовательно просто не имеет достаточно прав доступа — как в шаге 7 описанном выше ), либо за запуск AuthenticationEntryPoint (если принципал не прошел аутентификацию и мы должны начать шаг 3).

AuthenticationEntryPoint [ править ]

AuthenticationEntryPoint отвечает за шаг №3 из приведенного выше списка. Как вы понимаете, каждое веб-приложение имеет стратегию аутентификации по умолчанию (она может быть настроена как и почти все остальное в Spring Security, но в целях простоты пропустим это в настоящий момент). Все основные системы аутентификации будут иметь свою собственную реализацию AuthenticationEntryPoint , которая обычно выполняет одно из действий, описанных в шаге 3

Механизм аутентификации [ править ]

Как только браузер отправит удостоверение подлинности вашей личности (POST запрос HTTP формы, либо HTTP заголовок) на сервере должно присутствовать нечто, что будет «собирать» эти данные аутентификации. Т.е. сейчас мы говорим о шаге №6 из нашего списка. В Spring Security есть специальное название для функции сбора аутентификационных данных от пользовательского агента (обычно это веб-браузер). Обычно ее называют «механизм аутентификации». Например, аутентификация на основе формы, либо базовая аутентификация. Сразу как только данные аутентификации будут собраны от пользовательского агента, для нашего «запроса» будет построен объект Authentication и предоставлен AuthenticationManager .

Если после этого механизм аутентификации получит обратно полностью заполненный объект Authentication , он будет считать запрос корректным и поместит объект Authentication в SecurityContextHolder , и повторно вызовет первоначального запрос (шаг 7). Если же AuthenticationManager отклонит запрос, то механизм аутентификации повторно запросит данные у пользовательского агента (шаг 2).

Сохранение SecurityContext между запросами [ править ]

В зависимости от типа приложения может потребоваться стратегия хранения контекста безопасности между операциями пользователя. В типичном веб-приложении, пользователь регистрируется один раз и впоследствии идентифицируется по Id сессии. Сервер кэширует информацию принципала в течение сессии. В Spring Security, ответственность за хранение SecurityContext между запросами ложится на SecurityContextPersistenceFilter , который по умолчанию хранит контекст как атрибут HttpSession между запросами HTTP. Он восстанавливает контекст в SecurityContextHolder для каждого запроса и, что самое главное, очищает SecurityContextHolder после завершения запроса. Вы не должны напрямую взаимодействовать с HttpSession для решения задач безопасности. Для этого нет никаких оснований, всегда вместо этого используйте SecurityContextHolder .

Многие другие типы приложений(например, RESTful веб-сервисы без сохранения состояния) не используют HTTP сессии и будут требовать аутентификации при каждом запросе. Тем не менее, все равно очень важно, чтобы SecurityContextPersistenceFilter входил в цепочку, чтобы SecurityContextHolder очищался после каждого запроса.

Примечание В приложение, которое получает конкурентные запросы в одной сессии, один и тот же экземпляр SecurityContext будет разделяться между потоками. Несмотря на то, что используется ThreadLocal , это будет один и тот же экземпляр, что извлекается из HttpSession для каждого потока. Это будет иметь значение если вы захотите временно изменить контекст в выполняющемся потоке. Если Вы просто используете SecurityContextHolder.getContext().SetAuthentication(anAuthentication) , то объект Authentication изменится во всех параллельных потоках, которые разделяют один и тот же экземпляр SecurityContext . Вы можете настроить поведение SecurityContextPersistenceFilter чтобы он создавал совершенно новый экземпляр SecurityContext для каждого запроса, чтобы один поток не влиял на другой. Альтернативный вариант, это создать новый экземпляр в том месте где требуется временно изменить контекст. Метод SecurityContextHolder.createEmptyContext() всегда возвращает новый экземпляр контекста.

Управление доступом (Авторизация) в Spring Security [ править ]

В Spring Security основным интерфейсом, отвечающим за принятие решений в области контроля доступа, является AccessDecisionManager . У него имеется решающий метод, который принимает объект Authentication , представляющий принципала запрашивающего доступ, «объект безопасности» (см. ниже), а также список атрибутов метаданных безопасности, которые применяются к объекту (например, список ролей которым разрешен доступ).

Безопасность и Советы AOP [ править ]

Если вы хорошо знакомы с AOP, то должны знать что существуют различные виды советов: before, after, throws и around. Совет around очень полезен, потому что советчик может выбирать, следует или нет осуществить вызов метода, следует или нет изменить отклик, следует или нет пробросить исключение. Spring Security предоставляет around советы как для вызова методов, так и для веб-запросов. Для вызова методов совет around реализуется с помощью стандартного AOP модуля Spring’а, а для веб-запросов с помощью стандартного фильтра.

Для тех, кто не знаком с AOP, главное понять, что Spring Security может защищать вызовы методов так же хорошо, как и веб-запросы. Для большинства людей важно обеспечить безопасность вызова методов на уровне сервисов. Потому что на уровне сервисов сосредоточено большинство бизнес-логики нынешнего поколения J2EE приложений. Если вам просто нужно обеспечить безопасность при вызове методов на уровне сервисов, то стандартный Spring AOP будет весьма уместным. Если вам нужно обеспечить безопасность непосредственно объектов предметной области, то вероятно следует рассмотреть вариант использования AspectJ.

Вы можете выбрать метод авторизации с использованием AspectJ или Spring AOP, или вы можете выбрать авторизацию веб-запросов с помощью фильтров. Вы можете использовать ноль, один, два или даже все три эти подхода вместе. Типичный шаблон использования выглядит следующим образом: использование авторизации для части веб-запросов, в сочетании авторизаций вызова методов уровня сервисов с помощью Spring AOP .

Защищенные Объекты и AbstractSecurityInterceptor [ править ]

Что такое «защищенный объект» в общем смысле? Spring Security использует этот термин для обозначения любого объекта, к которому могут применяться механизмы обеспечения безопасности (например, авторизация). Наиболее распространенными примерами являются вызовы методов и веб-запросы.

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

AbstractSecurityInterceptor обеспечивает последовательный рабочий процесс для обработки запросов к защищенному объекту, обычно:

  1. Поиск «конфигурационных атрибутов», связанных с текущим запросом
  2. Отправка защищенного объекта, текущего Authentication объекта и конфигурационных атрибутов объекту AccessDecisionManager для принятия решение об авторизации
  3. Опциональное изменение Authentication под которым происходит вызов
  4. Разрешение выполнения вызова безопасного объекта (при условии что доступ был разрешен)
  5. При наличии в конфигурации, вызов AfterInvocationManager , сразу после возврата из вызова защищенного объекта.

Что такое конфигурационные атрибуты [ править ]

«Конфигурационный атрибут» можно рассматривать как String , которая имеет особое значение для классов, используемых AbstractSecurityInterceptor . Они представляются интерфейсом ConfigAttribute каркаса Spring Security. Они могут быть простыми именами ролей или иметь более сложный смысл, в зависимости от возможностей реализации AccessDecisionManager . AbstractSecurityInterceptor настроен так, чтобы использовать SecurityMetadataSource , который он использует для поиска атрибутов защищенного объекта. Обычно эта конфигурация будет скрыта от пользователя. Конфигурационные атрибуты устанавливаются в виде аннотаций для защищенных методов или атрибутов доступа для защищенных URL. Например, когда мы видим что-то вроде в конфигурационном файле, это говорит о том, что конфигурационные атрибуты ROLE_A и ROLE_B должны применяться к веб-запросам, соответствующим заданному шаблону. На практике, с конфигурацией по умолчанию для AccessDecisionManager , это будет означает, что доступ получит каждый, кто имеет GrantedAuthority совпадающий с одним из этих двух атрибутов. Строго говоря, это просто атрибуты и их интерпретация зависит от реализации AccessDecisionManager . Префикс ROLE_ используется как маркер, чтобы показать что эти атрибуты обозначают роли пользователей и будут использованы классом RoleVoter каркаса Spring Security. Это имеет смысл только тогда, когда используется AccessDecisionManager основанный на voter. В следующей главе мы увидим как реализован AccessDecisionManager .

RunAsManager [ править ]

Предположим, что AccessDecisionManager выдает разрешение на выполнение какому-либо запросу, тогда AbstractSecurityInterceptor , как правило, просто передаст данный запрос на обработку. Надо сказать, что в редких случаях пользователи могут захотеть заменить один Authentication объект в SecurityContext на другой Authentication объект, который будет обработан AccessDecisionManager с помощью вызова RunAsManager . Это может оказаться полезным в некоторых нетиповых ситуациях, например, если методу уровня сервисов нужно вызвать удаленную систему и идентифицироваться в ней с другими данными. Так как Spring Security автоматически распространяет аутентификационные данные с одного сервера на другой (предполагается, что вы пользуетесь правильно настроенными RMI или HttpInvoker клиентами протокола удаленного вызова), то эта возможность может быть полезной.

AfterInvocationManager [ править ]

После того как безопасный объект отработал и произошел возврат из него — что может означать завершение вызова метода или работы цепочки фильтров — AbstractSecurityInterceptor получает последний шанс для обработки вызова. На данном этапе AbstractSecurityInterceptor может быть заинтересован в возможности изменить возвращаемый объект. Мы можем захотеть чтобы это произошло в том случае, если решение об авторизации не могло быть принято «на пути к» вызову безопасного объекта. Будучи полностью настраиваемым с помощью плагинов, AbstractSecurityInterceptor передаст управление AfterInvocationManager чтобы изменить возвращаемый объект, если это потребуется. Этот класс может даже полностью заменить возвращаемый объект, или выбросить исключение, или же вообще ничего не изменять, в зависимости от потребностей.

AbstractSecurityInterceptor и связанные с ним объекты показаны на рисунке 5.1, «Модель перехватчиков системы безопасности и «защищенного объекта».

Расширение модели защищенного объекта [ править ]

Только те разработчики, которые рассматривают возможность создания полностью нового способа перехвата и авторизации запросов, должны напрямую использовать защищенные объекты. Например, можно было бы построить новый защищенный объект для системы обмена сообщениями. Все что требует обеспечения безопасности и обеспечивает способ перехвата вызовов (на подобие семантики AOP совета around) может быть превращено в защищенный объект. Можно сказать, что в большинстве Spring-приложений можно легко и прозрачно использовать три, в настоящее время поддержаващихся, типа защищенных объектов (MethodInvocation AOP альянса, AspectJ JoinPoint и FilterInvocation для веб-запросов).

Локализация [ править ]

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

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

В поставляемом файле spring-security-core-xx.jar , вы найдете пакет org.springframework.security , который в свою очередь содержит файл messages.properties . На него должна быть установлена ссылка в вашем ApplicationContext , поскольку классы Spring Security реализуют Spring интерфейс MessageSourceAware и ожидают, что в преобразователь сообщений (message resolver) будет включена зависимость из контекста приложения в момент его запуска.. Обычно все, что Вы должны сделать, это зарегистрировать bean в контексте приложения, который будет ссылаться на сообщения. Ниже приведен пример:

 id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  name="basename" value="org/springframework/security/messages"/>  

Messages.properties назван в соответствии со стандартом пакетов ресурсов и содержит сообщения на языке по умолчанию, поддерживаемые Spring Security. Этот файл по умолчанию на английском языке. Если вы не зарегистрировали источник сообщений, Spring Security все равно будет работать правильно, и показывать английскую версию сообщений жестко заданных в коде.

Если вы хотите настроить файл messages.properties или поддерживать другие языки, то вы должны скопировать файл, переименовать в соответствии с правилами и зарегистрировать его в приведенном выше определении компонента. Файл содержит небольшое количество ключей сообщений, поэтому локализация не рассматриваться как основная инициатива. Если вы выполните локализацию этого файла, пожалуйста подумайте о том, чтобы поделиться вашей работой с сообществом. Для этого войдите в JIRA, создайте задачу и прикрепите к ней вашу версию переименованного и локализованного файла messages.properties .

В завершении нашего обсуждения локализации: Существует Spring ThreadLocal известный как org.springframework.context.i18n.LocaleContextHolder . Вы должны установить LocaleContextHolder так, чтобы он представлял предпочтительную Locale каждого пользователя. Spring Security будет пытаться найти сообщения в соответствии с настройками Locale , который берется из ThreadLocal . Пожалуйста, обратитесь к документации Spring Framework для более подробной информацию об использовании LocaleContextHolder .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *