Spring Security) : 스프링 기반의 애플리케이션 보안을 담당하는 스프링 하위 프레임워크이다authentication) : 사용자의 신원을 입증하는 과정authorization): 사이트의 특정 부분에 접근할 수 있는 권한을 확인하는 과정인증,인과 관련 코드를 아무런 도움 없이 작성하면 어렵고, 시간도 많이 든다
스프링 시큐리티를`사용하면 쉽게 처리할 수 있다

UsernamePasswordAuthenticationFilter : 아이디, 패스워드가 넘어오면 인증 요청을 위임하는 ✅인증 관리자 역할FilterSecurityInterceptor : 권한 부여 처리를 위임해, 접근 제어 결정을 쉽게 하는 ✅접근 결정 관리자 역할

UsernamePasswordAuthenticationFilter에서 요청에 들어온 아이디,비밀번호의 유효성 검사를 한다UsernamePasswordAuthenticationToken을 만들어 넘겨준다UsernamePasswordAuthenticationToken를 ProviderManager을 구현한 AuthenticationManager (인증 메니저)로 보내준다UsernamePasswordAuthenticationToken을 AuthenticationProvider로 다시 보내준다AuthenticationProvider는 id를 가지고 인터페이스인 UserDetailService를 이용해 db에서 해당 id를 가진 객체를 찾는다이때, 해당 entity의 service는
UserDetailService를 구현하고 있다
이 때, 전달되는 UserDetailService를 구현한 해당 엔티티의 Service 계층의 findById를 이용해, 찾은 객체이다
7.이때 입력받은 정보와 UserDetail의 ✅정보를 비교해, 인증 처리를 완료한다
8. 인증 성공 -> AuthenticationSuccessHandler
9. 인증 실패 -> AuthenticationFailureHandler 를 실행
// 스프링 시큐리티를 사용하기 위한 스타터 추가
implementation 'org.springframework.boot:spring-boot-starter-security'
// 타임리프에 스프링 시큐리티를 사용하기 위한 의존성 추가
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurit6'
// 스프링 시큐리티를 테스트하기 위한 의존성 추가
testImplementation 'org.springframework.security:spring-security-test'
public class User implements UserDetails {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id",updatable = false)
private Long id;
private String email;
private String password;
...
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority("user"));
}
@Override
public String getUsername() {
return 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;
}
}
getAuthorities : Collection<? extends GrantedAuthority>타입 반환getUsername : String 반환isAccountNonExpired : boolean타입 반환isCredentialsNonExpired : boolena타입 반환 isEnabled() : boolean타입 반환public interface UserRepository extends JpaRepository<User,Long> {
public Optional<User> findByEmail(String email);
}
@Service
@RequiredArgsConstructor
public class UserDetailService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return userRepository.findByEmail(email)
.orElseThrow(() -> new ApiException(UserError.NOT_FOUND_USER));
}
}
UsernamePasswordAuthenticationToken를 확인하기 위해서 사용하는 serviceUserDetailsService를 구현하고, UserDetails를 반환하는 loadUserByUsername 메서드를 오버라이드 한다@RequiredArgsConstructor
@Configuration
public class WebSecurityConfig {
private final UserDetailService userDetailsService;
@Bean
public WebSecurityCustomizer configure(){
return web -> web.ignoring()
.requestMatchers("/static/**");
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests()
.requestMatchers("/login", "/signup", "/user").permitAll()
.anyRequest().authenticated()
.and()
.formLogin((login)->login
.loginPage("/login")
.defaultSuccessUrl("/articles"))
.logout((logout)->logout
.logoutSuccessUrl("/login")
.invalidateHttpSession(true))
.csrf(AbstractHttpConfigurer::disable) // csrf 비활성화
.build();
}
// 패스워드 인코더로 사용할 빈 등록
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
//인증 관리자 관련 설정
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() throws Exception {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
return daoAuthenticationProvider;
}
@Bean
public AuthenticationManager authenticationManager() throws Exception {//2 - AuthenticationManager 등록
DaoAuthenticationProvider provider = daoAuthenticationProvider();//DaoAuthenticationProvider 사용
return new ProviderManager(provider);
}
}
.requestMatchers("/login", "/signup", "/user").permitAll()
.anyRequest().authenticated()
permitAll)하게 한다anyRequest())는 별도의 인가는 필요하지 않지만 인증을 해서 접근 할 수 있다(authenticated()).formLogin((login)->login
.loginPage("/login")
.defaultSuccessUrl("/articles"))
thymeleaf를 이용한 html 페이지를 주었지만.logout((logout)->logout
.logoutSuccessUrl("/login")
.invalidateHttpSession(true))
.csrf(AbstractHttpConfigurer::disable)
bCryptPasswordEncoder를 이용한다daoAuthenticationProvider를 사용하고✅userDetailsService로 token의 유효성 검사를 진행한다bCryptPasswordEncoder를 사용한다authenticationManager 아까 만든 인증 관리자인 AuthenicationProvider 객체를 가져와서 사용한다Exception handler로 이동해 예외 처리를 해서 해당 예외를 담은 Api를 응답 메시지로 전달한다