Security Session
SecurityConfig.class
@Configuration
@EnableWebSecurity // 스프링 시큐리티 필터가 스프링 필터체인에 등록이 된다.
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(CsrfConfigurer::disable);
http.authorizeHttpRequests(authorize ->
authorize
.requestMatchers("/user/**").authenticated()
.requestMatchers("/manager/**").hasAnyRole("ADMIN", "MANAGER")
.requestMatchers("/admin/**").hasAnyRole("ADMIN")
.anyRequest().permitAll()
);
http.formLogin(formLogin -> formLogin
.loginPage("/loginForm")
.loginProcessingUrl("/login")
// login 주소가 호출되면 시큐리티가 낚아채서 대신 로그인 진행
.defaultSuccessUrl("/"));
// 로그인 시 기존의 페이지로 접근
// 만약에 로그인이 안된 상황에서 user에 접근하다 loginForm으로 튕겨지면
// 다시 로그인 시 root 페이지가 아니라 user path로 접근
return http.build();
}
}
SecurityConfig에서 loginForm 페이지에서 로그인을 통해 login 페이지를 호출하면 스프링 시큐리티가 낚아채서 대신 로그인을 진행하는 부분을 추가했다.
시큐리티가 /login 주소 요청이 오면 낚아채서 로그인을 진행시켜주는데, 진행이 완료되면 시큐리티 session을 만들어준다. 이 때의 오브젝트 타입은 Authentication 타입 객체이다.
Authentication 안에 User의 정보가 있어야하는데, 이 때의 User 오브젝트 타입은 UserDetails 타입의 객체이다.
쉽게 말하면 시큐리티 세션이 있는데 여기 들어갈 수 있는 객체가 Authentication 객체이다.
이 객체 안에 user를 저장하는 것은 UserDetails 객체인 것이다.
나중에 꺼내서 사용할 때에 Session에서 Authentication 객체를 꺼내고, 그 안에서 UserDetails 객체를 꺼내면 user의 object에 접근할 수 있는 것이다.
PrincipalDetails.class
public class PrincipalDetails implements UserDetails {
// UserDetails를 implements 했으니
// Security Session => Authentication => UserDetails(PrincipalDetails)
private User user;
public PrincipalDetails(User user) {
this.user = user;
}
// 해당 User의 권한을 리턴
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 해당 반환타입으로 변환을 위해 collect라는 ArrayList를 만들고 안에 user.getRole()을 넣기
Collection<GrantedAuthority> collect = new ArrayList<>();
collect.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return user.getRole();
}
});
return collect;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
// 계정이 만료되었으면 false
@Override
public boolean isAccountNonExpired() {
return true;
}
// 계정이 잠겨있으면 false
@Override
public boolean isAccountNonLocked() {
return true;
}
// 자격 증명이 만료되었으면 false
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// 계정이 활성화되지 않았으면 false
@Override
public boolean isEnabled() {
return true;
}
}
위의 코드에서는 UserDetails를 implement 했으니 PrincipalDetails에서 user의 object에 접근할 수 있다.
PrincipalDetialsService.class
@Service
public class PrincipalDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User userEntity = userRepository.findByUsername(username);
if(userEntity != null) {
return new PrincipalDetails(userEntity);
}
return null;
}
}
시큐리티 설정에서 loginProcessingUrl("/login"); 이라고 된 부분이 있다.
위의 코드는 login 요청이 오면 자동으로 UserDetailsService 타입으로 IoC 되어있는 loadUserByName 함수가 실행되는 것이다.
만약에 userEntity가 없으면 null을 반환하고 userEntity가 있다면 PrincipalDetials에 userEntity를 넣어서 반환해준다.
이 값들이 스프링 시큐리티의 session에 들어가는 것이다.
loginForm에서 로그인 버튼을 누르면 <form action="/login"method="POST">를 통해 /login이 요청이 되는데, 스프링 시큐리티는 UserDetailsSerivce가 implement 되어있는 PrincipalDetailsService를 찾아서 loadUserByUsername을 호출한다.
이러한 방식으로 로그인을 진행하면 세션에 user의 object가 담기는 것이다.