티스토리 뷰

실전 프로젝트 구성

  1. 프로젝트 명
    • core-spring-security
  2. 프로젝트 기본 구성
    • 의존성 설정, 환경설정, UI 화면 구성, 기본 CRUD 기능
    • 스프링 시큐리티 보안 기능을 점진적으로 구현 및 완성
  3. Springboot, Spring MVC, Spring Data JPA
    • 기초 지식 학습 권장
    • 스프링 시큐리티 기술에 집중
  4. 프로그램 설치
    • DB-Postgresql Server

 

PostgreSQL 설치

PostgreSQL은 오픈 소스 객체-관계형 데이터베이스 시스템(ORDBMS)으로, Enterprise급 DBMS의 기능과 차세대 DBMS에서나 볼 수 있을 법한 기능들을 제공한다.약 20여년의 오랜 역사를 갖는 PostgreSQL은 다른 관계형 데이터베이스 시스템과 달리 연산자, 복합 자료형, 집계 함수, 자료형 변환자, 확장 기능 등 다양한 데이터베이스 객체를 사용자가 임의로 만들 수 있는 기능을 제공함으로써 마치 새로운 하나의 프로그래밍 언어처럼 무한한 기능을 손쉽게 구현할 수 있다.

 

https://www.enterprisedb.com/download-postgresql-binaries

 

Community DL Page

Download PostgreSQL Binaries The links below allow you to download archives of the files installed by our PostgreSQL installers. These archives are provided as a convenience for expert users; unless you specifically need these files, you should download an

www.enterprisedb.com

최신버전 window용 binary zip 파일을 다운 받고

 

https://www.zinnunkebi.com/windows-postgresql14-zip-install/

 

[Windows Server 2012] PostgreSQL 14 Windows x64 버전 설치하기(zip archive 사용)

PostgreSQL Server 설치 프로그램에는 인스톨러 버전과 zip archive 버전이 있습니다. 이글에서는 zip archive 버전을 다운로드 받아 압축을 해제하여 데이터베이스 클러스터 작성 , 서비스

www.zinnunkebi.com

설치에 참고했다.

 

https://mangkyu.tistory.com/71

 

[PostgreSQL] PostgreSQL이란?

이번에는 오픈소스 객체-관계형 데이터베이스 시스템인 PostgreSQL에 대해서 알아보도록 하겠습니다. 1. PostgreSQL이란? [ PostgreSQL이란? ] PostgreSQL은 오픈 소스 객체-관계형 데이터베이스 시스템(ORDBMS)

mangkyu.tistory.com

https://velog.io/@gwak2837/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%8B%A4%EC%8A%B52

 

PostgreSQL 사용법

PostgreSQL엔 여러 데이터베이스가 존재하고, 한 데이터베이스엔 여러 테이블이 존재하고, 한 테이블엔 여러 레코드가 존재하고, 한 레코드엔 여러 필드가 존재한다. 필드는 테이블의 특정 행과 열

velog.io

 

postgreSQL DB 생성

CREATE DATABASE [데이터베이스 이름];

CREATE DATABASE springboot;

 

postgresSQL 유저 생성 (기본 유저 postgres)

CREATE USER [사용자이름]  WITH PASSOWRD '[비밀번호]';

CREATE USER myuser WITH PASSOWRD 'qwer1234';

 

postgresSQL 유저 권한 부여

GRANT ALL PRIVILEGES ON DATABASE [데이터베이스 이름] TO [사용자 이름];

GRANT ALL PRIVILEGES ON DATABASE springboot TO myuser;

 

 

springboot 3버전, 자바17, Gradle, spring security 6 버전으로 프로젝트를 셋팅했다.

 

 

 

 

시큐리티 기본 설정

어떤 경로로 접근하든 인증이 필요하고 formLogin 인증방식 설정

 

임시로 InMemory 방식의 유저생성과 권한 생성.

비밀번호는 BCryptPasswordEncoder 객체 사용했다.

 

강의에서는 위와 같은 메서드를 사용하는데

 

