1. 개요

웹 응용 프로그램에 대한 일반적인 요구 사항은 로그인 후 다양한 유형의 사용자를 다른 페이지로 리디렉션하는 것 입니다. 예를 들어 표준 사용자를 /homepage.html 페이지로 리디렉션하고 관리자 사용자를 /console.html 페이지로 리디렉션하는 것을 예로 들 수 있습니다.

이 기사에서는 Spring Security를 ​​사용하여 이 메커니즘을 빠르고 안전하게 구현하는 방법을 보여줍니다. 이 기사는 또한 프로젝트에 필요한 핵심 MVC 설정을 다루는 Spring MVC 예제 을 기반으로 작성되었습니다.

2. 스프링 Security 설정

Spring Security는 인증 성공 후 무엇을 할지 결정하는 직접적인 책임이 있는 구성 요소인 AuthenticationSuccessHandler 를 제공 합니다.

2.1. 기본 구성

먼저 기본  @Configuration  및 @Service 클래스를 구성해 보겠습니다.

@Configuration
@EnableWebSecurity
public class SecSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            // ... endpoints
            .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                .defaultSuccessUrl("/homepage.html", true)
            // ... other configuration   
        return http.build();
    }
}

이 구성에서 집중해야 할 부분은 defaultSuccessUrl() 메서드입니다. 로그인에 성공하면 모든 사용자가 website.html 로 리디렉션 됩니다 .

또한 사용자와 역할을 구성해야 합니다. 이 기사의 목적을 위해 각각 하나의 단일 역할을 가진 두 명의 사용자가 있는 간단한 UserDetailService 를 구현합니다. 이 주제에 대한 자세한 내용은 Spring Security – Roles and Privileges 기사를 읽어보세요 .

@Service
public class MyUserDetailsService implements UserDetailsService {

    private Map<String, User> roles = new HashMap<>();

    @PostConstruct
    public void init() {
        roles.put("admin2", new User("admin", "{noop}admin1", getAuthority("ROLE_ADMIN")));
        roles.put("user2", new User("user", "{noop}user1", getAuthority("ROLE_USER")));
    }

    @Override
    public UserDetails loadUserByUsername(String username) {
        return roles.get(username);
    }

    private List<GrantedAuthority> getAuthority(String role) {
        return Collections.singletonList(new SimpleGrantedAuthority(role));
    }
}

또한 이 간단한 예에서는 비밀번호 인코더를 사용하지 않으므로 비밀번호 앞에 {noop} 가 붙습니다 .

2.2. 사용자 지정 성공 처리기 추가

이제 useradmin 이라는 두 가지 역할을 가진 두 명의 사용자가 있습니다 . 로그인에 성공하면 둘 다 hompeage.html 로 리디렉션됩니다 . 사용자의 역할에 따라 다른 리디렉션을 가질 수 있는 방법을 살펴보겠습니다.

먼저 사용자 정의 성공 핸들러를 bean으로 정의해야 합니다.

@Bean
public AuthenticationSuccessHandler myAuthenticationSuccessHandler(){
    return new MySimpleUrlAuthenticationSuccessHandler();
}

그리고 defaultSuccessUrl 호출을 우리의 커스텀 성공 핸들러를 매개변수로 받아들이는 successHandler 메소드로 교체하세요:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
        // endpoints
        .formLogin()
            .loginPage("/login.html")
            .loginProcessingUrl("/login")
            .successHandler(myAuthenticationSuccessHandler())
        // other configuration      
    return http.build();
}

2.3. XML 구성

사용자 정의 성공 처리기의 구현을 보기 전에 동등한 XML 구성도 살펴보겠습니다.

<http use-expressions="true" >
    <!-- other configuration -->
    <form-login login-page='/login.html' 
      authentication-failure-url="/login.html?error=true"
      authentication-success-handler-ref="myAuthenticationSuccessHandler"/>
    <logout/>
</http>

