AuthenticationFilter.java

package sprout.security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import sprout.beans.InfrastructureBean;
import sprout.beans.annotation.Component;
import sprout.core.filter.Filter;
import sprout.core.filter.FilterChain;
import sprout.mvc.http.*;
import sprout.security.authentication.AuthenticationManager;
import sprout.security.authentication.UsernamePasswordAuthenticationToken;
import sprout.security.authentication.exception.LoginException;
import sprout.security.context.SecurityContextHolder;
import sprout.security.context.SecurityContextImpl;
import sprout.security.core.Authentication;
import sprout.security.core.SecurityContext;
import sprout.security.web.util.matcher.RequestMatcher;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class AuthenticationFilter implements Filter, InfrastructureBean {

    private final List<RequestMatcher> requestMatchers;
    private final AuthenticationManager authenticationManager;
    private final ObjectMapper objectMapper;

    public AuthenticationFilter(List<RequestMatcher> requestMatchers, AuthenticationManager authenticationManager) {
        this.requestMatchers = requestMatchers;
        this.authenticationManager = authenticationManager;
        this.objectMapper = new ObjectMapper();
    }

    @Override
    public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws IOException {
        for (RequestMatcher requestMatcher : requestMatchers) {
            if (requestMatcher.matches(request)) {
                try {
                    if (request.getMethod() != HttpMethod.POST) {
                        ResponseEntity<?> responseEntity = new ResponseEntity<>("Method Not Allowed. Only POST is supported for login.", null, ResponseCode.METHOD_NOT_ALLOWED);
                        response.setResponseEntity(responseEntity);
                        return;
                    }

                    Map<String, Object> requestBody = objectMapper.readValue((String) request.getBody(), Map.class);

                    String username = Optional.ofNullable((String) requestBody.get("username"))
                            .orElseThrow(() -> new LoginException("Username not provided."));
                    String password = Optional.ofNullable((String) requestBody.get("password"))
                            .orElseThrow(() -> new LoginException("Password not provided."));

                    UsernamePasswordAuthenticationToken unauthenticatedToken = new UsernamePasswordAuthenticationToken(
                            username,
                            password
                    );
                    Authentication authenticated = authenticationManager.authenticate(unauthenticatedToken);
                    SecurityContext securityContext = new SecurityContextImpl(authenticated);
                    SecurityContextHolder.setContext(securityContext);

                    ResponseEntity<?> responseEntity = new ResponseEntity<>("Authentication successful!", null, ResponseCode.SUCCESS);
                    response.setResponseEntity(responseEntity);

                } catch (LoginException e) {
                    ResponseEntity<?> responseEntity = new ResponseEntity<>("Authentication failed: " + e.getMessage(), null, ResponseCode.UNAUTHORIZED);
                    response.setResponseEntity(responseEntity);
                } catch (IOException | ClassCastException e) { // JSON 파싱 에러 또는 Body 타입 불일치
                    ResponseEntity<?> responseEntity = new ResponseEntity<>("Invalid request body format.", null, ResponseCode.BAD_REQUEST);
                    response.setResponseEntity(responseEntity);
                } catch (Exception e) {
                    ResponseEntity<?> responseEntity = new ResponseEntity<>("An internal server error occurred during authentication.", null, ResponseCode.BAD_REQUEST);
                    response.setResponseEntity(responseEntity);
                }
                return;
            }
        }
        chain.doFilter(request, response);
    }
}