역시 BCryptPasswordEncoder 를 사용하지만 왜인지 @SuppressWarnings("deprecation")으로 사용하지 말아야할 메서드의 경고를 막고있다.

내부적으로 스프링 시큐리티가 사용하는 메서드이고 개발자가 사용할 땐 이 방법 말고 직접 암호화 객체를 넘겨주면 될 것 같다.

 

페이지 접근 권한설정

  • "/" 경로 모든 사용자 인증상관 없이 접근 가능
  • "/mypage"경로 USER 권한 사용자만 접근 가능
  • "/message"경로 MANAGER 권한 사용자만 접근 가능
  • "/config"경로 ADMIN 권한 사용자만 접근 가능

 

 

WebIgnore 설정

  • js/css/image 파일 등 보안 필터를 적용할 필요가 없는 리소스 설정
//spring security5 버전

@Override
public void configure(WebSecurity web) throws Exception {
	web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}

spring security6 에서는 WebSecurityCustomizer 객체를 Bean으로 만들어서 StaticResourceLocation enum에 정의된 기본값을 Ignore 시킬 수 있고 추가적으로 더 패키지 권한 제외를 하려면 String 배열로 선언할 수 있다.

 

securityFilterChain 메서드 내부에서 PermitAll() 설정에서도 인증체크 제외를 설정할 수 있는데

WebSecurityCustomizer Bean생성과 다른점은 PermitAll() 설정은 보안 필터 심사를 거쳐 설정한 경로가 페이지 요청에 포함되어 있다면 무시되도록 처리 되는데 WebSecurityCustomizer Bean 생성 설정은 아얘 보안필터를 거치지 않아 비용적인 측면에서  더 나은 방법이다.

 

WebSecurityCustomizer 설정을 주석처리하고 확인해보면 FilterSecurityInterceptor에서 정적파일들을 체크하는 것을 확인할 수 있다.

 

spring 공식 사이트에서는 WebSecurity를 구성하는 것 보다 permitAll 설정을 사용하는 것이 더 좋다고 나와있는데 왜그런진 자세히 모르겠다.

부트 로딩시 WARN 으로 permitAll 설정 사용하라고 뜬다.

*security 보안 필터를 전혀 타지않으니 보안상 위험으로 permitAll 설정을 더 추천한다.

 

 

 

Form 인증 - PasswordEncoder

  • 비밀번호를 안전하게 암호화 하도록 제공
  • Spring Security 5.0 이전에는 기본 PasswordEncoder가 평문을 지원하는 NoOpPasswordEncoder(현재는 Deprecated)

 

  • 생성
    • PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()
    • 여러개의 PasswordEncoder 유형을 선언한 뒤, 상황에 맞게 선택해서 사용할 수 있도록 지원하는 Encoder이다.
  • 암호화 포멧 : {id}encodedPassword
    • 기본 포멧은 Bcrypt: {bcrypy}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
    • 알고리즘 종류: bcrypt, noop, pbkdf2, scrypt, sha256...
  • 인터페이스
    • encode(password)
      • 패스워드 암호화
    • matches(rawPassword, encodedPassword)
      • 패스워드 비교

 

@Entity는 JPA의 기술로 Account 객체가 JPA가 관여하는 클래스가 되고 실제 DB 테이블과 맵핑이 된다.

 

MVC 레이어를 옮겨다닐 Object Scope 역할의 Dto 객체 생성

 

UserRepository 인터페이스 생성하며 JpaRepository 확장하여 DB에 값을 넘길 Entity 객체와 Id의 타입을 제네릭으로 넣어준다.

 

UserService 인터페이스 생성하여 유저를 생성할 추상 메서드 선언

 

UserServiceImpl 객체 생성하여 UserRepository 의존성 주입 후 유저 생성 메서드 로직을 구현한다.

 

도메인 객체에 담긴 값을 다른 도메인에 Mapping 시켜주는 의존성 ModelMapper 추가 

 

컨트롤러에 회원가입 로직 메서드 작성

 

회원가입 경로 권한 인증 해제 설정

 

