Post

Spring Security 오류 FindBy~()가 DB에서 객체를 못 찾을 때

Spring 스터디를 하면서 Spring Securtiy를 통해 로그인, 로그아웃 구현을 하였다.

처음에는 내 식대로 커스텀마이징 하다가 많인 오류가 생겼고, 그냥 따라치기로 했다.

도대체 뭐가 문제였을까?

일단 알아야할 것이 있었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception{
        httpSecurity.
                authorizeRequests()
                .antMatchers("/login","/signup","/user").permitAll() //누구나 접근 허용
                .antMatchers("/").hasRole("USER") //USER, ADMIN만 접근 가능.
                .antMatchers("/admin").hasRole("ADMIN") //Admin만 접근 가능.
                .anyRequest().authenticated() //나머지 요청들은 권한의 종류에 상관없이 권한이 있어야 접근 가능.
            .and()
                .formLogin()
                .loginPage("/login") // 로그인 페이지 설정.
                .defaultSuccessUrl("/") // 로그인 성공시 라다이렉션 페이지
            .and()
                .logout()
                .logoutSuccessUrl("/login") // 로그아웃 성공시 리다이렉션 페이지
                .invalidateHttpSession(true) // 세션 날리기
        ;
    }

위의 코드에서 원래는 loginProcess()가 있어야하는데, 없어도 된단다. Security에서 loginProcess()가 없을 때 UserDetails를 상속받은 서비스에서 필수 구현 메소드인 loadUserByUserName()를 찾아 해당 로직을 수행한다.

Service 코드

1
2
3
4
5
6
@Override
    public UserInfo loadUserByUsername(String email) throws UsernameNotFoundException{
        Optional<UserInfo> findEmail = userRepository.findByEmail(email);
        System.out.println(findEmail + " !@!!#@!#");
        return userRepository.findByEmail(email).orElseThrow(() -> new UsernameNotFoundException(email));
    }

해당 로직을 수행할 때 Repositry에서의 코드는 다음과 같다.

1
2
public interface UserRepository extends JpaRepository<UserInfo,Long> {
    Optional<UserInfo> findByEmail(String email);

근데 DB에는 잘 저장되어 있는데 계속 Optional값을 뱉어내는 것이다.

이유가 뭘까 하다가 임시방편으로 @Query를 이용해서 해결했다.

1
2
3
4
5
6
public interface UserRepository extends
JpaRepository<UserInfo,Long>{
    @Query("select u from UserInfo u where u.email like %?1")
    Optional<UserInfo> findByEmail(String email);

}

하지만 Spring에서 직접 쿼리를 내보내는 것은 권장하지 않기에 근본적인 원인을 해결해야 했다.

위의 방식으로 로그인을 했더니, 일반 User계정 로그인은 잘 되고 Admin은 객체를 못 찾겠다고 또 오류를 뱉길래 그 로그를 그대로 구글링했다.

참고 : https://kimji0139.tistory.com/34

위 글에 따르면 view에서 name태그를 username으로 바꿔야한다고 하더라.

그래서 네임태그를 바꾸고 @Query문을 지우니 해결..

아마 SpringSecuriy 내부에서 로직을 돌릴 때 thymeleaf와 어떤 관계가 있나보다.


(정확하지 않음.) themleaf에서

1
 <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />

라는 문법을 통해 Form을 누르면(뭐 로그인하는 버튼) hidden 속성으로 CSRF토큰이라는 세션을 날림.
->
Spring security Config에서 설정한

1
protected void configure(HttpSecurity httpSecurity) throws Exception

이 함수가 작동함. 함수 내부에 있는

1
loginProcess("URL")

를 작성했으면 Controller에 있는 “URL”을 로직을 수행 작성 안 했으면 그냥 Security 내부에서 Login 처리를 함. Login 처리를 위해 필수 구현함수인

1
public UserInfo loadUserByUsername(String email) throws UsernameNotFoundException

loadUser~함수를 실행함.위 함수를 통해 Repo에 접근함(repo는 JPArepository를 Extends한 것) 그 속에

1
Optional<UserInfo> findByEmail(String email);

JPArepositroy를 Extends하면 저렇게 함수를 작성하기만해도 알아서 JPQL을 만듦(수정하고 싶으면 내가 했던 @Query를 사용하면 됨)

근데 그 내부에서 값을 view에서 name으로 지정한 username을 기준으로 찾나봄?

csrf관련 : https://reiphiel.tistory.com/entry/spring-security-csrf

This post is licensed under CC BY 4.0 by the author.