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

Что такое респонс-код 401?

2.0 Middle🔥 111 комментариев
#SOLID и паттерны проектирования#Spring Boot и Spring Data

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

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

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

HTTP статус-код 401 Unauthorized

401 Unauthorized — это HTTP статус-код, который указывает, что запрос требует аутентификации (authentication), но клиент либо не предоставил учётные данные, либо предоставил недействительные. Это означает, что сервер не смог идентифицировать личность клиента.

Разница между 401 и 403

Часто путают 401 и 403:

  • 401 Unauthorized — клиент не аутентифицирован (не прошёл проверку учётных данных)
  • 403 Forbidden — клиент аутентифицирован, но не имеет прав доступа к ресурсу
401 = Кто ты? (нужна аутентификация)
403 = Я знаю кто ты, но ты не можешь это делать (нет авторизации)

Сценарии возврата 401

1. Отсутствие токена аутентификации

// REST контроллер, защищённый от неавторизованного доступа
@RestController
@RequestMapping("/api/v1")
public class UserController {
    
    @GetMapping("/me")
    public ResponseEntity<UserDTO> getCurrentUser(
            @RequestHeader(value = "Authorization", required = false) String authHeader) {
        
        if (authHeader == null || authHeader.isEmpty()) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }
        
        String token = authHeader.replace("Bearer ", "");
        
        try {
            User user = authenticateToken(token);
            return ResponseEntity.ok(new UserDTO(user));
        } catch (InvalidTokenException e) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }
    }
}

2. Истёк срок действия токена

@Component
public class JwtValidator {
    
    public boolean isTokenValid(String token) {
        try {
            Claims claims = Jwts.parserBuilder()
                .setSigningKey(secretKey)
                .build()
                .parseClaimsJws(token)
                .getBody();
            
            // Проверка срока истечения
            if (claims.getExpiration().before(new Date())) {
                throw new ExpiredJwtException(null, null, "Token expired");
            }
            
            return true;
        } catch (ExpiredJwtException e) {
            // Вернуть 401
            return false;
        } catch (SignatureException | MalformedJwtException e) {
            // Вернуть 401
            return false;
        }
    }
}

3. Неправильные учётные данные (Basic Auth)

@RestController
public class LoginController {
    
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
        User user = userRepository.findByUsername(request.getUsername()).orElse(null);
        
        if (user == null || !passwordEncoder.matches(request.getPassword(), user.getPassword())) {
            // 401 — не смогли аутентифицировать
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .body(new ErrorResponse("Invalid username or password"));
        }
        
        String token = generateJwt(user);
        return ResponseEntity.ok(new LoginResponse(token));
    }
}

Обработка 401 в клиентском коде

public class ApiClient {
    
    public UserDTO getUser(String userId) throws IOException, InterruptedException {
        HttpClient client = HttpClient.newHttpClient();
        
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.example.com/users/" + userId))
            .header("Authorization", "Bearer " + getToken())
            .GET()
            .build();
        
        HttpResponse<String> response = client.send(request, 
            HttpResponse.BodyHandlers.ofString());
        
        if (response.statusCode() == 401) {
            // Токен истёк, попробовать обновить
            refreshToken();
            // Повторить запрос с новым токеном
            return getUser(userId);
        }
        
        if (response.statusCode() == 200) {
            return parseJson(response.body(), UserDTO.class);
        }
        
        throw new ApiException("Unexpected status: " + response.statusCode());
    }
}

Spring Security и обработка 401

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .requestMatchers("/api/login", "/api/register").permitAll()
                .requestMatchers("/api/**").authenticated()
                .and()
            .exceptionHandling()
                .authenticationEntryPoint((request, response, authException) -> {
                    // Обработка 401
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    response.setContentType("application/json");
                    response.getWriter().write("{\"error\":\"Unauthorized\"}");
                })
                .and()
            .addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class);
        
        return http.build();
    }
}

Типичные причины 401

ПричинаПример
Отсутствие токенаHeader Authorization не передан
Истёк токенJWT token expired
Неверная подписьТокен подделан
Неверные credentialsНеправильный пароль при Basic Auth
Токен в неправильном формате"Token" вместо "Bearer <token>"
Токен не для этого APIТокен от другого сервиса

REST API Best Practices

// Хорошая практика: вернуть 401 с объяснением
@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<ErrorResponse> handleAuthError(AuthenticationException e) {
    return ResponseEntity
        .status(HttpStatus.UNAUTHORIZED)
        .body(new ErrorResponse(
            "Unauthorized",
            "Authentication required. Please provide valid credentials.",
            HttpStatus.UNAUTHORIZED.value()
        ));
}

// Добавить WWW-Authenticate header
@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<ErrorResponse> handleAuthErrorWithHeader(AuthenticationException e) {
    return ResponseEntity
        .status(HttpStatus.UNAUTHORIZED)
        .header("WWW-Authenticate", "Bearer realm=\"api\"")
        .body(new ErrorResponse("Unauthorized"));
}

Когда использовать 401

  • Клиент не предоставил аутентификационные данные
  • Предоставленные учётные данные недействительны
  • Сессия истекла
  • Токен истёк или поддельный

Понимание 401 статуса критично при работе с защищённые API, OAuth 2.0 и JWT токенами.