부트 실행시 spring.jpa.hibernate.ddl-auto=create 설정으로 Account Entity 테이블 자동 생성

 

 

회원가입 진행하면

Dto 값이 Entity로 잘 맵핑되고 암호화 처리도 잘 되었다.

 

account 테이블에 유저 등록 완료

 

 

 

 

Form 인증 - CustomUserDetailsService

인메모리 방식이 아닌 실제 DB 데이터에 있는 유저로 로그인하기 위해 UserDetailsService 인터페이스를 구현한 CustomUserDetailsService 객체를 만든다.

UserDetails객체를 반환하는 User 를 확장하여 우리가 만든 Account 객체로 넘겨 받아 처리할 수 있게 AccountContext(UserDetails) 객체 생성

 

SpringSecurityConfig 클래스에서 기존 인메모리 방식의 유저를 주석 처리하고 우리가 만든 CustomUserDetailsService가 작동되도록 Bean으로 만들어준다.

 

 

 

Form 인증 - CustomAuthenticationProvider

CustomUserDetailsService가 최종 반환하는 AccountContext 객체를 받아서 추가적인 검증을 진행하는 AuthenticationProvider를 구현한 CustomAuthenticationProvider 객체를 만들어 인증처리를 진행한다.

 

AuthenticationProvider를 구현한 CustomAuthenticationProvider 클래스.

supports 메서드에는 파라미터로 받는 authentication 클래스 타입과 CustomAuthenticationProvider 클래스가 사용하는 토큰의 타입과 일치할 때 인증처리를 할 수 있도록 조건받는 로직을 만든다.

 

authenticate 메서드에는 검증을 진행하는 로직이 들어간다.

파라미터로 받는 Authentication객체는 AuthenticationManager로부터 받아오는 사용자가 로그인시 입력한 아이디, 패스워드 정보가 들어있는 인증 객체이다.

UserDetailsService로 아이디 값을 넘겨 존재하는 사용자인지 인증처리를 진행한 후 받아오는 AccountContext(UserDetails) 객체에 담긴 암호화된 비밀번호와 사용자가 입력한 비밀번호를 매칭해 일치하지 않으면 BadCredentialException을 발생시키고 일치한다면 인증에 성공한 정보를 담은 인증객체 토큰을 만들어 AuthenticationManager에게 반환한다.

 

SecurityConfig에 CustomAuthenticationProvider가 적용되게 Bean으로 만들어준다.

 

 

authentication 객체와 accountContext 객체를 가져와 최종 검증 후 인증토큰 반환

 

 

커스텀 로그인 페이지 생성하기

스프링 시큐리티에서 제공한 로그인 페이지가 아닌 커스텀 로그인 페이지를 만들고 formLogin API 설정을 한다.

login.html 추가

 

formLogin API 설정

  • loginPage: 시큐리티에서 제공하는 로그인 페이지가 아닌 커스텀 로그인 페이지 사용
  • loginProcessingUrl: form 요소 action url
  • defaultSuccessUrl: 인증 성공시 루트 페이지로 이동
  • permitAll: 커스텀 로그인 페이지는 인증없이 접근 가능하게 설정

 

커스텀 로그인 페이지
메인 페이지 이동

 

근데 defaultSuccessUrl 설정을 경로만 주면 

 

localhost:8080/error?continue 로그인 후에 이 페이지로 가는데 왜일까.. 물어봐야겠다.

 

해결 방법은 찾았다.

/error 로 들어오는 경로를 일단 pertmitAll 처리하면 대시보드로 넘어가는데 로그인 후에 에러페이지를 거쳐서 대시보드로 가는데 인증 받기 전 과정이라 접근못해서 그러는 건지.. 

 

계속 보니 화면을 불러올 때 불러오는 리소스를 불러와서 개발자 도구에서 에러처리가 1개라도 있으면 일단 백단에 넘어올 때 /error경로를 할당하여 에러 페이지로 넘어간다.

뷰단의 에러를 다 처리하니 /error 경로 permitAll() 설정 없이도 잘 넘어갔다.

 

