아! 졸리다!! 하루를 더 알차게 쓰려고 노력해보자!!
먼저 우리는 시큐리티에게 보안을 위임한걸 기억하자! 즉, 시큐리티가 로그인까지 대신 해준다는 이야기다!!
ㅎ 여기저기서 참 많이 본 사진이죠?
💡 시큐리티 또한 Service를 통해서 User(데이터)를 가져오고, 세션에 등록하는 과정을 거친다.
위 그림의 엄청 많은 단계는 대부분 시큐리티가 알아서 해준다! (따봉리티야 고마워~ 🦔)
하지만 이 유저가 정확한 유저인지, 우리의 회원리스트에 있는지!는 우리가 직접 구현해야한다. (따봉개발자야 고마워~ 🧑💻)
그렇게 우리가 "이 유저는 올바른 유저야~" 라고 user 정보를 전달해주면 시큐리티는 이 user를 세션에 등록해준다.
하지만 인생은 내마음대로 되는 것은 아니지! 😼 시큐리티가 요구하는 타입!에 맞춰서 우리는 값을 전달하고, 사용해야한다~
Authentication
이다. Authentication
에 들어갈 내부 객체는 UserDetails
이다.UserDetailsService
이다!UserDetails
타입(인터페이스)의 구현 클래스에 넣는다.UserDetails
을 구현한 클래스를 보통 PrincipalDetails
이라고 한다.UserDetails
를 반환해주고, DB에 접근할 UserDetailsService
타입(인터페이스)의 클래스 로직을 짠다.UserDetailsService
을 구현한 클래스를 보통 PrincipalDetailsService
라고 한다. 이 클래스는 결과적으로 PrincipalDetails
를 반환해야하고, 이 반환 메서드는 loadUserByUsername
이다. 추가적으로!
시큐리티에게 로그인에 사용할 url, 로그인 페이지에 사용할 페이지, 로그인이 성공했을 때, 실패했을 때 이동할 url을 알려줘야한다.
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeRequests((authorizeRequests) -> authorizeRequests
.requestMatchers("/user/**").authenticated()
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/manager/**").hasAnyRole("ADMIN", "MANGER"))
.formLogin(formLogin -> formLogin
.loginPage("/loginForm") //-> 사용자 정의 로그인 페이지
.loginProcessingUrl("/login") //이 주소가 호출 되면 시큐리티가 낚아채서 로그인 ㄱㄱ
.defaultSuccessUrl("/")); // -> 로그인 성공 후 이동 페이지
return http.build();
}
UserDetails
구현 클래스 PrincipalDetails
만들기package com.jsh.securitystudy.config.auth;
import com.jsh.securitystudy.model.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
/*
시큐리티가 /login 주소 요청이 오면 낚아채서 로그인을 진행시킨다.
로그인 진행이 완료가 되면?? session을 만들어준다.
=> Security ContextHoder
이 Session에 들어갈 수 있는 타입이 정해져있다. 바로바로 ~!~!~!
=> Authentication 타입 객체
=> 이 객체 안에는 user정보를 담기위한 또 타입이 정해져있다..
=> 그게 바로 UserDetails 타입 객체
Security Session -> Authentication -> UserDetails
*/
public class PrincipalDetails implements UserDetails {
//여기임
private User user;
public PrincipalDetails(User user) {
this.user = user;
}
//해당 유저의 권한을 리턴하는 곳!
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
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();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
//휴면 계정 사용할 때...
return true;
}
}
UserDetailsService
구현 클래스 PrincipalDetailsService
만들기package com.jsh.securitystudy.config.auth;
import com.jsh.securitystudy.model.User;
import com.jsh.securitystudy.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
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;
/*
시큐리티 설정에서 loginProcessingUrl("/login");
-> login 요청이 오면 자동으로 UserDetailsService 타입으로 IoC되어 있는 loadUserByUsername 실행~!!!
*/
@Service
public class PrincipalDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
//⭐PrincipalDetails를 반환해준다!!
/*
시큐리티 session = Authentication = UserDetails
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User userEntity = userRepository.findByUsername(username);
System.out.println("username = " + username);
System.out.println("userEntity = " + userEntity);
if (userEntity != null) { //해당 유저가 존재할 때, 즉 로그인이 가능할 때
return new PrincipalDetails(userEntity);
}
else {
return null;
}
/*
무엇을 return 해주죠? ??
=> PrincipalDetails를요!! 이것은 곧??? UserDetails
이 메서드는 어디로 리턴할까??
바로 Authentication의 내부로 리턴되어서 들어간다.
Authentication이건 session으로 또 가고!!
*/
}
}
먼저 회원가입을 해야하고, 로그인을 했을 때 의도대로 잘 움직이는 걸 확인했다.
아이디나 비밀번호를 틀리면 작동하지 않는 것도 확인!
만약 틀린거 하면 ?
작동 안된다~!
아주아주 문제가 많은 코드다!
테스트 코드도 작성하지 않았고, 에러 처리도 하지 않았다!
차근차근하면 된다
https://taetoungs-branch.tistory.com/204
🌿 인프런의 최주호 강사님의 스프링부트 시큐리티 & JWT 강의를 참고하는 중
🔗 깃허브 링크