Лекция 11-12
Тема: Работа со Spring Security. Понятие аутентификации и авторизации. Расширение WebSecurityConfigurerAdapter. Способы formLogin() и httpBasic(). Хеширование паролей.
Spring Security – это фреймворк обеспечения безопасности, предоставляющий возможность декларативного управления безопасностью приложений на основе фреймворка Spring.
Создадим новый проект, который включает модуль Spring Security или добавим в существующий проект зависимость
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
...
</dependencies>
При попытке перейти на любой URL-адрес проекта нас перенаправит на форму ввода логина и пароля

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

Если вы ввели правильно логин и пароль, то сервер переадресует вас на указанный URL.

В файле application.properties вы можете указать желаемый логин и пароль для пользователя по умолчанию (в данной лекции будет использоваться конфигурация с помощью языка yaml).
spring:
security:
user:
name: nick
password: 1234
Фреймворк Spring Security "из коробки" предоставляет вам возможность простой версии так называемой form-based аутентификации. Если быть точнее, то по умолчанию, Spring Security реализует следующее поведение:
добавляет обязательный процесс аутентификации для всех URL;
добавляет форму для входа;
обрабатывает ошибки формы ввода;
создает пользователя по умолчанию и генерирует пароль.
Основные понятия, связанные со Spring Security:
Authentication
Authorization
Principal - текущий залогиненный пользователь или текущий залогиненный аккаунт (если у одного физического лица или программы есть несколько аккаунтов, то тогда ему будет соответствовать несколько возможных principal`ов). Иногда, в общем случае, principal - это субъект, который принимает участие в осуществлении процедур безопасности. В качестве principal могут выступать люди, компьютеры, службы, процессы или их группа;
Granted Authority - ;
Role - .
Настройка процесса аутентификации в Spring
Для того, чтобы сконфигурировать процесс аутентификации, необходимо создать объект AuthenticationManager, в котором следует указать требуемые параметры аутентификации. Объект типа AuthenticationManager обычно настраивают с помощью builder`а, который имеет тип AuthenticationManagerBuilder.
Добавим класс SecurityConfig, который наследуется от класса WebSecurityConfigurerAdaper. Также укажем аннотации @Configuration (это означает, что данный класс является конфигурационным) и @EnableWebSecurity (это означает, что данный класс является содержит настройки для защиты веб-приложения).
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
Переопределим метод configure(), который принимает на вход объект типа AuthenticationManagerBuilder (обратите внимание, что нам нужна именно эта версия перегруженного метода).
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
}
Для начала укажем, что источник аутентификации это жестко прописанные пользователи (так называемая inMemoryAuthentication(). Далее указываем логин, пароль и роль пользователя.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("foo")
.password("bar")
.roles("USER");
}
}
Если необходимо указать несколько пользователей, после параметров первого пользователя вызываете метод and() после чего указываете параметры следующего пользователя.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("foo")
.password("bar")
.roles("USER")
.and()
.withUser("nick")
.roles("MODERATOR");
}
}
Хеширование паролей
Хранить пароли без хеширования является грубейшим нарушением правил безопасности, поэтому нам необходимо добавить процесс хеширования пароля в нашу систему.
Не будем вдаваться в подробности различных алгоритмов хеширования пароля, просто скажем, что на даннай момент рекомендуемым является алгоритм Bcrypt. Для обеспечения хеширования, вы можете поступить несколькими способами.
Первый способ - создайте Bean, который будет возвращать объект Encoder`а и добавьте его как метод конфигурационного класса.

Далее найдите в интернете генератор хеша с помощью алгоритма Bcrypt, скопируйте хеш для вашего пароля в метод password.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("foo")
.password("$2y$12$kElDOfhm4WgdsDc4UQjgtuz0VEi5MOqVVhXaMoD1F2lhLivokhCqe")
.roles("USER")
.and()
.withUser("nick")
.roles("MODERATOR");
}
@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
Если не хотите использовать Bean для хеширования пароля, можете в начале хеша добавить обозначение, что это хеш для алгоритма bcrypt.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("foo")
.password("{bcrypt}$2y$12$.JzV1A3qlof1.NzZpGaTYO1b26JGHevg0900QvwrSOdU3U9.g4hta")
.roles("USER");
}
// @Bean
// public PasswordEncoder getPasswordEncoder() {
// return new BCryptPasswordEncoder();
// }
}
Настройка процесса авторизации
Добавим класс контроллера
@Controller
public class IndexController {
@GetMapping("/")
public String index() {
return "index";
}
@GetMapping("/user/index")
public String user_index() {
return "/user/index";
}
@GetMapping("/admin/index")
public String admin_index() {
return "/admin/index";
}
}
Изменим SecurityConfig
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("foo")
.password("$2y$12$kElDOfhm4WgdsDc4UQjgtuz0VEi5MOqVVhXaMoD1F2lhLivokhCqe")
.roles("USER")
.and()
.withUser("admin")
.password("$2y$12$DQlTV6V1wMKEoCIW5lo1huAn2/bRk4hULDmRS5Jw6YW7HayHV4K66")
.roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "static/css", "static/js").permitAll()
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.antMatchers("/admin/**").hasRole("ADMIN")
.and().formLogin();
}
@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
Изменим formLogin() на httpBasic().
Создадим MyUserDetailsService
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
UserRepository repository;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
return new MyUserDetails(s);
}
}
Создадим MyUserDetails
public class MyUserDetails implements UserDetails {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}
private String userName;
public MyUserDetails(String userName) {
this.userName = userName;
}
public MyUserDetails() {
}
@Override
public String getPassword() {
return "$2y$12$8fKhxW71f4DzkzXYCh592.I.cd1uKkMrNwrHAApR1x5KHJ3qy1IjS";
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
Добавим в pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- PostgreSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
Настроим подключение к БД
jpa:
database: POSTGRESQL
show-sql: true
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
datasource:
platform: postgres
url: jdbc:postgresql://localhost:5432/ejournal
username: ejournal_user
password: 123456
driverClassName: org.postgresql.Driver
Добавим сущность User
@Entity
@Table(name = "profile")
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@Column(name = "user_name")
private String userName;
private String password;
private boolean active;
private String roles;
}
Изменим MyUserDetailsService
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
UserRepository repository;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
Optional<User> user = repository.findByUserName(s);
user.orElseThrow(() -> new UsernameNotFoundException("User not found: " + s));
return user.map(MyUserDetails::new).get();
}
}
Создадим UserRepository
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByUserName(String userName);
}
Изменим MyUserDetails
public class MyUserDetails implements UserDetails {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorityList;
}
private String userName;
private String password;
private boolean active;
private List<GrantedAuthority> authorityList;
public MyUserDetails(User userName) {
this.userName = userName.getUserName();
this.password = userName.getPassword();
this.active = userName.isActive();
this.authorityList = Arrays.stream(userName.getRoles().split(",")).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
public MyUserDetails() {
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return active;
}
}
Last updated
Was this helpful?