<beans:bean id="myAuthenticationSuccessHandler"
  class="com.baeldung.security.MySimpleUrlAuthenticationSuccessHandler" />

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="user1" password="{noop}user1Pass" authorities="ROLE_USER" />
            <user name="admin1" password="{noop}admin1Pass" authorities="ROLE_ADMIN" />
        </user-service>
    </authentication-provider>
</authentication-manager>

3. 사용자 지정 인증 성공 처리기

AuthenticationSuccessHandler 인터페이스 외에도 Spring은 이 전략 구성 요소에 대한 합리적인 기본값인 AbstractAuthenticationTargetUrlRequestHandler 와 간단한 구현인 SimpleUrlAuthenticationSuccessHandler 도 제공 합니다. 일반적으로 이러한 구현은 로그인 후 URL을 결정하고 해당 URL로 리디렉션을 수행합니다.

다소 유연하지만 이 대상 URL을 결정하는 메커니즘은 결정을 프로그래밍 방식으로 수행하는 것을 허용하지 않으므로 인터페이스를 구현하고 성공 핸들러의 사용자 정의 구현을 제공할 것입니다. 이 구현은 사용자의 역할에 따라 로그인 후 사용자를 리디렉션할 URL을 결정합니다. 

우선 onAuthenticationSuccess 메서드를 재정의해야 합니다.

public class MySimpleUrlAuthenticationSuccessHandler
  implements AuthenticationSuccessHandler {
 
    protected Log logger = LogFactory.getLog(this.getClass());

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, 
      HttpServletResponse response, Authentication authentication)
      throws IOException {
 
        handle(request, response, authentication);
        clearAuthenticationAttributes(request);
    }

사용자 정의된 메서드는 두 가지 도우미 메서드를 호출합니다.

protected void handle(
        HttpServletRequest request,
        HttpServletResponse response, 
        Authentication authentication
) throws IOException {

    String targetUrl = determineTargetUrl(authentication);

    if (response.isCommitted()) {
        logger.debug(
                "Response has already been committed. Unable to redirect to "
                        + targetUrl);
        return;
    }

    redirectStrategy.sendRedirect(request, response, targetUrl);
}

다음 방법이 실제 작업을 수행하고 사용자를 대상 URL에 매핑하는 경우:

protected String determineTargetUrl(final Authentication authentication) {

    Map<String, String> roleTargetUrlMap = new HashMap<>();
    roleTargetUrlMap.put("ROLE_USER", "/homepage.html");
    roleTargetUrlMap.put("ROLE_ADMIN", "/console.html");

    final Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
    for (final GrantedAuthority grantedAuthority : authorities) {
        String authorityName = grantedAuthority.getAuthority();
        if(roleTargetUrlMap.containsKey(authorityName)) {
            return roleTargetUrlMap.get(authorityName);
        }
    }

    throw new IllegalStateException();
}

이 메서드는 사용자가 가진 첫 번째 역할에 대해 매핑된 URL을 반환합니다. 따라서 사용자에게 여러 역할이 있는 경우 매핑된 URL은 권한 컬렉션에 제공된 첫 번째 역할과 일치하는 URL이 됩니다.

protected void clearAuthenticationAttributes(HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    if (session == null) {
        return;
    }
    session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}

전략의 핵심인 defineTargetUrl은 단순히 사용자 유형(권한이 결정) 보고 이 역할에 따라 대상 URL을 선택합니다 .

따라서 ROLE_ADMIN 권한 에 의해 결정된 관리자 사용자 는 로그인 후 콘솔 페이지로 리디렉션되는 반면 ROLE_USER 에 의해 결정된 표준 사용자 는 홈페이지로 리디렉션됩니다.

4. 결론

항상 그렇듯이 이 기사에 제공된 코드는 GitHub 에서 사용할 수 있습니다 . 메이븐 기반의 프로젝트이기 때문에 그대로 임포트하고 실행하기 쉬워야 합니다.

Security footer banner