← Назад к вопросам

Что такое аутентификация?

1.0 Junior🔥 231 комментариев
#Безопасность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Аутентификация: основные концепции и реализация

Аутентификация (Authentication) — это процесс проверки личности пользователя. Система подтверждает, что пользователь действительно тот, за кого себя выдаёт.

Определение

АутентификацияАвторизация

Аутентификация: "Кто ты?" — проверка личности
Авторизация: "Что ты можешь делать?" — проверка прав доступа

Примеры:
1. Вы входите в свой банк (аутентификация)
   ↓
2. Вам показывают счета, которые вам принадлежат (авторизация)

Основные методы аутентификации

1. Username + Password (самый распространенный)

// Простой пример: проверка username/password
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
        // 1. Получаем пользователя из БД
        User user = userRepository.findByUsername(request.getUsername())
            .orElseThrow(() -> new UnauthorizedException("User not found"));
        
        // 2. Проверяем пароль
        if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
            throw new UnauthorizedException("Invalid password");
        }
        
        // 3. Пользователь аутентифицирован
        return ResponseEntity.ok(new LoginResponse(user.getId(), user.getUsername()));
    }
}

// ❌ НЕПРАВИЛЬНО: хранить пароли в открытом виде
public class User {
    private String password; // "12345" в БД — ОПАСНО!
}

// ✅ ПРАВИЛЬНО: хеширование пароля
@Component
public class PasswordEncoderConfig {
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // Хеширует пароль
    }
}

public class UserService {
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    public void registerUser(String username, String rawPassword) {
        String hashedPassword = passwordEncoder.encode(rawPassword);
        // В БД сохраняем хеш: $2a$10$N9qo8uLOickgx2ZMRZoMye...
        User user = new User(username, hashedPassword);
        userRepository.save(user);
    }
}

2. Token-based Authentication (JWT)

После успешной аутентификации сервер выдаёт токен, который клиент отправляет при каждом запросе.

// JWT токен: Header.Payload.Signature
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
// eyJzdWIiOiIxMjM0NTY3ODkwIn0.
// dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U

@Service
public class JwtTokenProvider {
    
    private static final String SECRET_KEY = "my-secret-key";
    private static final long EXPIRATION_TIME = 3600000; // 1 час
    
    // Создание токена
    public String generateToken(User user) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", user.getId());
        claims.put("username", user.getUsername());
        claims.put("roles", user.getRoles());
        
        return Jwts.builder()
            .setClaims(claims)
            .setSubject(user.getUsername())
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
            .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
            .compact();
    }
    
    // Проверка и извлечение данных из токена
    public String getUsernameFromToken(String token) {
        return Jwts.parser()
            .setSigningKey(SECRET_KEY)
            .parseClaimsJws(token)
            .getBody()
            .getSubject();
    }
    
    public boolean isTokenValid(String token) {
        try {
            Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            return false; // Токен истёк или неверный
        }
    }
}

// Использование в Spring Security
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    
    @Autowired
    private JwtTokenProvider tokenProvider;
    
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
        // Аутентификация (проверка username/password)
        User user = authenticateUser(request.getUsername(), request.getPassword());
        
        // Генерируем JWT токен
        String token = tokenProvider.generateToken(user);
        
        return ResponseEntity.ok(new LoginResponse(
            user.getId(),
            user.getUsername(),
            token // Отправляем токен клиенту
        ));
    }
}

// Фильтр для проверки токена в каждом запросе
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
    @Autowired
    private JwtTokenProvider tokenProvider;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) 
            throws ServletException, IOException {
        
        try {
            String jwt = getJwtFromRequest(request); // Извлекаем токен из заголовка
            
            if (jwt != null && tokenProvider.isTokenValid(jwt)) {
                String username = tokenProvider.getUsernameFromToken(jwt);
                UserDetails userDetails = loadUserDetails(username);
                
                // Установить аутентификацию в контекст Spring Security
                UsernamePasswordAuthenticationToken authentication = 
                    new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception ex) {
            logger.error("Could not set user authentication", ex);
        }
        
        filterChain.doFilter(request, response);
    }
    
    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7); // Извлекаем токен
        }
        return null;
    }
}

// Защита endpoints
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping("/me")
    @PreAuthorize("isAuthenticated()") // Требует аутентификацию
    public ResponseEntity<?> getCurrentUser() {
        String username = SecurityContextHolder.getContext()
            .getAuthentication().getName();
        return ResponseEntity.ok(userRepository.findByUsername(username));
    }
    
    @DeleteMapping("/{id}")
    @PreAuthorize("hasRole('ADMIN')") // Требует роль ADMIN
    public ResponseEntity<?> deleteUser(@PathVariable Long id) {
        userRepository.deleteById(id);
        return ResponseEntity.ok("User deleted");
    }
}

3. OAuth 2.0 (для интеграции с третьими сервисами)

// Вход через Google, GitHub, Facebook
@Configuration
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/login**").permitAll()
                .anyRequest().authenticated()
            .and()
            .oauth2Login() // Включаем OAuth 2.0
                .loginPage("/login")
                .defaultSuccessUrl("/home")
            .and()
            .csrf().disable();
        
        return http.build();
    }
}

// application.properties
// spring.security.oauth2.client.registration.google.client-id=...
// spring.security.oauth2.client.registration.google.client-secret=...

// Использование в HTML
// <a href="/oauth2/authorization/google">Login with Google</a>

4. Multi-Factor Authentication (MFA)

// Двухфакторная аутентификация: пароль + SMS
@Service
public class MfaService {
    
