Какие механизмы аутентификации и авторизации вы реализовывали в Java-приложениях?

Ответ

Реализовывал различные механизмы аутентификации и авторизации в 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-ки, резервные одноразовые коды... Целый арсенал, чтобы усложнить жизнь хакерам и немного — своим же юзерам.

Вот так и живём, блядь. Строим крепости, роем рвы, ставим вышибал и просим пароль от второго телефона. А всё почему? Потому что доверия в интернете — ноль ебать.