@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorizeHttpRequests) -> authorizeHttpRequests
.requestMatchers(new AntPathRequestMatcher("/**")).permitAll())
.csrf((csrf) -> csrf
.ignoringRequestMatchers(new AntPathRequestMatcher("/h2-console/**")))
.headers((headers) -> headers
.addHeaderWriter(new XFrameOptionsHeaderWriter(
XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN)))
.formLogin((formLogin) -> formLogin
.loginPage("/user/login")
.defaultSuccessUrl("/"))
.logout((logout) -> logout
.logoutRequestMatcher(new AntPathRequestMatcher("/user/logout"))
.logoutSuccessUrl("/")
.invalidateHttpSession(true))
;
return http.build();
}
@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
http.formLogin()
.loginPage("/user/login") //
.defaultSuccessUrl("/");
http.formLogin(formLogin ->
formLogin
.loginPage("/user/login") // 커스텀 로그인 페이지
.loginProcessingUrl("/user/doLogin") // 로그인 처리 URL
.usernameParameter("email") // 사용자명 파라미터 이름 변경
.passwordParameter("passwd") // 비밀번호 파라미터 이름 변경
.defaultSuccessUrl("/dashboard") // 로그인 성공 시 이동 경로
.failureUrl("/user/login?error=true") // 로그인 실패 시 이동 경로
);
안 쓰는 경우?
-OAuth2 로그인 (http.oauth2Login())
-JWT 기반 인증 (필터 커스터마이징)
-API 서버에서 토큰 기반 인증만 사용하는 경우
.logout((logout) -> logout
.logoutRequestMatcher(new AntPathRequestMatcher("/user/logout")) // get/post 방식 로그아웃 처리
.logoutSuccessUrl("/") //로그아웃 처리 후 url
.invalidateHttpSession(true) // 로그아웃 후 세션 파기
.deleteCookies("JSESSIONID") // 쿠키 내 세션정보 파기
)
<div th:if="${param.error}">
<div class="alert alert-danger">
사용자ID 또는 비밀번호를 확인해 주세요.
</div>
</div>
<li class="nav-item">
<a class="nav-link" sec:authorize="isAnonymous()" th:href="@{/user/login}">로그인</a>
<a class="nav-link" sec:authorize="isAuthenticated()" th:href="@{/user/logout}">로그아웃</a>
</li>
package com.mysite.sbb.user;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<SiteUser, Long> {
Optional<SiteUser> findByusername(String username);
}
package com.mysite.sbb.user;
import lombok.Getter;
@Getter
public enum UserRole {
ADMIN("ROLE_ADMIN"),
USER("ROLE_USER");
UserRole(String value) {
this.value = value;
}
private String value;
}
enum 열거 자료형을 이용하여 ADMIN, USRER 라는 상수를 등록하고 각각의 값을 부여. 이때 ROLE_ 이라는 접두사를 반드시 적용. @Setter가 없는 이유는 상수 선언이기 때문
package com.mysite.sbb.user;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class UserSecurityService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<SiteUser> _siteUser = this.userRepository.findByusername(username);
if (_siteUser.isEmpty()) {
throw new UsernameNotFoundException("사용자를 찾을수 없습니다.");
}
SiteUser siteUser = _siteUser.get();
List<GrantedAuthority> authorities = new ArrayList<>();
if ("admin".equals(username)) {
authorities.add(new SimpleGrantedAuthority(UserRole.ADMIN.getValue()));
} else {
authorities.add(new SimpleGrantedAuthority(UserRole.USER.getValue()));
}
return new User(siteUser.getUsername(), siteUser.getPassword(), authorities);
}
}

// 일반적 적용단계
@Service // 서비스계층임을 명시하면서 Spring이 자동으로 Bean에 등록하고 의존성 주입 대상으로 등록하기 위해 사용
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다: " + username));
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
getAuthorities(user)
);
}
private Collection<? extends GrantedAuthority> getAuthorities(User user) {
return List.of(new SimpleGrantedAuthority("ROLE_" + user.getRole()));
}
}