| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- AJAX
- security
- 보안
- js
- Security 설정
- 카카오
- login
- @AuthenticationPrincipal
- 공부
- HttpOnly
- 취미
- Method 방식
- VS Code
- 웹개발
- 백틱
- 유효성 검사
- 자바 스크립트
- SpringBoot
- 시
- findBy
- IntelliJ
- 웹
- ES6+
- 로그인
- JavqScript
- 그림
- 분리 개발
- MockAPI
- 낙서
- API
- Today
- Total
개발가능구역
[ IntelliJ ] Spring Security 로그인 02 본문
앞 전 유효성 검사 글에서는 회원가입을 함께 다뤘고,
이전 글에서는 초기 Security 로그인 창에서 원래 작업하던 웹 페이지로 이동하는 작업을 했다
[ IntelliJ ] Security 로그인 01
로그인은 개인의 계정을 입력하여 권한을 얻는 행위이다앞전 유효성 검사는 그 계정을 만들기 위한 페이지를 구현하기 위함이었고, 이제부터는 로그인을 공부해보려 한다 아직 초심자라 모르
oosomall.tistory.com

이번 글에서는 회원가입한 계정으로 로그인을 실제로 하는 코드를 다룰 예정이다
🔴 목차
1. 작업 툴
2. 전체 흐름
3. 네비게이션 바 Thymeleaf Security 추가
4. 의존성 추가
5. SecurityConfig 클래스 업데이트
- 이전 코드 / 현재 코드
- 로그인 관련 설정
- 로그아웃 관련 설정
- Builder
6. CustomUserDetailsService 클래스 생성
🔴 작업 툴
IDE : IntelliJ
웹 서버 : Apache
UI : HTML5, Thymeleaf
사용 클래스 : Config, Service
사용 DB : Oracle
🔴 전체 흐름
1. 로그인 폼에서 ID, PW 입력 후 로그인 버튼 클릭 → 폼 전송
2. SecurityConfig - formLogin() 폼 전송으로 받은 ID, PW 매핑
3. CustomUserDetailsService 에서 DB 조회
4. User.builder 반환 → PasswordEncoder로 검증 → 성공 시 로그인 완료
🔴 Nav 바( top.html ) Thymeleaf Security 추가
html xmlns:th="http://www.thymeleaf.org" th:fragment="menu"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
기존에는 윗줄 th:fragment = "menu" 까지 있었는데 밑에 한 줄 추가해준다
- xmlns:th ( Thymeleaf 선언 ) : 해당 문서가 Thymeleaf 템플릿 엔진을 사용함을 선언한다
- th:접두사( th:text / th:each / th:action / th:fragment )
- th:fragment=" menu " : menu 라는 이름을 다른 파일에서 페이지의 일부분으로 사용할 수 있다
- xmlns:sec (Spring Security 선언 ) : Thymeleaf의 Spring Security 확장 기능을 사용함을 선언한다
- sec:접두사 ( authorize / authentication ) : 사용자의 현재 로그인 및 권한 상태
- 예 / sec:authorize = " isAuthenticated() " : 로그인 상태일 때만 메뉴를 보여주는 역할
<ul class="nav navbar-nav navbar-right">
<li sec:authorize="!isAuthenticated()">
<a href="sign_up"><span class="glyphicon glyphicon-user"></span> Sign Up</a>
</li>
<li sec:authorize="!isAuthenticated()">
<a href="login"><span class="glyphicon glyphicon-log-in"></span> Login</a>
</li>
<li sec:authorize="isAuthenticated()">
<a>
<span class="glyphicon glyphicon-ok"></span>
<span sec:authentication="name"></span> 님, 반갑습니다.
</a>
</li>
<li sec:authorize="isAuthenticated()">
<a href="/logout"><span class="glyphicon glyphicon-log-out"></span> Logout</a>
</li>
</ul>
로그인 상태 [ "사용자 ID님, 반갑습니다" / 로그아웃 ]
로그인하지 않은 상태 [ 회원가입 / 로그인 ]
- sec:authorize 속성
- sec:authorize는 해당 태그가 렌더링될지 여부를 조건 제어한다
- isAuthenticated() : 사용자 로그인 상태라면 true
- !isAuthenticated() : 사용자 로그인하지 않은 상태라면 true
- sec:authentication = " name "
- 현재 로그인한 사용자의 username을 가져온다
- User.builder().username(member.getId()) 값이 가져와진다
🔴 의존성 추가
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
‼️문제 발생‼️
계속 네비게이션 바에서 [ 회원가입 / 로그인 / "id님, 반갑습니다" / 로그아웃 ] 이
모두 한꺼번에 출력되었어서 한참 헤맸다
해당 의존성을 추가 안 해서 Thymeleaf가 제대로 작동하지 않았던 것이다
build.gradle 에 해당 코드를 넣어준 뒤 오른쪽에 코끼리 클릭, 실행을 한다
🔴 SecurityConfig
❎ 이전 코드
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/sign_up", "/css/**", "/js/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/")
.permitAll()
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
✅ 바뀐 코드
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/sign_up", "/signup_save",
"/check/**", "/image/**", "/css/**", "/js/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.loginProcessingUrl("/go_login")
.usernameParameter("id")
.passwordParameter("pw")
.defaultSuccessUrl("/")
.permitAll()
)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll()
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
이전 글에서 다뤘다 지금은 현재 바뀐 내용 위주로 글을 써보려 한다
csrf().disable() → csrf( AbstracHttpConfigurer :: disable ) : 버전 문제가 생겨 이렇게 바꿨다
1️⃣ 로그인 관련 설정
.formLogin(form -> form
.loginPage("/login")
.loginProcessingUrl("/go_login")
.usernameParameter("id")
.passwordParameter("pw")
.defaultSuccessUrl("/")
.permitAll()
)
- loginProcessingUrl( " 주소 " ) : 로그인 폼에서 ID와 PW 값을 제출할 URL 주소
- usernameParameter( " ID " ) : 로그인 폼에서 name="ID" 와 연결되어 값을 가져온다
- passwordParameter( " PW " ) : 로그인 폼에서 name="PW" 와 연결되어 값을 가져온다
2️⃣ 로그아웃 관련 설정
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll()
);
- logoutUrl( " URL " ) : 로그아웃 요청할 때 사용하는 URL 지정
- logoutSuccessUrl( " URL " ) : 로그아웃 성공 시 이동할 URL
3️⃣ Builder
return http.build();
http.build()를 반환하며 사용자 인증을 하는 것이다
여기서 build는 무엇인가 하면 빌더 패턴( Builder Pattern )이다
시작은 🔴CustomUserDetailsService 에서 시작하여
"USER"라는 틀에 담긴 설정을 build()로 가져오는 것이다
- http
- HttpSecurity 타입이며 내부적으로 빌더 패턴을 사용해서 웹 보안 필터 체인을 설정한다
- build()가 호출되면 이전 설정된 모든 내용이 적용된 보안 설정 객체가 완성되어 Spring 컨테이너에 등록된
- Builder Pattern
- 객체를 한 번에 만드는 대신, 필요한 설정을 단계적으로 수행한 후, 마지막에 완성된 객체를 얻는 방식이다
- build() : 빌더 패턴의 마지막 단계이고, builder()와 설정 메서드를 이용해 앞서 설정한 모든 상태를 사용하여 원래의 최종 객체를 생성한다
- 역할 : 앞에 csrf, authorizeHttpRequests, formLogin 등이 완료된 후 설정된 내용에 따라 동작하는 SecurityFilterChain 객체를 생성하여 반환한다
🔴 CustomUserDetailsService
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final MemberRepository memberRepository;
public CustomUserDetailsService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MemberEntity member = memberRepository.findById(username)
.orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다."));
return User.builder()
.username(member.getId())
.password(member.getPw())
.roles("USER")
.build();
}
}
CustomUserDetailsService 라는 서비스 클래스 파일을 만든다
사용자 인증 과정 중 사용자의 핵심정보 ( ID, 비밀번호, 권한 )를 DB에서 조회하고 로드하는 역할이다
- Builder Pattern
- builder() : 빌더 패턴의 시작점이고, 객체를 대신 만들어줄 빌더 객체를 반환한다
- 역할 : 최종적으로 만들고자 하는 객체와 동일한 필드를 가지는 내부 클래스(빌더)의 인스턴스를 만든다
- User.builder() : DB 조회 결과를 기반으로 Security가 사용할 UserDetails 객체를 만드는 것
- 설정 메서드( username(), password(), roles() )를 호출
- 역할 : 빌더 객체의 필드값 설정 후 다시 빌더 객체 자신을 반환한다
- 이를 통해 메서드를 연속적으로 호출하는 메서드 체이닝(Method Chaining)이 가능해진다
- builder() : 빌더 패턴의 시작점이고, 객체를 대신 만들어줄 빌더 객체를 반환한다
- @Service
- 비즈니스 로직을 담당하는 클래스를 명시하는 것이다. 해당 클래스가 서비스 계층에 속하며 핵심 기능을 수행한다
- Bean 자동 등록
- @Service는 기본적으로 @Component 어노테이션을 포함하고 있다
- 역할 : Spring을 시작할 때, @Service 가 붙은 클래스를 자동 스캔하여 Spring Bean으로 등록한다
- 결과 : 개발자가 별도로 XML 설정이나 @Configuraion 클래스에 @Bean 메서드를 작성하지 않아도 SpringContainer가 이 클래스의 인스턴스를 관리하고 필요한 곳에 주입할 수 있게 된다
- @Component 특성을 상속받아 Spring Bean이 된다. 역할 분담과 가독성 때문에 @Service, @Controller, 기타 등을 쓴다
'SpringBoot > IntelliJ' 카테고리의 다른 글
| [ IntelliJ ] Spring Security로 역할 기반 접근 제어(RBAC) 구현하기: 관리자, 일반 회원, 비회원 권한 설정 가이드 (0) | 2025.10.08 |
|---|---|
| [ IntelliJ ] Spring Security 로그인 01 (0) | 2025.09.30 |
| [ IntelliJ ] JavaScript 활용 유효성 검사 ( 전화번호 ) (0) | 2025.09.29 |
| [ IntelliJ ] SpringBoot JavaScript 활용 유효성 검사 ( 비밀번호 확인 ) (2) | 2025.09.27 |
| [ IntelliJ ] Validation 유효성 검사 (4) | 2025.09.26 |