1. Spring Security Basics
Spring Security = фильтры вокруг приложения, которые решают:
- кто может войти (Authentication),
- что он может делать (Authorization).
📌 Подключение:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>⚡ После добавления этой зависимости:
- Spring автоматически защищает все эндпоинты.
- По умолчанию включается Basic Auth → логин = user, пароль генерируется при старте (в логах).
2. Basic Auth
📄 Настройка:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain security(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic(); // Basic Auth
return http.build();
}
}Теперь:
- GET /api/public/** → доступен всем
- остальные → требуют Basic Auth.
3. Form Login
http.formLogin(withDefaults());При попытке входа пользователь будет видеть стандартную HTML-форму.
4. UserDetailsService (свои пользователи)
@Service
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) {
return User.withUsername(username)
.password("{noop}password") // {noop} = без шифрования
.roles("USER")
.build();
}
}5. Password Encoding
Никогда не храни пароли в plain text.
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
String encoded = passwordEncoder.encode("secret");
System.out.println(encoded);
// $2a$10$F5n2k...6. JWT Authentication
📄 Зависимость:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>📄 Генерация токена:
String token = Jwts.builder()
.setSubject("user1")
.claim("role", "USER")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60))
.signWith(SignatureAlgorithm.HS256, "secret-key")
.compact();📄 Фильтр для проверки токена:
@Component
public class JwtFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String authHeader = req.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String jwt = authHeader.substring(7);
Claims claims = Jwts.parser()
.setSigningKey("secret-key")
.parseClaimsJws(jwt)
.getBody();
String username = claims.getSubject();
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(username, null, List.of());
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(req, res);
}
}📄 Подключение фильтра:
@Bean
public SecurityFilterChain security(HttpSecurity http, JwtFilter jwtFilter) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}Теперь API принимает токен:
Authorization: Bearer <jwt>
7. Role-based Access Control
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String admin() {
return "Hello Admin!";
}📌 Включить проверку аннотаций:
@EnableMethodSecurity
public class SecurityConfig {}8. OAuth2, OpenID Connect
📄 Зависимость:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>📄 Настройка (пример для Google):
spring:
security:
oauth2:
client:
registration:
google:
client-id: <id>
client-secret: <secret>
scope: profile,email📌 Теперь пользователь может логиниться через Google.
9. API Key (простейшая защита)
@Component
public class ApiKeyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String apiKey = req.getHeader("X-API-KEY");
if (!"secret-key".equals(apiKey)) {
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
chain.doFilter(req, res);
}
}10. Best Practices
- ✅ Никогда не храни пароли в plain text → только BCrypt.
- ✅ Для REST API используй JWT, для корпоративных порталов → OAuth****2/OpenID.
- ✅ Защищай API → по умолчанию все закрыты.
- ✅ Логику авторизации (roles/permissions) выноси в аннотации @PreAuthorize.
- ✅ Не выключай CSRF для веб-форм, но для REST API обычно CSRF отключают.
- ✅ Разделяй auth эндпоинты (/api/auth/) от бизнес-логики (/api/).