DOing
[Spring Security] 로그인 성공, 실패 - 2 본문
저번 포스팅에서는 로그인과 로그아웃 기능을 구현해보았다.
이번 포스팅에서는 로그인의 성공, 실패 기능을 구현하겠다. 단순히 특정 페이지로 이동할 수도 있고 Handler를 통해서 특정한 로직을 수행하도록 할 수도 있다. 더불어서 로그인은 성공했으나, 사용자가 권한이 없을때 나오는 접근 제한 페이지와 로직도 구현해보자.
💡 <security:form-login>이용하기
기본으로 제공되는 로그인 페이지는 위의 그림과 같다. 만약 직접 커스텀을 하고 싶다면 security-context.xml에 아래의 코드를 추가해준다.
<security:http auto-config="true" use-expressions="true">
<security:form-login login-page="/customLogin"/>
</security:http>
이 외에도 <security:form-login>태그에서는 로그인에 관련된 다른 일들을 할 수 있다.
<security:http auto-config="true" use-expressions="true">
<security:form-login username-parameter="email" password-parameter="userpw"
login-processing-url="/mypage/login" login-page="/mypage/customlogin"
default-target-url="/" authentication-failure-url="/mypage/login"/>
</security:http>
- username-parameter : 입력한 ID에 대한 파라미터명
- password-parameter : 입력한 PW에 대한 파라미터명
- login-processing-url : View 페이지의 <form action=" "> 에서 지정한 URL
- login-page : 로그인 페이지 URL
- default-target-url : 로그인에 성공 페이지 URL
- authentication-failure-url : 로그인에 실패 페이지 URL
1. 로그인 성공 로직 구현하기
지금까지는 단순히 로그인에 성공하면 단순하게 특정 페이지로 이동시켰다. 하지만 로그인 성공 이후에 특정한 동작을 수행하도록 할 수도 있다.
로그인에 성공하면 사용자의 권한을 체크하여 member의 권한이라면 sample/member페이지로 이동시키고 admin의 권한이라면 sample/admin페이지로 이동시키는 로직을 구현해보자.
이런 경우에는 AuthenticationSuccessHandler라는 인터페이스를 구현해서 사용할 수 있다. AuthenticationSuccessHandler말고 SavedRequestAwareAuthenticationSuccessHandler를 이용할 수도 있다. 이 클래스는 사용자가 원래 보려고 했던 페이지의 정보를 유지해서 로그인 후에 다시 원했던 페이지로 이동하는 방식이다.
@Service
public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
log.warn("login success");
List<String> roleNames = new ArrayList<>();
// 사용자가 가진 모든 권한을 문자열로 체크한다!
// 사용자는 여러권한을 가졌을 수 있다.
authentication.getAuthorities().forEach(authority->{
roleNames.add(authority.getAuthority());
});
log.warn("ROLE NAMES: "+roleNames);
if(roleNames.contains("ROLE_ADMIN")) {
response.sendRedirect("/sample/admin");
return;
}
if(roleNames.contains("ROLE_MEMBER")) {
response.sendRedirect("/sample/member");
return;
}
response.sendRedirect("/");
}
}
AuthenticationSuccessHandler를 구현시킨 클래스를 security-context.xml에 빈으로 등록시킨다.
등록시킨 빈을 <security:form-login>태그에 다음과 같이 설정하자.
<bean id = "customLoginSuccess" class="com.phonemall.security.CustomLoginSuccessHandler"></bean>
<security:http auto-config="true" use-expressions="true">
<security:form-login username-parameter="email" password-parameter="userpw"
login-processing-url="/mypage/login" login-page="/mypage/customlogin"
authentication-success-handler-ref="customLoginSuccess" authentication-failure-url="/mypage/login"/>
</security:http>
2. 로그인 실패 로직 구현하기
같은 원리로 로그인 실패 로직을 구현해보자. 이번에는 AuthenticationFailureHandler를 라는 인터페이스를 구현하면 된다. 로그인을 실패하는 원인에는 아이디가 틀렸을 수도 있고 비밀번호가 틀렸을 수도 있고 비활성화된 계정일 수 있다.
이런 경우들을 로그인일 실패했을때 사용자에게 알려주는 로직을 만들어 보자.
@Service
public class CustomLoginFailHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException accessException) throws IOException, ServletException {
log.error("Access Denied Handler");
log.error("Redirect....");
if (accessException instanceof AuthenticationServiceException) {
request.setAttribute("error", "존재하지 않는 사용자입니다.");
} else if(accessException instanceof BadCredentialsException) {
request.setAttribute("error", "비밀번호가 틀립니다.");
} else if(accessException instanceof LockedException) {
request.setAttribute("error", "잠긴 계정입니다..");
} else if(accessException instanceof DisabledException) {
request.setAttribute("error", "비활성화된 계정입니다..");
} else if(accessException instanceof AccountExpiredException) {
request.setAttribute("error", "만료된 계정입니다..");
} else if(accessException instanceof CredentialsExpiredException) {
request.setAttribute("error", "비밀번호가 만료되었습니다.");
}
// 로그인 페이지로 다시 포워딩
RequestDispatcher dispatcher = request.getRequestDispatcher("/mypage/customlogin");
dispatcher.forward(request, response);
}
}
동일하게 security-context.xml에 빈으로 등록시킨다.
등록시킨 빈을 <security:form-login>태그에 다음과 같이 설정하자.
<bean id = "customLoginSuccess" class="com.phonemall.security.CustomLoginSuccessHandler"></bean>
<bean id = "customLoginFail" class="com.phonemall.security.CustomLoginFailHandler"/>
<security:http auto-config="true" use-expressions="true">
<security:form-login username-parameter="email" password-parameter="userpw"
login-processing-url="/mypage/login" login-page="/mypage/customlogin"
authentication-success-handler-ref="customLoginSuccess" authentication-failure-handler-ref="customLoginFail"/>
</security:http>
💡 접근 제한 페이지
로그인을 한 일반 계정이 관리자 페이지에 접근하려 할 수 있다. 이때는 다음과 같은 403에러 페이지가 뜨게 되는데 사용자 입장에서 조금 더 깔끔한 페이지를 제공하면 좋을 것이다. security-context.xml에 태그를 추가해보자.
<bean id = "customLoginSuccess" class="com.phonemall.security.CustomLoginSuccessHandler"></bean>
<bean id = "customLoginFail" class="com.phonemall.security.CustomLoginFailHandler"/>
<security:http auto-config="true" use-expressions="true">
<security:form-login username-parameter="email" password-parameter="userpw"
login-processing-url="/mypage/login" login-page="/mypage/customlogin"
authentication-success-handler-ref="customLoginSuccess" authentication-failure-handler-ref="customLoginFail"/>
<security:access-denied-handler error-page="/mypage/accessError" />
</security:http>
1. 접근 제한 로직 구현하기
마찬가지로 단순하게 페이지를 제공하는 것이 아닌 특정한 동작을 수행하게 만들고싶을 수 있다. 이번에는 AccessDeniedHandler 인터페이스를 구현하면된다.
@Service
public class CustomAccessDeniedHandler implements AccessDeniedHandler{
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.sendRedirect("/mypage/accessError");
}
}
마지막으로 security-context.xml에 빈으로 등록시키고 태그를 추가하자.
<bean id = "customLoginSuccess" class="com.phonemall.security.CustomLoginSuccessHandler"></bean>
<bean id = "customLoginFail" class="com.phonemall.security.CustomLoginFailHandler"/>
<bean id = "customAccessDenied" class="com.phonemall.security.CustomAccessDeniedHandler"></bean>
<security:http auto-config="true" use-expressions="true">
<security:form-login username-parameter="email" password-parameter="userpw"
login-processing-url="/mypage/login" login-page="/mypage/customlogin"
authentication-success-handler-ref="customLoginSuccess" authentication-failure-handler-ref="customLoginFail"/>
<security:access-denied-handler ref="customAccessDenied" />
</security:http>
'Spring' 카테고리의 다른 글
[Spring Boot] 세션저장소 (0) | 2021.04.11 |
---|---|
[Spring Security] 소셜 로그인 - 구글 (0) | 2021.04.10 |
[Spring Security] 로그인 로그아웃 - 1 (0) | 2021.04.04 |
[Spring] Spring Security (0) | 2021.04.03 |
[Spring] @RestController (0) | 2021.04.02 |