    @Autowired
    private SmsService smsService;
    
    // Шаг 1: Проверяем username/password
    public void sendMfaCode(String username) {
        User user = userRepository.findByUsername(username)
            .orElseThrow(() -> new NotFoundException("User not found"));
        
        // Генерируем код
        String code = generateRandomCode(); // "123456"
        
        // Сохраняем код с expiration time
        MfaToken mfaToken = new MfaToken(
            user.getId(),
            code,
            LocalDateTime.now().plusMinutes(5) // Код действителен 5 минут
        );
        mfaRepository.save(mfaToken);
        
        // Отправляем код на телефон
        smsService.sendSms(user.getPhoneNumber(), 
            "Your verification code: " + code);
    }
    
    // Шаг 2: Проверяем введённый код
    public String verifyMfaCode(String username, String code) {
        User user = userRepository.findByUsername(username)
            .orElseThrow();
        
        MfaToken mfaToken = mfaRepository.findByUserIdAndCode(
            user.getId(), code
        ).orElseThrow(() -> new InvalidCodeException("Invalid code"));
        
        // Проверяем, не истёк ли код
        if (mfaToken.getExpirationTime().isBefore(LocalDateTime.now())) {
            throw new ExpiredCodeException("Code expired");
        }
        
        // Код верный — генерируем JWT
        mfaRepository.delete(mfaToken); // Удаляем использованный код
        
        return jwtTokenProvider.generateToken(user);
    }
}

// Flow:
// 1. POST /auth/login (username, password)
// 2. Сервер проверяет пароль → отправляет SMS
// 3. Клиент получает код
// 4. POST /auth/verify-mfa (username, code)
// 5. Сервер проверяет код → выдаёт JWT токен

5. Biometric Authentication (отпечатки пальцев, лицо)

// Используется на мобильных приложениях
// Spring Security поддерживает через WebAuthn

@RestController
@RequestMapping("/api/biometric")
public class BiometricController {
    
    @PostMapping("/register")
    public ResponseEntity<?> registerBiometric(
            @RequestBody BiometricData biometricData) {
        // Сохраняем биометрические данные
        User user = getCurrentUser();
        user.setBiometricData(encryptBiometricData(biometricData));
        userRepository.save(user);
        
        return ResponseEntity.ok("Biometric registered");
    }
    
    @PostMapping("/authenticate")
    public ResponseEntity<?> authenticateWithBiometric(
            @RequestBody BiometricData biometricData) {
        // Сравниваем с сохранённой биометрией
        User user = getCurrentUser();
        
        if (matchesBiometric(biometricData, user.getBiometricData())) {
            String token = jwtTokenProvider.generateToken(user);
            return ResponseEntity.ok(new LoginResponse(token));
        }
        
        throw new UnauthorizedException("Biometric does not match");
    }
}

Полный flow аутентификации в Spring Boot

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // 1. Конфигурируем доступ к endpoints
            .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll() // Публичные endpoints
                .antMatchers("/api/admin/**").hasRole("ADMIN") // Только для ADMIN
                .anyRequest().authenticated() // Все остальные требуют аутентификацию
            .and()
            // 2. Добавляем JWT фильтр
            .addFilterBefore(jwtAuthenticationFilter(), 
                UsernamePasswordAuthenticationFilter.class)
            .and()
            // 3. Отключаем CSRF для REST API
            .csrf().disable()
            .and()
            // 4. Настраиваем обработку ошибок
            .exceptionHandling()
                .authenticationEntryPoint((request, response, e) -> {
                    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, 
                        "Unauthorized");
                })
                .accessDeniedHandler((request, response, e) -> {
                    response.sendError(HttpServletResponse.SC_FORBIDDEN, 
                        "Forbidden");
                });
        
        return http.build();
    }
    
    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }
}

Сравнение методов аутентификации

МетодБезопасностьУдобствоИспользование
Username + PasswordСредняяВысокоеВеб, мобил
JWTВысокаяВысокоеREST API, SPA
OAuth 2.0Очень высокаяВысокоеСоциальные сети
MFAОчень высокаяНизкоеБанки, корпорации
BiometricОчень высокаяОчень высокоеМобильные приложения

Best Practices

  1. Никогда не передавай пароли в открытом виде

    // ❌ Неправильно
    String url = "https://api.example.com/login?password=123456";
    
    // ✅ Правильно: используй POST с HTTPS
    POST /api/auth/login
    Host: api.example.com
    Content-Type: application/json
    
    {"username": "john", "password": "123456"}
    
  2. Всегда хешируй пароли

    passwordEncoder.encode(rawPassword); // BCrypt
    
  3. Используй HTTPS

    Протокол HTTP отправляет данные в открытом виде!
    Всегда используй HTTPS (SSL/TLS)
    
  4. Устанавливай expiration time для токенов

    setExpiration(new Date(System.currentTimeMillis() + 3600000)); // 1 час
    
  5. Используй HttpOnly cookies для JWT

    // Защита от XSS атак
    response.addCookie(new HttpCookie("jwt", token, 
        maxAge, path, domain, secure=true, httpOnly=true));
    

Итого

Аутентификация — это процесс проверки личности пользователя. В Java приложениях обычно используют:

  • Spring Security для управления аутентификацией
  • JWT для token-based authentication
  • BCrypt для хеширования паролей
  • HTTPS для защиты данных в пути

Высокая безопасность требует комбинации методов: strong passwords + JWT + HTTPS + MFA.

Что такое аутентификация? | PrepBro