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. 사용자 지정 성공 처리기 추가
이제 user 와 admin 이라는 두 가지 역할을 가진 두 명의 사용자가 있습니다 . 로그인에 성공하면 둘 다 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 에서 사용할 수 있습니다 . 메이븐 기반의 프로젝트이기 때문에 그대로 임포트하고 실행하기 쉬워야 합니다.