Микросервисы, как небольшие самодостаточные модули, взаимодействуют друг с другом, чтобы создавать сложные системы. При этом безопасность играет ключевую роль, так как микросервисы обмениваются данными, которые могут быть конфиденциальными. Без правильной настройки доступа можно легко допустить утечки, ошибки прав доступа, и, как следствие, потерю доверия пользователей.
Для работы с микросервисами Spring Security предоставляет гибкие механизмы защиты и управления правами доступа. Давайте вместе разберемся, как сделать наши микросервисные системы не только функциональными, но и безопасными.
Итак, включаем Spring Security и обсуждаем правильные подходы и практики.
Немного теории
Для начала кратко разберем потребности микросервисов в плане безопасности:
- Авторизация и аутентификация. Необходимость четко определить, кто делает запрос, и имеет ли этот "кто-то" право на доступ к ресурсу.
- Управление ролями и доступами. Например, в нашем приложении администраторы видят больше данных, чем обычные пользователи.
- Межсервисное взаимодействие. Один микросервис должен быть уверен в том, что запрос от другого микросервиса действительно исходит от доверенной службы.
В микросервисной архитектуре нам зачастую приходится делить безопасность на два уровня:
- Внешний доступ — запросы от клиента (пользователя) к микросервисам.
- Внутренние коммуникации — взаимодействие между микросервисами.
Для решения задач безопасности мы будем использовать комбинацию Spring Security и JWT — идеальный дуэт для обеспечения защиты и легкости настройки.
Практическая часть: настройка безопасности в Spring Security
Шаг 1: Подключаем Spring Security Для начала добавим зависимость Spring Security в наш pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Эта зависимость добавляет в наше приложение все необходимое для базовой настройки безопасности.
Шаг 2: Создаем конфигурацию безопасности
Для микросервисов мы обычно начинаем с Java-based конфигурации. Создадим класс, в котором будем конфигурировать Spring Security.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // Отключаем CSRF для упрощения (только для API!)
.authorizeRequests()
.antMatchers("/public/**").permitAll() // Публичный доступ
.antMatchers("/admin/**").hasRole("ADMIN") // Только для ролей ADMIN
.anyRequest().authenticated() // Все остальные запросы должны быть авторизованы
.and()
.httpBasic(); // Используем базовую аутентификацию (для начала)
}
}
Что мы здесь сделали:
- Отключили CSRF (это важно, так как мы работаем с REST API). Впрочем, будьте осторожны — в продуктивных системах CSRF стоит учитывать.
- Настроили публичные и защищенные маршруты. Теперь
/public/**доступен для всех, а/admin/**только для администраторов.
Шаг 3: Добавляем пользователей в память
Сейчас мы добавим простую настройку для хранения пользователей в памяти. Это полезно на этапе разработки, чтобы быстро настроить доступ.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER") // {noop} означает, что пароль не зашифрован
.and()
.withUser("admin").password("{noop}admin").roles("ADMIN");
}
Теперь у нас есть два пользователя: "user" с паролем "password", который принадлежит роли USER, и "admin" с паролем "admin", который является ADMIN.
Шаг 4: Добавляем JWT
Теперь давайте добавим токенизацию с помощью JWT. JWT заменит базовую аутентификацию и позволит нам гибко управлять доступом.
Шаг 4.1: Добавляем JWT-зависимости
Обновим pom.xml, чтобы добавить библиотеку для работы с JWT:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
Шаг 4.2: Создаем утилиту для работы с JWT
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class JwtUtil {
private static final String SECRET_KEY = "super_secret_key";
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 часов
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public String validateToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody().getSubject();
}
}
Шаг 4.3: Настраиваем фильтр для JWT-аутентификации
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JwtFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
public JwtFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
String username = jwtUtil.validateToken(token);
if (username != null) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
}
Итоги
Теперь наше приложение готово работать с JWT для аутентификации и защиты микросервисов. Вы можете протестировать API с помощью инструмента вроде Postman или curl, передавая JWT с запросами. Adieu, инъекции SQL в заголовках запросов! Spring Security здесь, чтобы всё обезопасить.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