본문 바로가기

개발(합니다)/Java&Spring

Spring Security 정리 (4) : 로그인 실패 후 처리

반응형

로그인 실패 시 후처리에 대한 포스팅 정리입니다.



로그인 실패시

1. 로그인 정보 담기
2. 세션 없이 에러 전달하기
3. 3회 이상 틀리면 계정 잠그기


폴더 구성




security-context.xml

    <beans:bean id="customizeAuthenticationFailureHandler"
        class="com.otrodevym.mfaw.common.security.custom.CustomizeAuthenticationFailureHandler">
        <beans:property name="loginIdName" value="user_id"></beans:property>
        <beans:property name="loginPwdName" value="password"></beans:property>
        <beans:property name="errorMsgName" value="ERRORMSG"></beans:property>
        <beans:property name="defaultFailureUrl"
            value="/login?error=true"></beans:property>
    </beans:bean>



CustomizeAuthenticationFailureHandler.java

public class CustomizeAuthenticationFailureHandler implements AuthenticationFailureHandler {

    private String loginIdName;
    private String loginPwdName;
    private String errorMsgName;
    private String defaultFailureUrl;


    
    @Inject
    private LoginService loginService;

    @Override
    public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse res,
            org.springframework.security.core.AuthenticationException exception)
throws IOException, ServletException {

//       로그인 정보 저장
        String userName = req.getParameter(loginIdName);
        String password = req.getParameter(loginPwdName);

        req.setAttribute(loginIdName, userName);
        req.setAttribute(loginPwdName, password);

//      에러 메세지 저장
//      String errorMsg = exception.getMessage();
//      req.setAttribute(errormsgname, errorMsg);

        String errorMsg = null;

        if (exception instanceof BadCredentialsException) {
            loginFailure(userName);
            errorMsg = MessageUtils.getMessage("error.BadCredentials");
        } else if (exception instanceof InternalAuthenticationServiceException) {
            errorMsg = MessageUtils.getMessage("error.BadCredentials");
        } else if (exception instanceof DisabledException) {
            errorMsg = MessageUtils.getMessage("error.Disaled");
        } else if (exception instanceof CredentialsExpiredException) {
            errorMsg = MessageUtils.getMessage("error.CredentialsExpired");
        }
        req.setAttribute(errorMsgName, errorMsg);
        
        
        req.getRequestDispatcher(this.defaultFailureUrl).forward(req, res);
    }
    protected void loginFailure(String username) {
        loginService.loginFailCount(username);
        int count = loginService.checkFailCount(username);
        if(count == 3) {
            loginService.disabledUser(username);
        }
    }
    public String getLoginIdName() {
        return loginIdName;
    }
    public void setLoginIdName(String loginIdName) {
        this.loginIdName = loginIdName;
    }
    public String getLoginPwdName() {
        return loginPwdName;
    }
    public void setLoginPwdName(String loginPwdName) {
        this.loginPwdName = loginPwdName;
    }
    public String getErrorMsgName() {
        return errorMsgName;
    }
    public void setErrorMsgName(String errorMsgName) {
        this.errorMsgName = errorMsgName;
    }
    public String getDefaultFailureUrl() {
        return defaultFailureUrl;
    }
    public void setDefaultFailureUrl(String defaultFailureUrl) {
        this.defaultFailureUrl = defaultFailureUrl;
    }
    public LoginService getLoginService() {
        return loginService;
    }
    public void setLoginService(LoginService loginService) {
        this.loginService = loginService;
    }
    
}




에러에 따라서 에러에 맞는 예외 메세지를 전송해야 합니다.

스프링에서 제공하는 예외 명칭

 명칭 

 설명 

 BadCredentialException 

 비밀번호가 일치하지 않은 경우 

 InternalAuthenticationServiceException

 시스템 문제로 내부 인증 관련 처리 요청을 할 수 없는 경우

 AthenticationCredentialNotFoundException 

 인증 요구가 거부 됐을 경우 

 LockedException 

 인증 거부 : 잠긴 계정일 경우

 DisabledException 

 인증 거부 : 계정 비활성화

 AccountExpiredException 

 인증 거부 : 계정 유효 기간 만료 

 CredentialExpiredException 

 인증 거부 : 비밀번호 유효 기간 만료 



login.xml

    <update id="disabledUser" parameterType="java.lang.String">
        update login_info
        set
        enabled = '0'
        where user_id = #{username}
    </update>


    <update id="loginFailCount" parameterType="java.lang.String">
        update login_info
        set
        fail_count = fail_count +1
        where user_id = #{username}
    </update>

    <select id="checkFailCount" parameterType="java.lang.String"
        resultType="java.lang.Integer">
        select
        fail_count
        from login_info
        where user_id = #{username}
    </select>



LoginTest.java

//@RunWith(JUnitParamsRunner.class)
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations="file:src/main/webapp/WEB-INF/spring/**/**.xml")
public class LoginTest {
    private final Logger logger = LoggerFactory.getLogger(LoginTest.class);
    
    @Inject
    private LoginService loginService;
    
//  private WebApplicationContext wac;
//  private MockMvc mockMvc;
    
    
    @Before
    public void setUp() {
//      this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }
    

    
    /*@Test
    @Parameters({
        "test, $2a$10$U23dLUDB9ABFXCfK0PO6kukpXIG.gwHHguAmvUKLYEjYgvGvsTgRu"
    })
    public void loginTest_로그인_실패시_카운팅이_증가하는지_확인(String user_id, String password)
throws Exception{
        logger.error(user_id);
        logger.error(password);
//      mockMvc.perform(post("/login").params(params))
        loginService.loginFailCount(user_id);
    }*/
    
    @Test
    public void loginTest_로그인_실패시_카운팅이_증가하는지_확인() throws Exception{
        String user_id = "test";
        String password = "$2a$10$U23dLUDB9ABFXCfK0PO6kukpXIG.gwHHguAmvUKLYEjYgvGvsTgRu";
        loginService.loginFailCount(user_id);
        int count = loginService.checkFailCount(user_id);
        loginService.disabledUser(user_id);
        logger.info(String.valueOf(count));
//      mockMvc.perform(post("/login").params(params))
    }
}

JUnitParamsRunner.class를 사용해보려고 했는데 LoginService를 찾지 못하여 실패했습니다.

그래서 일반적인 단위 테스트를 했습니다.







반응형