| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
Tags
- SpringSecurity 로그아웃
- 중첩for
- StringBuffer
- D2Coding
- 증감 연산자
- 회원정보 수정
- 중첩 if
- jdk 설정
- SpringSecurity 로그인
- 스프링시큐리티 로그아웃
- Springsecurity
- 스프링부트 로그인
- MySQL workbench dump
- 별찍기
- @PreAuthorize("isAuthenticated()")
- Node.js 설치
- SQL dump
- JSP 실습
- if else if
- 이클립스 설치
- JAVA 변수
- SQL import
- 클래스 형변환
- 인텔리제이 Web 애플리케이션
- 접근제어자
- Scanner 시간구하기
- SpringBoot
- if else
- StringBuilder
- System클래스
Archives
- Today
- Total
gi_dor
Spring Security 로그인 / 로그아웃 본문
과거 : 직접 세션을 다루는 로그인
@PostMapping("/login")
public String loginComplete(@Valid @ModelAttribute MemberDTO memberDTO,BindingResult bindingResult,HttpServletRequest request) {
// DTO에서 정의한 입력값 검증하기
if (bindingResult.hasErrors()) {
return "login"; // login.html
}
// memberService에서 login 메서드 호출
Member loginMember = memberService.login(memberDTO.getEmail(), memberDTO.getPassword());
// 로그인 실패 처리 - 아이디 공백 , 비밀번호 틀릴경우
if (loginMember == null) {
bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
log.warn("로그인 실패 : {} ,{}",memberDTO.getEmail(),memberDTO.getPassword());
return "login";
}
// 로그인 성공 (세션 생성)
HttpSession session = request.getSession();
session.setAttribute("loginMember", loginMember);
return "redirect:/";
}
// 로그아웃
@PostMapping("/logout")
public String logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return "redirect:/";
}
<!-- 로그인하지 않은 경우 -->
<th:block th:if="${session.loginMember == null}">
<a th:href="@{/members/login}">로그인</a>
</th:block>
<!-- 로그인한 경우 -->
<th:block th:if="${session.loginMember != null}">
<form th:action="@{/members/logout}" method="post">
<button type="submit">로그아웃</button>
</form>
</th:block>
- 로그인 : POST /members/login 요청은 Spring Security 가 가로채기에 MemberService 에서 loadUserByUserName을 통해 인증을 처리한다
- 로그아웃 : POST /members/logout 요청은 Spring Security가 세션을 무효화 하고 쿠키 삭제를 해서 처리해준다
- 상태확인 : HTML 에서 sec:authorize을 통해 컨트롤러 도움없이 로그인 상태를 확인할 수있다
Spring Security에게 모든 것을 위임해버리기
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
UserDetails와 UserDetailsService
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EntityListeners(AuditingEntityListener.class) // 생성일/수정일 자동 관리
@Entity // JPA가 관리하는 엔티티가됨
@Getter // getter 필드 값 가져오기
@Table(name = "member") // 테이블 이름을 명시적으로 지정
public class Member implements UserDetails { // Java에는 Member 객체있음 , DB에는 member 라는 테이블 있음 이걸 1:1 매핑해주는 Entity
// UserDetails 상속받아서 인증객체로 사용
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // 내가 건들이지 않고 DB가 알아서 증가시킴
private Long id; // 회원 고유 ID
@Column(nullable = false, unique = true) // 중복 불가
private String email; // 이메일 (로그인 ID)
@Column(nullable = false)
private String password;
private String name;
@Column(nullable = false, unique = true)
private String nickname; // 사용자 커뮤니티나 마이정보에서 사용할 닉네임
// 나중에 카카오 로그인을 위해 미리 만들어둠 "google", "kakao", "naver" 등
private String provider;
// 사용자인지 , 관리자인지 구분 - 기본 회원가입시 사용자 default값 설정
@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 10)
private MemberRole memberRole;
// 등록일
@CreatedDate
@Column(nullable = false, updatable = false)
private LocalDateTime createdAt;
// 수정일
@LastModifiedDate
private LocalDateTime updatedAt;
@Builder
public Member(String email, String password, String name, String nickname, String provider, MemberRole memberRole) {
this.email = email;
this.password = password;
this.name = name;
this.nickname = nickname;
this.provider = provider;
this.memberRole = memberRole;
}
@Builder
public Member(String email, String password) {
this.email = email;
this.password = password;
}
// 권한반환
@Override
@NonNull
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority("ROLE_"+ memberRole.name()));
}
// 사용자의 id를 반환 - 고유한값
@Override
@NonNull
public String getUsername() {
return this.email;
}
// 계정이 만료되었는지 확인
@Override
public boolean isAccountNonExpired() {
return true;
}
// 계정이 잠금되어있는 지 확인
@Override
public boolean isAccountNonLocked() {
return true;
}
// 패스워드 만료 확인
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// 계정 사용 여부 확인
@Override
public boolean isEnabled() {
return true;
}
}
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true) /
public class MemberService implements UserDetailsService {
private final MemberRepository memberRepository;
// 비밀번호를 암호화하기 위한 도구 (SecurityConfig에 만들어둠)
private final PasswordEncoder passwordEncoder;
@NonNull
@Override
public UserDetails loadUserByUsername(@NonNull String email) {
return memberRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("해당하는 이메일을 찾을 수 없습니다 : " + email));
}
SecurityConfig를 설정하여, 로그인/로그아웃 절차를 구체적으로 지시
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// http 객체를 사용하여 메서드 체이닝 방식으로 보안 설정을 구성합니다.
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/", "/css/**", "/js/**", "/images/**", "/members/login", "/members/join").permitAll()
.requestMatchers("/members/mypage").authenticated()
// 그외 페이지는 인증 요구할꺼임
.anyRequest().authenticated()
)
// 폼 기반 로그인 설정
.formLogin(form -> form
.loginPage("/members/login") // 로그인 페이지 URL GET
.loginProcessingUrl("/members/login") // 로그인 처리 URL POST
.usernameParameter("email")
.defaultSuccessUrl("/", true) // 로그인 성공 시 항상 메인 페이지로 이동
.permitAll() // 로그인 페이지 자체는 누구나 접근 가능
)
// 로그아웃 설정
.logout(logout -> logout
.logoutUrl("/members/logout") // 로그아웃을 처리할 URL
.logoutSuccessUrl("/") // 로그아웃 성공 후 리다이렉트될 URL
.invalidateHttpSession(true) // 세션 무효화
.deleteCookies("JSESSIONID") // 쿠키 삭제
);
return http.build();
}
}
.loginPage("/members/login")
- 컨트롤러의 GET 요청과 매핑이라고 생각하면된다
.loginProcessingUrl("/members/login")
- Security 가 POST 요청을 보고 , 중간에 가로채서 MemberSerivce 안에 있는 loadUserByUserName 을 호출
- 컨트롤러 XXX
- .loginProcessingUrl()에 설정된 URL은 Spring Security의 인증 필터를 깨우는 '가상의' 주소
| 설정 | 목적 | HTTP 메서드 | 매핑 대상 | 컨트롤러에 메서드 필요한가 |
| .loginPage() | 로그인 화면 보기 | GET | MemberController | O |
| .loginProcessingUrl() | 로그인 정보 제출/처리 | POST | Spring Security | X |
<!-- 로그인하지 않은 경우 -->
<!-- sec:isAnonymous() : 현재 사용자가 익명(비로그인) 상태인지 확인 -->
<th:block sec:authorize="isAnonymous()">
<li class="nav-item ms-2">
<a class="btn btn-outline-light btn-sm" th:href="@{/members/login}">로그인</a>
</li>
<li class="nav-item ms-2">
<a class="btn btn-primary btn-sm" th:href="@{/members/join}">회원가입</a>
</li>
</th:block>
<!-- 로그인한 경우 -->
<!-- sec:isAuthenticated() : 현재 사용자가 인증(로그인)된 상태인지 확인 -->
<th:block sec:authorize="isAuthenticated()">
<li class="nav-item">
<!-- sec:authentication="name" : 현재 인증된 사용자의 이름을 표시 -->
<span class="nav-link text-white">환영합니다 <span sec:authentication="principal.nickname"></span>님</span>
</li>
<li class="nav-item ms-2">
<form th:action="@{/members/logout}" method="post" class="d-inline">
<button type="submit" class="btn btn-outline-light btn-sm">로그아웃</button>
</form>
</li>
</th:block>


728x90
'Back_End > SpringSecurity' 카테고리의 다른 글
| Spring Security 로그인 실패 시 에러메세지 SimpleUrlAuthenticationFailureHandler (0) | 2026.02.23 |
|---|---|
| Spring Security와 BCrypt 해싱 (0) | 2026.02.04 |
