로그인, 로그아웃

Yuri Lee·2020년 11월 11일
0

스프링 시큐리티 로그인/ 로그아웃 설정

@Configuration // 일단 메모리에 떠야 하니까 어노테이션 추가해줌
@EnableWebSecurity // 직접 시큐리티 설정을 하겠다는 의미
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/", "/login", "/sign-up", "/check-email-token",
                        "/email-login", "/check-email-login", "/login-link").permitAll()
                .mvcMatchers(HttpMethod.GET, "/profile/*").permitAll()
                .anyRequest().authenticated();
        http.formLogin()  // 스프링 시큐리티가 가지고 있는 loginform이 있음, loginPage를 설정해 커스텀하게 표현할 수 있다. 
                .loginPage("/login").permitAll(); //로그인 안해도 접근, 해도 접근할 수 있도록 permitAll ! 

        http.logout()
                .logoutSuccessUrl("/");
    }
}

main 컨트롤러

@GetMapping("/login")
public String login() {
	return "login";
}

로그인 page

로그인 뷰는 우리가 만들겠지만, 로그인 처리는 스프링 시큐리티에게 맡길 것

   <div th:if="${param.error}" class="alert alert-danger" role="alert">
                <p>이메일(또는 닉네임)과 패스워드가 정확하지 않습니다.</p>
                <p>또는 확인되지 않은 이메일을 사용했습니다. 이메일을 확인해 주세요.</p>
                <p>
                    확인 후 다시 입력하시거나, <a href="#" th:href="@{/find-passsword}">패스워드 찾기</a>를 이용하세요.
                </p>
            </div>

param에 error가 있으면 .. 아래와 같은 정보를 보여줌. param.error은 null이 아니면 true 이다. 계정을 찾지 못하는 경우에는 /find-passsword 로 갈 수 있도록 함.

  • margin: 사람과 사람간의 거리
  • padding: 나의 지방

스프링 시큐리티 로그인 기본값

스프링 시큐리티가 알아서 들어오는 값과 패스워드로 로그인을 처리한다. 이때 그냥 처리하는 게 아니라 디비에 저장되어있는 정보를 참조해서 인증을 해야 한다. 디비 정보를 우리의 정보를 조회할 수 있는 UserDetailService 라는 것을 만들어줘야 한다. 로그인, 로그아웃을 처리하는 핸들러는 만들지 않아도 되지만 UserDetailService 인터페이스를 구현해야 한다.

    @Override
    public UserDetails loadUserByUsername(String emailOrNickname) throws UsernameNotFoundException {
        Account account = accountRepository.findByEmail(emailOrNickname);
        if (account == null) { // 이메일로도 찾아보고 닉네임으로도 찾아보고
            account = accountRepository.findByNickname(emailOrNickname);
        }

        if (account == null) { // 그래도 null 이라면 예외던지기! 해당하는 유저가 없다. 그런 경우에는 이메일 혹은 패스워드가 잘못되었다고 보여주면 된다.
            throw new UsernameNotFoundException(emailOrNickname);
        }
        // 프린시펄에 해당하는 UserAccount
        return new UserAccount(account);
    }

이메일 또는 유저 네임을 사용하기로 함. 여기까지 지나왔으면 해당하는 유저가 있는 것이다. 그러므로 Principal라는 객체를 넘기면 된다. 우리가 만들었던 Principal은 UserAccount.java로 스프링 시큐리티가 제공하는 User를 확장한 UserAccount이다.

이 파일은 @Service 어노테이션을 통해 빈으로 등록이 되어있고 UserDetailsService 타입의 빈이 하나만 있으면 스프링 시큐리티에 굳이 무언가 설정해줄 필요가 없다. 자동으로 저 빈을 가져다가 사용하게 된다. 로그인, 로그아웃 전부다 동작을 해야 한다.

로그인 할때 입력한 값은 password는 plain text 이다. plain text 역시 PasswordEncoder 가 사용된다. 빈으로 등록해 놓았는데 자동으로 스프링 시큐리티 쪽에서 해당하는 것을 가져다가 사용한다. 명시적으로 설정을 해주지 않더라도!

원래는 AuthenticationManger 에다가 UserDetailsService와 passwordEncoder 설정해주지만 스프링 부트가 제공하는 기본 설정을 그대로 따른다면 빈으로만 등록이 되어있으면 된다.

UserDetailsService 도 마찬가지로 빈으로만 등록되어 있으면 자동으로 픽업한다. 하지만 UserDetailsService와 passwordEncoder 가 여러개라면 다른 여러가지 선택방법이 있다.

질문

질문1. DB의 username 컬럼에 "sampleID" 값이 있고, password 컬럼에 "samplePassword" 값의 암호화된 값이 있을 때, POST 요청으로 username=sampleID&password=samplePassword 라고 요청이 들어오면,UserDetailService 구현을 안해도 알아서 로그인처리가 되나요?

-> UserDetailsService는 password encoder가 아니라 sameId라는 유저아이디에 해당하는 유저 데이터를 읽어오는 역할을 하니까 당연히 있어야 합니다. 그 다음 실제 패스워드가 매치하는지는 PasswordEncoder를 사용해서 확인하기 때문에 패스워드를 저장할 때 사용한 그 PasswordEncoder를 스프링 시큐리티 설정에 추가해야 합니다.

질문2. UserDetailService에서 이메일 또는 닉네임만 확인해서 반환하고,
비밀번호 확인은 구현이 안되어있는데요, 비밀번호 확인은 어떻게 이루어지고 있는 건가요? 이미 로그인 인증은 처리 된거고, 그 이후에 이메일 또는 닉네임을 확인해서 authentication token을 만들어서 SecurityContextHolder에 넣어주고 있는건가요?? DB의 컬럼값도 email, nickname으로 username이랑 전혀 관련 없는데, 로그인 POST 요청 시 들어오는 username과 password값으로 DB에 있는 값들과의 비교 및 로그인 인증이 어떻게 되는건지 모르겠습니다.

-> 비밀번호 확인은 PasswordEncoder를 사용해서 스프링 시큐리티가 확인합니다. DB에 들어있는 email이나 nickname을 username으로 취급해서 유저를 읽어오는거구요. 그렇게 인증 처리가 되면 Authentication이라는 객체를 만들어서 SecurityContextHodler에 넣어주는 겁니다.

스프링 시큐리티 강의에서 자세히 설명하지만, 간략히 설명드리자면 로그인 시 username에 해당하는 값으로 UserDetailsService의 구현체를 사용해서 유저 데이터를 읽어옵니다. 그때 username이라는 값을 DB에 들어있는 nickname이나 email로 매치되는지 살펴보고 그에 해당하는 유저 데이터를 읽어오는 겁니다. 그 다음 그렇게 읽어온 유저 데이터에는 패스워드가 인코딩 된 값으로 들어있는데, 그 값을 스프링 시큐리티 설정에 연결해 놓은 PasswordEncoder를 사용해서 체크하는 겁니다.

질문3.
결국 스프링 시큐리티의 로그인 인증은, UserDetailService 구현체에서 DB에서 검색해 username, password 등의 정보를 채워넣어 반환한 "스프링 시큐리티의 User객체"의 username, password 정보만 보고 인증을 진행하는게 맞나요?
-> 네 맞습니다. 그 방법 말고 토큰 기반의 다른 인증 방식도 있지만 가장 일반적인 유저네임/패스워드 인증 방식은 그렇게 동작합니다.


출처 : 인프런 백기선님의 스프링과 JPA 기반 웹 애플리케이션 개발

profile
Step by step goes a long way ✨

0개의 댓글