[Eclipse]_Spring Security Handler 구현 3단계 (로그인 실패 시)

2022. 12. 20. 17:22[Spring]_/[Eclipse]

728x90
반응형

개요

Intellij를 사용하다가 다음 프로젝트가 Eclipse 환경으로 인해

Eclipse 에 spring 최신버전을 설치하였고

SpringSecurity  에서 제공하는

handler 들을 커스텀화 하여 적용하는 방법 서울

 

3단계에서는 로그인 실패시 핸들러처리를 구현합니다.

 

환경 

Eclipse 최신버전 2022-09 (4.25.0)

Tomcat - 10.0.23  v

maven Artifact ID : maven-archetype-webapp 1.4v

Spring 6.0.2 ver

Spring Security 6.0.0 ver

Jakarta 5.0.0 ver

 

특이사항

이전 포스트 에서 바뀐점이 있습니다.

Tomcat 10 버전 사용시 필요한 servlet 버전은 5 입니다.

이전까지는 Tomcat 에 내장된 jakarta 를 사용하였고

이번에 maven build 에 해당 servlet 이 없을 경우 오류가 발생합니다.

 

다음 포스트 참고

https://yn971106.tistory.com/167

 

determined from the Dynamic Web Module facet version (2.3), was not found on the Java Build Path

환경 Eclipse 최신버전 2022-09 (4.25.0) Tomcat - 10.0.23 v maven Artifact ID : maven-archetype-webapp 1.4v Spring 6.0.2 ver Spring Security 6.0.0 ver spring-jdbc : 6.0.2 ver mybatis : 3.5.11ver mybatis-spring : 3.0.1ver 문제상황 오류코드 deter

yn971106.tistory.com

 

또한 본문에 springSecurty 6.0.0 버전에 맞는 DTD도 기술하였습니다.

 

해당 포스트는 하위 포스트의 후속내용입니다.

https://yn971106.tistory.com/163

 

[eclipse]_spring_security 설치

개요 Intellij를 사용하다가 다음 프로젝트가 Eclipse 환경으로 인해 Eclipse 에 spring 최신버전을 설치하였고 SpringSecurity 를 설치 하는 과정 설명 환경 Eclipse 최신버전 2022-09 (4.25.0) Tomcat - 10.0.23 v maven Ar

yn971106.tistory.com


1. security.context 수정

 

기존의 DTD 의 경우 이전버전으로 6.0.0 에 맞는 DTD 작성

DTD 의 경우 다음 링크 참고

https://docs.spring.io/spring-security/reference/6.0.0-M3/servlet/appendix/namespace/http.html#nsa-http-attributes

 

Web Application Security :: Spring Security

Adds support for concurrent session control, allowing limits to be placed on the number of active sessions a user can have. A ConcurrentSessionFilter will be created, and a ConcurrentSessionControlAuthenticationStrategy will be used with the SessionManagem

docs.spring.io

코드 + 주석 설명

<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:b="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">


	

	<!-- http 요청시 처리 로직 -->
	<http request-matcher="ant" auto-config="true">
	
	<!-- url intercept 설정 -->
		<intercept-url pattern="/login/**"  access="permitAll" />
		<intercept-url pattern="/menu1" access="hasRole('ROLE_ADMIN')" />
		<intercept-url pattern="/**"  access="permitAll" />
	
	<!-- 로그인 페이지 설정 -->	
		<form-login login-page="/login/login"
			username-parameter="username" password-parameter="password"
			login-processing-url="/loginProcessing"
			authentication-success-handler-ref="loginSuccessHandler"
			authentication-failure-handler-ref="loginFailureHandler" />
		<!--  엑세스 거부시 설정 -->
		<access-denied-handler ref="accessDeniedHandler" />
		
		<!--  로그아웃 설정 페이지 아직 미구현 -->
		<logout logout-url="/logout" logout-success-url="/login/login"
			invalidate-session="true" />
		
	</http>

	<!-- 엑세스 거부 프로세스 객체 (빈) 등록 -->
	<!-- 구현체의 defaultDeniedUrl 에 value 의 값을 대입 -->
	<b:bean id="accessDeniedHandler"
		class="com.yoon.loginHelp.security.CustomAccessDeniedHandler">
		<b:property name="defaultDeniedUrl" value="/login/login"></b:property>
	</b:bean>
	
<!-- 로그인 성공시 작동할 객체(빈) 등록-->
	<b:bean id="loginSuccessHandler"
		class="com.yoon.loginHelp.security.LoginSuccessHandler">
	</b:bean>