로그아웃 및 인증에 따른 화면 보안 처리

  • 로그아웃 방법
    • <form> 태그를 사용해서 POST로 요청
    • <a> 태그를 사용해서 GET 으로 요청 - SecurityContextLogoutHandler 활용
  • 인증 여부에 따라 로그인 / 로그아웃 표현
    • <li sec:authorize="isAnonymous()"><a ht:href="@{/login}">로그인</a></li>
    • <li sec:authorize="isAuthenticated()"><a ht:href="@{/logout}">로그아웃</a></li>

인증 여부에 따른 view 페이지 표현 설정

sec:authorize 표현식은 시큐리티가 타임리프에 제공하는 보안관련 표현식을 지원하는 문법이다.

익명사용자일 경우 로그인, 회원가입이 보이고 로그인 사용자라면 로그아웃이 보인다.

로그인 사용자중 'ROLE_ADMIN' 권한은 관리자 네비가 보인다.

sec 문법을 위해 build.gradle 에서 implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' 의존성 추가와 

타임리프 페이지 내 xmlns:sec="http://www.thymeleaf.org/extras/spring-security" 네임 스페이스 추가

 

LoginController에서 Get 방식 로그아웃 처리 메서드를 만든다.

인증 객체가 존재하면 로그아웃 처리하는 핸들러인 SecurityContextLogoutHandler에게 request, response, authentication 객체를 넘겨주고 로그아웃 처리 후 로그인 페이지로 리다이렉트 시킨다.

 

로그아웃 처리 로직을 보면 넘겨받은 request 객체에서 세션이 존재하면 세션을 날리고 SecurityContext를 날리고 Authentication 객체도 null로 셋팅한다.

 

 

 

인증 부가 기능 - WebAuthenticationDetails, AuthenticationDetailsSource

사용자가 인증요청시 username과 password 정보 외에 추가로 값을 보내면 WebAuthenticationDetails에 저장하여 인증과정 중이나 후에 값을 사용할 수 있다.

WebAuthenticationDetails에 request 객체가 전달되면 파라미터에 담긴 정보들과 remoteAddress, SessionId와 함께 저장되고 WebAuthenticationDetails 객체는 Authentication 객체의 details 속성에 담긴다.

WebAuthenticationDetails를 생성하는 객체가 AuthenticationDetailsSource 이다.

두 개의 객체를 상속 받고 구현하여 추가적인 파라미터 정보를 받아 활용할 수 있다.

  •  WebAuthenticationDetails
    • 인증 과정 중 전달된 데이터를 저장
    • Authentication의 details 속성에 저장
  • AuthenticationDetailsSource
    • WebAuthenticationDetails 객체를 생성

 

UsernamePasswordAuthenticationFilter의 인증 과정 중 Authentication 객체에 details 속성에 request 정보와 UsernamePasswordAuthenticationToken 객체를 넘겨준다.

 

setDetails 메서드에 setDetails에 저장되는 타입은 Object 타입이고 WebAuthenticationDetails 객체를 넘겨주기 위해 AuthenticationDetailsSource를 만들며 request 객체를 넘겨주고 있다.

 

UsernamePasswordAuthenticationFilter는 AbstractAuthenticationProcessingFilter를 확장하고 있는데 여기서 AuthenticationDetailsSource를 구현한 WebAuthenticationDetailsSource를 생성하고 있다.

 

WebAuthenticationDetailsSource에 buildDetails에서 WebAuthenticationDetails를 생성하며 request 정보를 넘겨주고 있다.

 

WebAuthenticationDetails에서는 request 정보로 remoteAddr, sessionId 정보를 갖게 된다.

 

 

코드 작성

security 패키지에 common 패키지 생성 후 WebAuthenticationDetails를 확장한 클래스인 FormWebAuthenticationDetails 클래스를 만들고 추가로 받아볼 파라미터를 필드값으로 만든다.

super()메서드를 통해 FormWebAuthenticationDetails 객체가생성될 때 remoteAddr과 sessionId 정보를 담고 request.getParameter로 추가로 받을 정보도 객체에 담는다. 

 

