📙 지난 시간
formLogin.loginPage("/loginForm") 을 통해, 권한이 필요한 url 요청시 loginForm으로 이동하도록 했다.
아래 예시에서는 .authorizeRequests.requestMatchers("/user/**").authenticated(); 이므로, /user/**로 요청되는 주소는 loginForm으로 이동한다.
/admin/** 이나 /manager/**의 경우에는 Role확인이 필요하므로 403 에러가 뜬다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable) // 사이트 위변조 요청 방지
.authorizeHttpRequests((authorizeRequests) -> { // 특정 URL에 대한 권한 설정.
authorizeRequests.requestMatchers("/user/**").authenticated();
authorizeRequests.requestMatchers("/manager/**")
.hasAnyRole("ADMIN", "MANAGER"); // ROLE_은 붙이면 안 된다. hasAnyRole()을 사용할 때 자동으로 ROLE_이 붙기 때문이다.
authorizeRequests.requestMatchers("/admin/**")
.hasRole("ADMIN"); // ROLE_은 붙이면 안 된다. hasRole()을 사용할 때 자동으로 ROLE_이 붙기 때문이다.
authorizeRequests.anyRequest().permitAll();
})
.formLogin((formLogin) -> {
formLogin
.loginPage("/loginForm") // 권한이 필요한 요청은 해당 url로 리다이렉트
.loginProcessingUrl("/login") // login 주소가 호출되면 시큐리티가 낚아채서 대신 로그인을 해준다.
.defaultSuccessUrl("/"); //로그인 성공시 /주소로 이동
})
.build();
}
📒 시큐리티 로그인 구현
이제, formLogin에 loginProcessingUrl과 defaultSuccessUrl을 추가해보자.
- loginProcessingUrl("/login") : "/login" 주소가 호출되면 스프링 시큐리티가 낚아채서 대신 로그인을 진행해준다.
- defaultSuccessUrl("/") : "/loginForm" 주소 요청해서 로그인 성공 시, "/" 주소로 이동한다. ( 단, "/user"와 같이 다른 주소를 요청했는데 loginForm으로 리다이렉트 된 경우에는 원래 요청했던 "/user" 주소로 이동한다. )
🧨 시큐리티 로그인 진행과정은 다음과 같다.
- /login 주소 요청이 와서 시큐리티가 낚아채서 로그인을 진행한다.
- 완료되면 시큐리티가 시큐리티 session을 만들어준다.
- 시큐리티 세션 안의 정보를 이용해 회원의 정보를 가져온다. 시큐리티 Session ((내부 Authentication ( 내부 UserDetails ))
따라서, Security Session => Authentication => UserDetails => 유저 정보를 통해 정보를 가져온다.
먼저, UserDetails 에서 유저 정보를 가져오는 클래스인 PrincipalDetails 클래스를 만들어보자.
public class PrincipalDetails implements UserDetails {
private User user; // Composition
public PrincipalDetails(User user){
this.user = user;
}
// 해당 User의 권한을 리턴하는 곳
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collect = new ArrayList<>();
collect.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return user.getRole();
}
});
return null;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
// 우리 사이트에서 1년동안 회원이 로그인을 안하면 휴먼 계정으로 하기로 함
// 현재 시간 - 로그인 한 시간 >= 1년 이면 false 반환
return true;
}
}
PrincipalDetails 클래스는 UserDetails 인터페이스를 실행하므로 UserDetails(PrincipalDetails)의 구조이다.
이제, Session에서 UserDetails를 반환하는 클래스인 PrincipalDetailsService를 만들어보자.
@Service
public class PrincipalDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
// 시큐리티 session ((내부 Authentication (내부 UserDetails))
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user!= null){
return new PrincipalDetails(user);
}
return null;
}
}
login 요청이 오면 자동으로 UserDetailsService 타입으로 IoC 되어있는 클래스의 loadByUsername이 실행된다.
로그인의 경우, 시큐리티가 자동으로 실행되므로, UserDetailsService 타입으로 되어있는 클래스만 생성하면 완료! 😄
이제 실행해보면, 로그인도 잘 실행되는 것을 볼 수 있다!!!
'SpringSecurity' 카테고리의 다른 글
스프링 시큐리티 복습 1 - 시큐리티 기본 API 및 Filter 이해 (0) | 2023.08.18 |
---|---|
[5강] 시큐리티 권한처리 (0) | 2023.07.27 |
[3강] Security 회원 가입 (0) | 2023.07.26 |
[2강] SecurityConfig 설정 (Spring Security v6.1) - (0) | 2023.07.26 |
[1강] SpringSecurity DB 적용 (0) | 2023.07.26 |
댓글