2022. 12. 20. 17:22ㆍ[Spring]_/[Eclipse]
개요
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
또한 본문에 springSecurty 6.0.0 버전에 맞는 DTD도 기술하였습니다.
해당 포스트는 하위 포스트의 후속내용입니다.
https://yn971106.tistory.com/163
1. security.context 수정
기존의 DTD 의 경우 이전버전으로 6.0.0 에 맞는 DTD 작성
DTD 의 경우 다음 링크 참고
코드 + 주석 설명
<?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 생성
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>
결과
'[Spring]_ > [Eclipse]' 카테고리의 다른 글
[Eclipse]_Spring Security Handler 구현 1단계 (로그인 과정 구현) (0) | 2022.12.22 |
---|---|
[Spring]_Oracle 연결 & Mapper 연동 ( Error 정리) (0) | 2022.12.15 |
[eclipse]_spring_security 설치 (0) | 2022.12.12 |
[eclipse]_Spring 설치 및 구현 (0) | 2022.12.09 |
[Eclipse]_Spring 최신 버전 servlet 오류(feat.jakarta error) (0) | 2022.12.09 |