실제로 로그인을 진행하는 PrincipalDetailService 클래스(UserDetailsService 타입인) 만들어서
스프링이 로그인 요청을 가로채어 username,password 변수2개를 PrincipalDetailService에 던져주는데
이때 password 부분처리는 스프링이 인코딩해서 알아서 하고 username이 DB에 있는지만 확인해주면 된다.
[PrincipalDetailService]
여기서 username이 DB에 있는지만 확인하기
1. loadUserByUsername 오버라이드해준다.(UserDetails 타입인 loadUserByUsername 를 통해서 로그인을 한다.)
@Service
public class PrincipalDetailService implements UserDetailsService{
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User principal = userRepository.findByUsername(username)
.orElseThrow(()->{
return new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다. : " + username);
});
return new PrincipalDetail(principal); //시큐리티의 세션이 유저 정보가 저장이 됨.
}
}
* 람다식 표현
User principal = userRepository.findByUsername(username)
.orElseThrow(()->{
return new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다. : " + username);
});
return new PrincipalDetail(principal);
username이 없으면 .orElseThrow(()->{});
username이 있으면 new PrincipalDetail(principal) 리턴한다.
userRepository 에 findByUsername 함수가 없으니 직접 만들어준다.
3. [UserRepository] findByUsername 함수이름 만들기
- 네이밍쿼리 방식
findByUsername => SELECT * FROM user WHERE username=?; (해당 쿼리 자동 실행)
public interface UserRepository extends JpaRepository<User, Integer>{
//SELECT * FROM user WHERE username=?;
Optional<User> findByUsername(String username);
}
4. return new PrincipalDetail(principal); 리턴할때 user가 null이니까 생성자가 있어야한다.
UserDetails 타입인 User를 리턴 할 수 없고 UserDetails 타입인 PrincipalDetail 을 리턴한다
public class PrincipalDetail implements UserDetails
[PrincipalDetail]
user가 null이니까 생성자 생성하기
public class PrincipalDetail implements UserDetails{
private User user; // 콤포지션( 객체를 들고 있는)
//생성자
public PrincipalDetail(User user) {
this.user = user;
}
...
리턴과 동시에 시큐리티의 세션에 유저 정보가 저장이 된다.
만약 loadUserByUsername 오버라이드를 안해준다면?
아래의 [PrincipalDetail] 에 user 생성자없고
return new PrincipalDetail() 으로 리턴이 되는데 내가 회원가입한 정보의 아이디와 패스워드가 아닌 아이디 user , 비밀번호는 콘솔창에 있는걸로 로그인이 된다
어렵다..
해쉬로 암호화 된 비밀번호 비교하기
시큐리티가 대신 로그인 할때 password 를 가로채는데 password가 어떻게 암호화가 되어 회원가입이 되었는지 알아야
로그인 할때도 같은 해쉬로 암호화해서 DB에 있는 해쉬랑 비교할 수 있다.
[SecurityConfig]
- configure(AuthenticationManagerBuilder auth) 로 오버라이드
@Autowired
private PrincipalDetailService principalDetailService;
@Bean
public BCryptPasswordEncoder encodePWD() { // 시큐리티가 들고있는 함수
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(principalDetailService).passwordEncoder(encodePWD());
}
principalDetailService 을 통해 로그인 하면 패스워드를 encode 처리하여
해쉬로 암호화 된 비번 vs DB에 있는 해쉬로 암호화 된 패스워드 를 비교할 수 있다.
패스워드가 같으면 로그인 되는것!
과정 간단히 다시 설명
<form action="/auth/loginProc" method="post">
해당 주소로 로그인 요청이 들어오면 스프링 시큐리티는 action ="/auth/loginProc" 주소로 가는 로그인 요청을 가로챈다
post 방식 X ▶ .loginProcessingUrl("/auth/loginProc")
[SecurityConfig]
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // csrf 토큰 비활성화 (테스트시 걸어두는게 좋음)
.authorizeRequests()
.antMatchers("/","/auth/**", "/js/**", "/css/**","/image/**")
.permitAll()
.anyRequest() // 다른 요청들은
.authenticated()// 인증이 되어야한다.
.and()
.formLogin()
.loginPage("/auth/loginForm")
.loginProcessingUrl("/auth/loginProc") // 스프링 시큐리티가 해당 주소로 오는 로그인 요청을 가로채어 로그인한다.
.defaultSuccessUrl("/"); // 정상적으로 요청이 완료되면 "/" 주소로 이동
}
username,password 정보를 PrincipalDetailService 클래스의 loadUserByUsername 에 던져준다
[PrincipalDetailService]
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User principal = userRepository.findByUsername(username)
.orElseThrow(()->{
return new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다. : " + username);
});
return new PrincipalDetail(principal);
username 정보를 DB에 찾아 유저 obj을 만들어 넣어주고 해당 obj을 PrincipalDetail에 담아 리턴을 해줄때
[SecurityConfig]
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(principalDetailService).passwordEncoder(encodePWD());
}
principalDetailService가 로그인 요청을 하고 리턴시
사용자가 적은 패스워드를 해쉬로 암호화하고 VS DB의 회원가입 되어 암호화 된 비밀번호와 비교를 먼저해준다.
끝나고 정상일때 세션이 만들어진다.
스프링 시큐리티 세션에 PrincipalDetail로 감싸져서 - PrincipalDetail(principal) - 유저 정보가 저장이 된다.
.defaultSuccessUrl("/"); 로그인이 되고나면 "/" 주소로 이동한다.
/ 주소는 BoardController에 get매핑 주소이다.
@GetMapping({"", "/"})
public String index() {
return "index";
}
컨트롤러에서 세션에 접근하는 법
@AuthenticationPrincipal PrincipalDetail principalDetail 쓰면 된다.
@Controller
public class BoardController {
@GetMapping({"","/"})
public String index(@AuthenticationPrincipal PrincipalDetail principalDetail) { // 컨트롤러에서 세션을 찾는법
System.out.println("로그인 사용자 아이디 : "+ principalDetail.getUsername());
return "index";
}
}
아래처럼 안쓰면 에러가 나서 못찾는다.
@Controller
public class BoardController {
private PrincipalDetail principal;
@GetMapping({"","/"})
public String index() { // 컨트롤러에서 세션을 찾는법
System.out.println("로그인 사용자 아이디 : "+ principal.getUsername());
return "index";
}
}
하지만 메인페이지에서 로그인 인증이 필요없어서 @AuthenticationPrincipal PrincipalDetail principalDetail 지운다
** 로그아웃을 하고싶을땐 주소 뒤에 /logout만 붙여도 된다. 스프링 시큐리티에서 정한 default 값이다.
'Spring boot | 블로그 만들기' 카테고리의 다른 글
블로그 만들기 | 게시글 목록 보기, 페이징 처리하기 (0) | 2022.11.14 |
---|---|
블로그 만들기 | 게시글 쓰기 (0) | 2022.11.14 |
스프링 시큐리티 로그인 | UserDetails , getAuthorities() 람다식 (0) | 2022.11.13 |
비밀번호 해쉬화 하여 회원가입하는 법 (0) | 2022.11.12 |
스프링 시큐리티 로그인 | SecurityConfig 설정 (0) | 2022.11.09 |