Spring Security — среда для аутентификации и авторизации пользователей. Фреймворк применяется для защиты приложений на Spring. В нем представлены базовые инструменты безопасности, которые без труда расширяются для решения разных задач.
В этой статье мы изучим основы Spring Security, а также попрактикуемся, создав простое приложение с регистрацией и аутентификацией пользователя.
Содержание:
1. Основные возможности
2. Пример подключения Spring Security
3. Создание проекта и подключение зависимостей
Итоги
Spring Security не ограничивается аутентификацией. Фреймворк также помогает решить проблему с безопасным хранением паролей.
Spring Security предлагает назначить объекту UserDetails собственный шифровальщик паролей. По умолчанию используется BCrypt. Также можно настроить количество раундов хеширования и реализацию случайного алгоритма.
Для сохранения информации о пользователях и выполнения аутентификации можно использовать временную базу данных, которая остается в оперативной памяти приложения.
Это полезно при разработке и тестировании. Реальная база данных при таком подходе остается нетронутой.
Lightweight Directory Access Protocol (LDAP) — протокол аутентификации учетных записей пользователей в организациях. Позволяет определять структуру пользователей и групп пользователей, назначать им права доступа.
Дополнительную информацию вы можете получить в официальном руководстве по использованию аутентификации LDAP.
Spring Security предоставляет механизмы для управления сеансом пользователя. Он создает эти механизмы контроля при входе в систему и уничтожает при выходе.
Для обеспечения безопасности доступны дополнительные средства, которые помогают, например, избежать отслеживания сеансов.
Это встроенный механизм распознавания, благодаря которому пользователям не нужно вводить учетные данные при каждом посещении сайта.
Spring Security предлагает несколько способов реализации этого типа аутентификации — например, хеширование данных с помощью секретного ключа или хранение постоянного токена в базе данных.
Open Authorization 2.0 — открытый стандарт проверки прав пользователя с помощью сервиса авторизации. Он также используется для реализации таких функций, как вход через учетные записи Facebook, Google и других крупных площадок.
Настройка сервера авторизации и внедрение OAuth сопряжены с высоким риском и могут отнять много времени. В этом процессе легко ошибиться и создать уязвимость. Spring Security предлагает защитить сайт с помощью готовых инструментов — например, Auth0.
Это лишь некоторые возможности аутентификации и авторизации через Spring Security. Если хотите узнать больше, смотрите документацию Spring Boot.
Ознакомьтесь с курсами от наших партнеров, которые помогут вам повысить свой уровень в среде разработки приложений, от Mate Academy и Hillel. Практикующие преподаватели и единомышленники, это именно то что вам нужно для развития.
Посмотрим на практике, какие настройки включаются по умолчанию при добавлении в проект Spring Security.
Сгенерируйте на Spring Initializr приложение с зависимостью Web
:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Напишите REST
-контроллер:
@RestController public class HelloController { @GetMapping("/api/hello") public String hello(){ return "Highload"; } }
Проверьте приложение в браузере. Сейчас доступ к нему имеют все пользователи.
Добавьте Maven
-зависимость, чтобы подключить Spring Security:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
Проверьте приложение снова. Теперь при переходе по первоначальному адресу http://localhost:8080/api/hello
мы попадем на страницу для ввода логина и пароля — http://localhost:8080/login
. Здесь размещена форма входа. Введите в нее любые данные и нажмите Sign in — появится сообщение об ошибке.
Приложение уже выполняет проверку пользователей, хотя вы еще даже ничего не настраивали.
Spring Security после подключения добавляет своего пользователя. Его имя user, пароль генерируется при запуске. Кроме того, в проекте при добавлении зависимости:
Кроме страницы для входа создается страница для выхода из аккаунта. Она находится по адресу /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
В билдере нужно задать тип аутентификации. В этом примере используется In-Memory — auth.inMemoryAuthentication()
. Но может быть другой вариант — Jdbc, LDAP, кастомная аутентификация. Тип аутентификации определяет, где хранится пользователь.
Затем определяются специальные настройки AuthenticationManager. Вы уточняете, как он извлекает пользователя из хранилища, чтобы сравнить его с введенными данными. В этом случае In-Memory-аутентификация позволяет ускорить процесс. Имя и пароль заданы в том же файле.
AuthenticationManager достает не только имя и пароль, но и разрешения пользователя — authorities("ROLE_USER"
).
Что в итоге делает приведенный выше фрагмент кода:
Обратите также внимание на бин PasswordEncoder. В нем указано, как шифровать пароль. В приведенном выше примере пароль не шифруется. Так можно делать только на учебных проектах для наглядности. В реальном приложении NoOpPasswordEncoder не используется.
Итак, мы готовы, чтобы написать обещанное простое приложение с регистрацией и входом в учетную запись пользователя. Используемые технологии: 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
:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.hellokoding.springboot</groupId> <artifactId>springboot-registration-login</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.3.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Содержимое файла 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 Set<Role> roles; 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<Role> getRoles() { return roles; } public void setRoles(Set<Role> 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 Set<User> users; 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<User> getUsers() { return users; } public void setUsers(Set<User> users) { this.users = users; } }
Репозитории помогают сократить шаблонный код, который необходим для реализации уровней доступа к данным в разных хранилищах — 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<User, Long> { User findByUsername(String username); }
Файл 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<Role, Long>{ }
Для использования аутентификации с помощью 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); Set<GrantedAuthority> grantedAuthorities = 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(Class<?> aClass) { 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({"/", "/welcome"}) public String welcome(Model model) { return "welcome"; } }
POST-контроллер /login
определять не нужно. Его по умолчанию предоставляет Spring Security.
Здесь достаточно трех файлов: для регистрации, входа и приветствия.
Registration.jsp: <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <c:set var="contextPath" value="${pageContext.request.contextPath}"/> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Create an account</title> <link href="${contextPath}/resources/css/bootstrap.min.css" rel="stylesheet"> <link href="${contextPath}/resources/css/common.css" rel="stylesheet"> </head> <body> <div class="container"> <form:form method="POST" modelAttribute="userForm" class="form-signin"> <h2 class="form-signin-heading">Create your account</h2> <spring:bind path="username"> <div class="form-group ${status.error ? 'has-error' : ''}"> <form:input type="text" path="username" class="form-control" placeholder="Username" autofocus="true"></form:input> <form:errors path="username"></form:errors> </div> </spring:bind> <spring:bind path="password"> <div class="form-group ${status.error ? 'has-error' : ''}"> <form:input type="password" path="password" class="form-control" placeholder="Password"></form:input> <form:errors path="password"></form:errors> </div> </spring:bind> <spring:bind path="passwordConfirm"> <div class="form-group ${status.error ? 'has-error' : ''}"> <form:input type="password" path="passwordConfirm" class="form-control" placeholder="Confirm your password"></form:input> <form:errors path="passwordConfirm"></form:errors> </div> </spring:bind> <button class="btn btn-lg btn-primary btn-block" type="submit">Submit</button> </form:form> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="${contextPath}/resources/js/bootstrap.min.js"></script> </body> </html> Login.jsp: <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <c:set var="contextPath" value="${pageContext.request.contextPath}"/> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Log in with your account</title> <link href="${contextPath}/resources/css/bootstrap.min.css" rel="stylesheet"> <link href="${contextPath}/resources/css/common.css" rel="stylesheet"> </head> <body> <div class="container"> <form method="POST" action="${contextPath}/login" class="form-signin"> <h2 class="form-heading">Log in</h2> <div class="form-group ${error != null ? 'has-error' : ''}"> <span>${message}</span> <input name="username" type="text" class="form-control" placeholder="Username" autofocus="true"/> <input name="password" type="password" class="form-control" placeholder="Password"/> <span>${error}</span> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> <button class="btn btn-lg btn-primary btn-block" type="submit">Log In</button> <h4 class="text-center"><a href="${contextPath}/registration">Create an account</a></h4> </div> </form> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="${contextPath}/resources/js/bootstrap.min.js"></script> </body> </html> Welcome.jsp: <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <c:set var="contextPath" value="${pageContext.request.contextPath}"/> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Create an account</title> <link href="${contextPath}/resources/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container"> <c:if test="${pageContext.request.userPrincipal.name != null}"> <form id="logoutForm" method="POST" action="${contextPath}/logout"> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> </form> <h2>Welcome ${pageContext.request.userPrincipal.name} | <a >Определение свойств
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 и разобрали основные возможности этого фреймворка. Не ограничивайтесь теорией. Попробуйте собрать собственное простое приложение с регистрацией и аутентификацией. Если у вас что-то не получится, то не огорчайтесь, вам поможет менторство практикующих специалистов.
Прокси (proxy), или прокси-сервер — это программа-посредник, которая обеспечивает соединение между пользователем и интернет-ресурсом. Принцип…
Согласитесь, было бы неплохо соединить в одно сайт и приложение для смартфона. Если вы еще…
Повсеместное распространение смартфонов привело к огромному спросу на мобильные игры и приложения. Миллиарды пользователей гаджетов…
В перечне популярных чат-ботов с искусственным интеллектом Google Bard (Gemini) еще не пользуется такой популярностью…
Скрипт (англ. — сценарий), — это небольшая программа, как правило, для веб-интерфейса, выполняющая определенную задачу.…
Дедлайн (от англ. deadline — «крайний срок») — это конечная дата стачи проекта или задачи…