Ответ
Реализовывал различные механизмы аутентификации и авторизации в Java-приложениях:
1. JWT-аутентификация (Stateless)
// Генерация токена
String token = Jwts.builder()
.setSubject(username)
.claim("roles", roles)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
// Валидация в фильтре
public class JwtFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) {
String token = extractToken(request);
if (token != null && validateToken(token)) {
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, null, authorities);
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(request, response);
}
}
2. OAuth2 с Spring Security
# application.yml
spring:
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_SECRET}
scope: profile,email
3. Базовая аутентификация для микросервисов
@Configuration
@EnableWebSecurity
public class BasicAuthConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.httpBasic(withDefaults())
.csrf().disable();
return http.build();
}
}
4. Ролевая модель и безопасность
- Хеширование паролей: BCrypt с солью
- Защита от атак: CSRF-токены, CORS-политики, заголовки безопасности
- Хранение сессий: Redis для распределенных приложений
- Авторизация: аннотации
@PreAuthorize,@PostAuthorize
5. Двухфакторная аутентификация (2FA)
- Генерация TOTP-кодов через Google Authenticator
- Отправка кодов по SMS/email
- Резервные коды для восстановления
Ответ 18+ 🔞
А, ну это ж моя любимая тема — как заставить пользователей доказывать, что они не верблюды, прежде чем пустить их в систему! Сижу тут, вспоминаю, как я эти штуки городил, блядь. Прямо ностальгия накрывает, ёпта!
Вот смотри, бывало, начнёшь проект, а там: «Сделай нам безопасность». А ты такой: «Окей». А они: «Ну чтобы надёжно». А ты: «Ну JWT, наверное?» А они: «А что это? Ну давай». И понеслась, сука.
Первое, JWT — это как пропуск в закрытый клуб, только бумажный и его можно подделать в гараже, если знать секретный пароль от принтера.
Вот смотри, как эту хуйню генерируешь:
// Генерация токена
String token = Jwts.builder()
.setSubject(username)
.claim("roles", roles)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
Выдал ты пользователю этот токен, а он его таскает в каждом запросе, как талончик в столовой. А на стороне сервера стоит фильтр — этакий суровый вышибала с лицом кирпичом:
public class JwtFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) {
String token = extractToken(request);
if (token != null && validateToken(token)) {
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, null, authorities);
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(request, response);
}
}
И этот вышибала каждый раз проверяет: а не просрочен ли твой талончик? А не подделан ли он? А ты вообще из того ли отдела, чтобы в этот бар пускать? Stateless, блядь, красота — серверу вообще похуй, кто ты, он только подпись проверяет. Пока секретный ключ не утек, живём.
Дальше, OAuth2. Это когда ты такой: «Э, Google/Гитхаб/ВК, скажи-ка, это действительно Вася Пупкин?» А они: «Да, это он, вот его почта и аватарка с котиком». И ты пускаешь Васю, даже не спрашивая пароль. Удобно, ёпта! В конфиге просто прописываешь:
spring:
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_SECRET}
scope: profile,email
И всё, пользователь тыкает кнопку «Войти через Гугл» — и он уже твой. Главное, чтобы этот самый GOOGLE_SECRET не закоммитить в публичный репозиторий, а то будет пиздец и овердохуища смеху, когда к тебе в админку начнут ломиться все желающие.
А вот базовая аутентификация — это для настоящих ценителей минимализма и боли. Для микросервисов, которые между собой общаются. Настраивается в три строки, но это пиздец как небезопасно, если не поверх HTTPS.
@Configuration
@EnableWebSecurity
public class BasicAuthConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.httpBasic(withDefaults())
.csrf().disable();
return http.build();
}
}
Отправляешь логин и пароль, просто склеенные через двоеточие и закодированные в Base64, в каждом запросе. Элегантно? Нет. Просто? Да. Как топор, блядь.
Ну и куда же без ролевой модели и прочей безопасности. Тут целый цирк:
- Пароли хешируешь через BCrypt, чтобы даже если базу утянут, то это была бы не база, а месиво из рандомных символов. Соль, блядь, главное — соль не забыть!
- Защита от CSRF, CORS — это такие заголовки, которые говорят браузеру: «Э, дружок, этот запрос со стороннего сайта выглядит подозрительно, давай-ка не будем». Без них — как без штанов на людях.
- Сессии в Redis — чтобы если у тебя десять инстансов приложения, пользователя не выкидывало из системы при каждом запросе на другой сервер. Все сервера смотрят в одну копилку.
- Авторизация через
@PreAuthorize("hasRole('ADMIN')")— это когда ты прямо в коде метода пишешь: «Слушай, Spring, пускать сюда только админов, а всех остальных — нахуй». Удобно, ёпта.
И вишенка на торте — двухфакторка (2FA). Это когда ты уже ввёл логин и пароль, а система такая: «Погоди, дружок-пирожок. А докажи ещё, что это ты! Введи-ка код из приложения на телефоне». Пользователи, конечно, орут, что это неудобно, но зато когда их аккаунт не скомпрометируют — благодарят. TOTP-коды, SMS-ки, резервные одноразовые коды... Целый арсенал, чтобы усложнить жизнь хакерам и немного — своим же юзерам.
Вот так и живём, блядь. Строим крепости, роем рвы, ставим вышибал и просим пароль от второго телефона. А всё почему? Потому что доверия в интернете — ноль ебать.