만든 common 패키지에 AuthenticationDetailsSource를 구현한 FormAuthenticationDetailsSource 클래스를 만들고 여기에 우리가 만든 FormAuthenticationDetails 객체가 생성되게 만든다.

 

SpringSecurity 설정에 AuthenticationDetailsSource 구현 객체로 FormAuthenticationDetailsSource가 적용되게 설정한다.

 

로그인시 hidden 값으로 secretKey 파라미터를 작성하여 값이 잘 넘어오는지 확인해 본다.

 

CustomAuthenticationProvider 인증 로직 처리에서 secretKey 파라미터를 가져와 실제 파라미터 값을 가져오지 못하면 인증처리 되지 않게 예외를 던져본다.

 

 

추가 정보값을 잘 받아오는 것을 확인할 수 있다.

 

 

 

Form 인증 - CustomAuthenticationSuccessHandler

formLogin 인증 필터가 인증처리 성공 후 AuthenticationSuccessHandler를 호출하여 후속 작업들을 처리할 수 있다.

AuthenticationSuccessHandler 구현하여 커스텀하게 후속처리할 객체를 만든다.

 

실제 사용하는 핸들러인 SimpleUrlAuthenticationSuccessHandler를 상속받는 CustomAuthenticationSuceesHandler를 handler 패키지에 만들고 onAuthenticationSuccess 메서드를 오버라이딩 한다.

  • RequestCache는 현재 클라이언트 요청정보 쿠키, 헤더, 파라미터 값들을 추출하여 보관하는 SavedRequest 객체를 세션에 저장하는 역할로 request와 response 정보로 SavedRequest 객체를 꺼내와 속에 있는 정보를 사용한다.
    RequestCache의 기본 구현체는 HttpSessionRequestCache이고 SavedRequest 기본 구현체는 DefaultSavedRequest다.
  • RedirectStrategy 객체는 화면 이동에 대한 규칙을 정의하는 부분을 만든인터페이스로 최종 화면 이동을 위해 사용한다. (response 로도 리다이렉트할 수 있다.)

로직은 savedRequest 객체에 요청정보가 담겨있다면 사용자가 원래 가고자 했던 페이지 정보를 가져와서 해당 페이지로 이동시키고 savedRequest 객체에 요청정보가 없어 null 이라면 default 경로로 이동시킨다.

SecurityConfig 에서 설정한 디폴트 정보는 핸들러 구현으로 인해 무시되고 핸들러에 설정된 경로로 가게 된다.

setDefaultTargetUrl()로 설정하면 되는데 

default 경로가 "/" 라면 이미 기본정보로 셋팅되어 있어서 주석으로 가려놨다.

 

SecurityConfig에서 CustomAuthenticationSuccessHandler()를 Bean 으로 생성하고 formLogin API 설정에 successHandler를 지정했다. 기존 defaultSuccessUrl은 주석처리 하였다.

 

그냥 로그인 페이지 접근하면

savedRequest 객체는 null 로

defaultTargetUrl 인 루트경로로 이동한다.

 

 

로그아웃 후 사용자 페이지에 접근하면 

SavedRequest 구현체인 DefaultSavedRequest에 RequestURI 정보가 "/mypage"로 들어와서 session에 저장하고 권한이 "ROLE_USER"인 사용자만 접근 가능하니 로그인 페이지로 넘어간다.

 

로그인 하면

CustomAuthenticationSuccessHandler에서 savedRequest에 저장된 요청 경로를 가져와서 마이페이지로 이동시킨다.

 

 

 

 

Form 인증 - CustomAuthenticationFailureHandler

formLogin 인증 필터가 인증처리 실패 후 AuthenticationFailureHandler를 호출하여 후속 작업들을 처리할 수 있다.

AuthenticationFailureHandler 구현하여 커스텀하게 후속처리할 객체를 만든다.

 

handler 패키지에 실제 사용하는 핸들러인 SimpleUrlAuthenticationFailureHandler를 상속받는 CustomAuthenticationFailureHandler를 만들고 onAuthenticationFailure메서드를 오버라이딩 한다.