<!-- 로그인 실패시 작동할 객체(빈) 등록-->
<!-- 해당 클래스의(구현체의) defaultFailureUrl 에 value 값 대입-->
	<b:bean id="loginFailureHandler"
		class="com.yoon.loginHelp.security.LoginFailureHandler">
		<b:property name="defaultFailureUrl" value="/login/error" />
	</b:bean>

<!-- 로그인 프로세스-->
<!-- 구현체에 기존 security 에서 작동하는 메소드 오버라이드 하여 커스텀화 가능-->
	<authentication-manager>
		<authentication-provider
			ref="customAuthenticationProvider" />  <!-- 로그인 관련 로직 구현 -->
	</authentication-manager>

<!-- 로그인 구현체에 등록한 객체의 빈 등록 -->
 <!-- 로그인 관련 로직 구현 -->
	<b:bean id="customAuthenticationProvider"
		class="com.yoon.loginHelp.security.CustomAuthenticationProvider" />

</b:beans>

 

 

 

등록해야 하는 빈 객체

  • loginSuccessHandler : 로그인 성공시 핸들러
  • loginFailureHandler : 로그인 실패시 핸들러 < NOW
  • accessDeniedHandler : 접근 거부 ( 예시 permitall 이 아닌 곳 직접 url 치고 들어올 경우)
  • CustomAuthenticationProvider : 로그인 하는 과정 직접 오버라이드 하여 구현

2.  loginFailureHandler 작성

 

<!-- 로그인 실패시 작동할 객체(빈) 등록-->
<!-- 해당 클래스의(구현체의) defaultFailureUrl 에 value 값 대입-->
	<b:bean id="loginFailureHandler"
		class="com.yoon.loginHelp.security.LoginFailureHandler">
		<b:property name="defaultFailureUrl" value="/login/error" />
	</b:bean>

security context 에 작성한 경로에 맞춰서 

loginFailureHandler.java 생성

해당 경로에 login 관련 빈들을 생성할 예정입니다.

loginFailureHandler.java

package com.yoon.loginHelp.security;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
	
	final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Override
	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException exception) throws IOException,
	ServletException {
		HttpSession session = request.getSession();
		
		String errorMsg = (String)exception.getMessage();
		
		session.setAttribute("errorMsg", errorMsg);
		session.setAttribute("userID", request.getParameter("username"));
		
		logger.debug("로그인에 실패하였습니다. 로그인 아이디 : " +request.getParameter("username") );
		logger.debug("원인: " + errorMsg );
		
		super.onAuthenticationFailure(request, response, exception);
		
	}

}

 

SimpleUrlAuthenticationFailureHandler 를 extends로 상속받습니다.

 

 

SimpleUrlAuthenticationFailureHandler 에서 사용한 메소드는 다음과 같습니다.

/**
	 * Performs the redirect or forward to the {@code defaultFailureUrl} if set, otherwise
	 * returns a 401 error code.
	 * <p>
	 * If redirecting or forwarding, {@code saveException} will be called to cache the
	 * exception for use in the target view.
	 */
	@Override
	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException exception) throws IOException, ServletException {
		if (this.defaultFailureUrl == null) {
			if (this.logger.isTraceEnabled()) {
				this.logger.trace("Sending 401 Unauthorized error since no failure URL is set");
			}
			else {
				this.logger.debug("Sending 401 Unauthorized error");
			}
			response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
			return;
		}
		saveException(request, exception);
		if (this.forwardToDestination) {
			this.logger.debug("Forwarding to " + this.defaultFailureUrl);
			request.getRequestDispatcher(this.defaultFailureUrl).forward(request, response);
		}
		else {
			this.redirectStrategy.sendRedirect(request, response, this.defaultFailureUrl);
		}
	}

기존의 onAuthenticationFailure 메소드에서는 인증 실패시 this.defaultFailureUrl 로 리다이렉트를 하는것을 알 수있습니다.

 

이를 이용해서 bean 등록시 

<b:property name="defaultFailureUrl" value="/login/error" />

를 사용하여 해당 값에 제가 error 페이지로 지정할 url 주소를 넣고 bean을 생성하면

defaultFailureUrl 이 원하는 url 로 변경된 상태로 bean 객체를 생성할 수 있습니다.

 

따라서 extends 로 onAuthenticationFailure 를 오버라이드 하여 원하는대로 구현합니다.

 

저의 경우에는 errorMsg 와 어떤 아이디와 비밀번호로 로그인했는지를 로그에 찍기 위하여 오버라이드 하여 사용하였고

이를 super. 으로 부모의 onAuthenticationFailure 에 3가지 파라미터를 전달하고 끝나게 됩니다.

 

3 . defaultFailureUrl  에 해당하는 controller 생성

4. jsp 생성

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
로그인에 실패하였습니다.
</body>
</html>

결과

728x90
반응형