[Spring] What, How - Security에서 FormLogin과 Principal.

하쮸·2025년 10월 4일

Error, Why, What, How

목록 보기
42/70

1. Architecture


Servlet Authentication Architecture


2. FormLogin

  • 스프링 시큐리티의 FormLogin 방식에 대해서 구글링을 하다보면 위에서 본 아키텍처에서 UsernamePasswordAuthenticationToken은 빠질 수 없는 주제임.
    • 그래서 FormLogin 방식을 디버깅을 통해 내부 구조를 간략하게 살펴보고 기록해놓기.

  • UsernamePasswordAuthenticationToken에 브레이크 포인트를 걸어놓고 디버깅.

    • UsernamePasswordAuthenticationFilter에서 UsernamePasswordAuthenticationTokenunauthenticated()메서드가 가장 먼저 호출되고 UsernamePasswordAuthenticationToken의 생성자를 호출함.


  • 생성자를 통해 사용자로 부터 입력받은 아이디, 비밀번호가 각각 principal, credentials 멤버필드에 값이 할당됨.

  • 반환값은 UsernamePasswordAuthenticationToken타입의 authRequest에 저장.
    • 상단을 보면 DEAFULT_ANT_PATH_REQUEST_MATCHER가 있는데 "/login", (httpMethod)"POST"를 확인할 수 있음.

  • authRequest를 보면 아이디, 비밀번호만 할당되어 있는 상황.

  • this.setDetails()메서드를 통해 세션id를 할당 받은 모습.

  • 그 후 this.getAuthenticationManager().authenticate()에 authRequest를 담아서 호출.

  • 그러면 UserDetails의 구현체에 오버라이딩 되어있는 getAuthorities()가 호출됨.


  • 그 다음 AbstractUserDetailsAuthenticationProvidercreateSuccessAuthentication()메서드가 호출되고 이 메서드의 매개변수에 할당된 값은 아래와 같음.

  • 메서드 내부에서 UsernamePasswordAuthenticationTokenauthenticated()메서드가 호출됨.
UsernamePasswordAuthenticationToken.authenticated(principal, authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));

  • 처음에는 unauthenticated()메서드에 아이디, 비밀번호만 사용됐지만
    이번에는 authenticated()메서드에 아이디, 비밀번호, Collection 타입의 권한까지 받고 있는 모습.
    • 생성자를 호출해서 멤버필드에 값을 할당하며
      이전에는 super()를 호출하면서 null을 전달했지만
      이번에는 super()를 통해 Collection 타입의 권한을 전달해줌.
    • 그리고 this.setAuthenticated(true)를 호출함.
      • setAuthenticated()는 AbstractAuthenticationToken이라는 추상 클래스에 있음.

  • AbstractAuthenticationToken 코드 하단 쪽을 확인.
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
    private final Collection<GrantedAuthority> authorities;
    private Object details;
    private boolean authenticated = false;

    public AbstractAuthenticationToken(Collection<? extends GrantedAuthority> authorities) {
        if (authorities == null) {
            this.authorities = AuthorityUtils.NO_AUTHORITIES;
        } else {
            Iterator var2 = authorities.iterator();

            while(var2.hasNext()) {
                GrantedAuthority a = (GrantedAuthority)var2.next();
                Assert.notNull(a, "Authorities collection cannot contain any null elements");
            }

            this.authorities = Collections.unmodifiableList(new ArrayList(authorities));
        }
    }

    public Collection<GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    public String getName() {
        Object var2 = this.getPrincipal();
        if (var2 instanceof UserDetails userDetails) {
            return userDetails.getUsername();
        } else {
            var2 = this.getPrincipal();
            if (var2 instanceof AuthenticatedPrincipal authenticatedPrincipal) {
                return authenticatedPrincipal.getName();
            } else {
                var2 = this.getPrincipal();
                if (var2 instanceof Principal principal) {
                    return principal.getName();
                } else {
                    return this.getPrincipal() == null ? "" : this.getPrincipal().toString();
                }
            }
        }
    }

    public boolean isAuthenticated() {
        return this.authenticated;
    }

    public void setAuthenticated(boolean authenticated) {
        this.authenticated = authenticated;
    }
    
    			....
                
}

  • logger.debug("Authenticated user")를 출력하고 result(UsernamePasswordAuthenticationToken 타입)을 리턴함.

3. Principal

  • 컨트롤러의 매개변수에 Principal을 넣어놓고 디버깅.

  • 매개변수로 받은 Principal 객체 내부에는
    인증된 사용자 객체(principal, siteUser(User 엔터티)), 권한(authorities), details(세션id), authenticated(=true)가 들어있음.

4. 읽어볼 것.

profile
Every cloud has a silver lining.

0개의 댓글