보통 인증처리시 username을 찾을 수 없거나 비밀번호가 틀렸을때, 추가적으로 검증하는 작업이 실패할 경우 예외가 발생하며 인증 필터가 받아서 AuthenticationFailureHandler 객체에서 실패 처리 로직이 실행된다.

 

인증 예외가 발생하면 예외에 따라 예외 메세지를 지정해주고 화면에 출력하기 위해 setDefaultFailureUrl에 쿼리스트링을 지정한다. 화면에서는 error 파라미터가 true 라면 지정된 errorMessage를 출력한다.

최종적으로 부모클래스에 onAuthenticationFailure 메서드에 request, response, exception을 넘겨줘 처리를 위임한다.

 

 

LoginController에서 "/login" 경로로 접근시 error, exception 파라미터 값이 있으면 Model 객에 담아서 값을 넘긴다.

 

login.html 뷰 페이지에서 error 파라미터 값이 존재하면 에러 메세지를 출력하는 구문을 추가한다.

 

SecurityConfig에서 CustomAuthenticationFailureHandler()를 Bean 으로 생성하고 formLogin API 설정에 failureHandler를 지정한다.

formLogin API 설정에서 loginPage("/login") permitAll() 설정을 주었지만 스프링 시큐리티에서는 파라미터 값이 붙은 경로를 통으로 인식하기 때문에 requestMatchers 설정에 "/login*" permitAll() 설정을 추가한다.

 

 

로그인시 비밀번호를 다르게 입력하면

 

CustomAuthenticationProvider 에서 BadCredentialsException이 발생하고

 

CustomAuthenticationFailureHandler 에서 예외 메세지가 설정된다.

 

화면으로 출력된다.

 

 

Form 인증 - Access Denied

인증 시도중 발생한 예외는 인증 필터가 받는다. 인증 성공 후 권한 없는 리소스에 접근하게 될 경우 인가 예외가 발생한다.

인가 예외는 AccessDeciedException 이 발생하여 ExceptionTranslationFilter가 예외를 받아 로그인 페이지로 보낸다.

ExceptionTranslationFilter에서 AccessDeciedException 발생하여 후속 작업을 처리하는 핸들러는 AccessDeniedHandler로 이 객체를 구현하여 인가 예외시 커스텀하게 후속 처리할 객체를 만든다.

 

handle 패키지에 AccessDenineHandler 객체를 구현한 CustomAccessDeniedHandler를 만들고 생성시 지정된 errorPage 경로로 예외메세지 파라미터를 가진 Url을 만들어 해당 경로로 페이지 이동되고 뷰단에서 예외 메세지를 출력한다.

 

LoginController에서 "/denied" 경로로 접근시  exception 파라미터 값이 있으면 Model 객체에 담아서 값을 넘긴다.

요청자의 이름을 표시하기 위해 SecurityContext에서 username을 꺼내와서 Model에 담는다.

 

SecurityConfig에서 CustomAccessDeniedHandler()를 Bean 으로 생성하고 errorPage 경로를 "/denied"로 설정한다. exceptionHandling API 설정은 인가예외가 발생했을 때 핸들러를 호출해주는 API 설정을 제공해준다. accessDeniedHandler를 지정한다.

 

인가예외 페이지인 denied.html을 만든다.

username을 출력하면서 예외 메세지를 출력한다.

 

user로 로그인하고 설정페이지에 접근시 권한 없는 페이지로 인가 예외가 발생하며 CustomAccessDeniedHandler가 처리하게 되며 denied 페이지로 exception 에러메세지와 함께 넘어간다.

 

처리 화면

 

 

 

 

 

https://www.inflearn.com/course/%EC%BD%94%EC%96%B4-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0/dashboard

 

스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security - 인프런 | 강의

초급에서 중.고급에 이르기까지 스프링 시큐리티의 기본 개념부터 API 사용법과 내부 아키텍처를 학습하게 되고 이를 바탕으로 실전 프로젝트를 완성해 나감으로써 스프링 시큐리티의 인증과

www.inflearn.com

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함