authenticate메서드에서 pw확인 로직을 안넣었더니, 비밀번호 뭘넣든 그냥 승인됨..ㅜ matches함수로 검증로직추가함, 로그인 성공 실패시 적용될 handler클래스와 config설정도 추가해주었음...

This commit is contained in:
Taesan
2020-02-12 15:51:10 +09:00
committed by taesan
parent d8c29ba6c5
commit 4e0e8c798f
5 changed files with 311 additions and 8 deletions

View File

@@ -8,6 +8,7 @@ import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@@ -19,6 +20,9 @@ public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private AccountService accountService;
@Autowired
private PasswordEncoder passwordEncoder;
private Logger log = LoggerFactory.getLogger(this.getClass());
@@ -28,8 +32,15 @@ public class CustomAuthenticationProvider implements AuthenticationProvider {
log.info("### authenticate ### ");
String username = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
String passwordEnc = passwordEncoder.encode(password);
Account account = (Account) accountService.loadUserByUsername(username);
// pw같은지 검증.
if ( !passwordEncoder.matches(password,account.getPassword())) {
throw new BadCredentialsException(username);
}
return new UsernamePasswordAuthenticationToken(account, account, account.getAuthorities());
}

View File

@@ -1,15 +1,27 @@
package com.boot.test1.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import com.boot.test1.handler.CustomAuthenticationFailureHandler;
import com.boot.test1.handler.CustomAuthenticationSuccessHandler;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private AuthenticationSuccessHandler successHandler;
@Autowired
private AuthenticationFailureHandler failureHandler;
@Override
protected void configure(HttpSecurity http) throws Exception{
http
@@ -24,7 +36,9 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter{
.loginPage("/login") // 로그인이 수행될 경로.
.loginProcessingUrl("/loginProcess")// 로그인form의 action과 일치시켜주어야 함.
.defaultSuccessUrl("/loginSuccess") // 로그인 성공 시 이동할 경로.
.failureUrl("/login?error=true") // 인증에 실패했을 때 보여주는 화면 url, 로그인 form으로 파라미터값 error=true로 보낸
//.failureUrl("/login?error=true") // 인증에 실패했을 때 보여주는 화면 url, 로그인 form으로 파라미터값 error=true로 보낸다.
.successHandler(successHandler)
.failureHandler(failureHandler)
.permitAll()
.and()
.logout()
@@ -47,6 +61,17 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter{
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
// 로그인 성공 처리를 위한 Handler
@Bean
public AuthenticationSuccessHandler successHandler() {
return new CustomAuthenticationSuccessHandler("loginRedirect", "/login", false);
}
// 실패 처리를 위한 Handler
@Bean
public AuthenticationFailureHandler failureHandler() {
return new CustomAuthenticationFailureHandler("username", "password" , "loginRedirect" , "securityExceptionMsg" , "/login?fail=true");
}
}

View File

@@ -0,0 +1,76 @@
package com.boot.test1.handler;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
private String loginIdName ;
private String loginPasswordName ;
private String loginRedirectName ;
private String exceptionMsgName ;
private String defaultFailureUrl ;
public CustomAuthenticationFailureHandler(String loginIdName, String loginPasswordName, String loginRedirectName,
String exceptionMsgName, String defaultFailureUrl) {
this.loginIdName = loginIdName;
this.loginPasswordName = loginPasswordName;
this.loginRedirectName = loginRedirectName;
this.exceptionMsgName = exceptionMsgName;
this.defaultFailureUrl = defaultFailureUrl;
}
public String getLoginIdName() {
return loginIdName;
}
public void setLoginIdName(String loginIdName) {
this.loginIdName = loginIdName;
}
public String getLoginPasswordName() {
return loginPasswordName;
}
public void setLoginPasswordName(String loginPasswordName) {
this.loginPasswordName = loginPasswordName;
}
public String getLoginRedirectName() {
return loginRedirectName;
}
public void setLoginRedirectName(String loginRedirectName) {
this.loginRedirectName = loginRedirectName;
}
public String getExceptionMsgName() {
return exceptionMsgName;
}
public void setExceptionMsgName(String exceptionMsgName) {
this.exceptionMsgName = exceptionMsgName;
}
public String getDefaultFailureUrl() {
return defaultFailureUrl;
}
public void setDefaultFailureUrl(String defaultFailureUrl) {
this.defaultFailureUrl = defaultFailureUrl;
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
String loginId = request.getParameter(loginIdName);
String loginPw = request.getParameter(loginPasswordName);
String loginRedirect = request.getParameter(loginRedirectName);
request.setAttribute(loginIdName, loginId);
request.setAttribute(loginPasswordName, loginPw);
request.setAttribute(loginRedirectName, loginRedirect);
request.setAttribute(exceptionMsgName, exception.getMessage());
request.getRequestDispatcher(defaultFailureUrl).forward(request, response);
}
}

View File

@@ -0,0 +1,191 @@
package com.boot.test1.handler;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.util.StringUtils;
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
// RedirectStrategy은 화면을 이동하기위한 인터페이스이다.
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
private RequestCache requestCache = new HttpSessionRequestCache();
private String targetUrlParameter ;
private String defaultUrl ;
private boolean useReferer ;
private Logger log = LoggerFactory.getLogger(this.getClass());
// constructor
public CustomAuthenticationSuccessHandler(String targetUrlParameter, String defaultUrl ,boolean useReferer) {
this.targetUrlParameter=targetUrlParameter;
this.defaultUrl=defaultUrl;
this.useReferer = useReferer;
log.info(" targetUrlParameter : " , targetUrlParameter + ", defaultUrl : " + defaultUrl +", useReferer : " + useReferer );
}
// getter, settger
public String getTargetUrlParameter() {
return targetUrlParameter;
}
public void setTargetUrlParameter(String targetUrlParameter) {
this.targetUrlParameter = targetUrlParameter;
}
public String getDefaultUrl() {
return defaultUrl;
}
public void setDefaultUrl(String defaultUrl) {
this.defaultUrl = defaultUrl;
}
public boolean isUseReferer() {
return useReferer;
}
public void setUseReferer(boolean useReferer) {
this.useReferer = useReferer;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
clearAuthenticationAttributes(request);
int intRedirectStrategy = decideRedirectStrategy(request,response);
switch(intRedirectStrategy) {
case 1:
useTargetUrl(request, response);
break;
case 2:
useSessionUrl(request, response);
break;
case 3:
useRefererUrl(request, response);
break;
default:
useDefaultUrl(request, response);
}
}
/**
*
* 인증 성공 후 어떤 URL로 redirect 할지를 결정한다.
*
* 판단 기준은 targetUrlParameter 값을 읽은 URL이 존재할 경우 그것을 1순위
*
* 1순위 URL이 없을 경우 Spring Security가 세션에 저장한 URL을 2순위
* 2순위 URL이 없을 경우 Request의 REFERER를 사용하고 그 REFERER URL이 존재할 경우 그 URL을 3순위
* 3순위 URL이 없을 경우 Default URL을 4순위로 한다.
*
* @param request
* @param response
* @return 1 : targetUrlParameter 값을 읽은 URL
* 2 : Session에 저장되어 있는 URL
* 3 : referer 헤더에 있는 URL
* 0 : default URL
*/
private int decideRedirectStrategy(HttpServletRequest request, HttpServletResponse response) {
int result = 0 ;
SavedRequest savedRequest = requestCache.getRequest(request, response);
if ( !"".equals(targetUrlParameter) ) {
String targetUrl = request.getParameter(targetUrlParameter);
if(StringUtils.hasText(targetUrl)) {
result = 1;
}else {
if (savedRequest != null ) result=2;
else {
String refererUrl = request.getHeader("REFERER");
if ( useReferer && StringUtils.hasText(refererUrl)) result = 3;
else result = 0 ;
}
}
return result;
}
if( savedRequest != null ) {
result = 2 ;
return result ;
}
String refererUrl = request.getHeader("REFERER");
if(useReferer && StringUtils.hasText(refererUrl)) result =3;
else result = 0 ;
return result;
}
/*
* 기본적으로 SpringSecurity는 로그인 실패하면 에러세션을 저장한다.
* 그러나, 예를들어 로그인을 5번시도하고 성공했다고해도 그 에러세션이 저장되어 있을것이다.
* 이 남아있는 에러세션을 주기위한 메서드이다.
*/
private void clearAuthenticationAttributes(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if ( session == null ) return;
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION); // SpringSecurity는 에러발생시 해당 key값을 사용한다.
}
private void useTargetUrl(HttpServletRequest request, HttpServletResponse response) throws IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
if(savedRequest != null ) {
requestCache.removeRequest(request, response);
}
String targetUrl = request.getParameter(targetUrlParameter);
redirectStrategy.sendRedirect(request, response, targetUrl);
}
private void useSessionUrl(HttpServletRequest request, HttpServletResponse response) throws IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
String targetUrl = savedRequest.getRedirectUrl();
redirectStrategy.sendRedirect(request, response, targetUrl);
}
private void useRefererUrl(HttpServletRequest request, HttpServletResponse response) throws IOException {
String targetUrl = request.getHeader("REFERER");
redirectStrategy.sendRedirect(request, response, targetUrl);
}
private void useDefaultUrl(HttpServletRequest request, HttpServletResponse response) throws IOException {
redirectStrategy.sendRedirect(request, response, defaultUrl);
}
}

View File

@@ -57,15 +57,15 @@
</div>
<button name="submit" type="submit" class="btn btn-block btn-primary text-light">로그인</button>
<c:if test="${not empty SPRING_SECURITY_LAST_EXCEPTION}">
<c:if test="${not empty securityExceptionMsg}">
<%--<c:if test="${not empty SPRING_SECURITY_LAST_EXCEPTION}"> --%>
<font color="red">
<p>
Your login attempt was not successful due to <br />
${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
</p>
<c:remove var="SPRING_SECURITY_LAST_EXCEPTION" scope="session" />
<p>Your login attempt was not successful, try again</p>
<p>${securityExceptionMsg}</p>
<%--<c:remove var="SPRING_SECURITY_LAST_EXCEPTION" scope="session" /> --%>
</font>
</c:if>
<input type="hidden" name="loginRedirect" value="${loginRedirect}" />
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> <br>
<sec:authorize access="isAuthenticated()">