스프링과 JPA 기반 웹 애플리케이션 개발 #23 로그인 로그아웃
해당 내용은 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발의 강의 내용을 바탕으로 작성된 내용입니다.
강의를 학습하며 요약한 내용을 출처를 표기하고 블로깅 또는 문서로 공개하는 것을 허용합니다 라는 원칙 하에 요약 내용을 공개합니다. 출처는 위에 언급되어있듯, 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발입니다.
제가 학습한 소스코드는 https://github.com/n00nietzsche/jakestudy_webapp 에 지속적으로 업로드 됩니다. 매 커밋 메세지에 강의의 어디 부분까지 진행됐는지 기록해놓겠습니다.
http.formLogin()
.loginPage("/login").permitAll();
http.logout()
.logoutSuccessUrl("/");
username
password
POST /login
POST /logout
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<title>로그인</title>
<th:block th:replace="fragments :: headLibraryInjection"></th:block>
</head>
<body class="bg-light">
<th:block th:replace="fragments :: main-nav"></th:block>
<div class="container">
<div class="py-5 text-center">
<p class="lead">스터디올래</p>
<h2>로그인</h2>
</div>
<div class="row justify-content-center">
<div th:if="${param.error}" class="alert alert-danger" role="alert">
<p>이메일(또는 닉네임)과 패스워드가 정확하지 않습니다.</p>
<p>또는 확인되지 않은 이메일을 사용했습니다. 이메일을 확인해주세요.</p>
<p>
확인 후 다시 입력하거나, <a href="#" th:href="@{/find-password}">패스워드 찾기</a>를 이용하세요
</p>
</div>
<!-- POST /login 으로 가는 값은 스프링 시큐리티가 알아서 처리해주기 때문에, 우리가 따로 로그인 핸들러 로직을 만들 필요가 없다. -->
<!-- 단 스프링 시큐리티가 사용자의 정보를 디비에서 끌고올 수 있어야 하기 때문에, UserDetailsService 인터페이스는 구현해야 한다. -->
<form class="needs-validation col-sm-6" action="#" th:action="@{/login}" method="post" novalidate>
<div class="form-group">
<label for="username">이메일 또는 닉네임</label>
<input id="username" type="text" name="username" class="form-control"
placeholder="your@email.com" aria-describedby="emailHelp" required />
<small id="emailHelp" class="form-text text-muted">
가입할 때 사용한 이메일 또는 닉네임을 입력하세요.
</small>
<small class="invalid-feedback">이메일을 입력하세요.</small>
</div>
<div class="form-group">
<label for="password">패스워드</label>
<input id="password" type="password" name="password" class="form-control"
aria-describedby="passwordHelp" required />
<small id="passwordHelp" class="form-text text-muted">
패스워드가 기억나지 않는다면, <a href="#" th:href="@{/email-login}">패스워드 없이 로그인하기</a>
</small>
<small class="invalid-feedback">패스워드를 입력하세요.</small>
</div>
<div class="form-group">
<button class="btn btn-success btn-block" type="submit"
aria-describedby="submitHelp">로그인</button>
<small class="form-text text-muted">스터디올래에 처음 오셨다면,
<a href="#" th:href="@{/sign-up}">계정을 먼저 만드세요.</a>
</small>
</div>
</form>
</div>
<th:block th:replace="fragments :: footer"></th:block>
</div>
<script th:replace="fragments :: form-validation"></script>
</body>
</html>
id
와 password
에 대한 input
의 name
값은 스프링 시큐리티를 활용하려면 username
과 password
로 설정하는 것이 좋다.username
과 password
파라미터를 POST /login
으로 보내면, 스프링 시큐리티가 UserDetailsService
인터페이스를 구현한 클래스를 이용하여 로그인 처리를 자동으로 해준다.POST /logout
요청을 하면 스프링 시큐리티에 작성된 로그아웃 핸들러가 자동으로 로그아웃된다. @GetMapping("/login")
public String login() {
return "login";
}
메인 컨트롤러단에서는 따로 뭘 해줄 필요 없이 그냥 로그인 뷰만 잘 넘겨주면 된다.
http.formLogin() // 이것만 해도 폼 로그인 기능은 활성화됨
.loginPage("/login").permitAll(); // 이렇게 하면 커스텀한 로그인 페이지를 설정할 수 있음
http.logout() // 이것만 해도 로그아웃 기능은 됨
.logoutSuccessUrl("/"); // 로그아웃 했을 때 어느 페이지로 갈지에 대한 설정임
위와 같이 설정하면, 커스텀 로그인 페이지인 /login
으로 페이지 연결이 가능하고, 로그아웃 시에 방문할 페이지도 설정할 수 있다.
http.formLogin() // 이것만 해도 폼 로그인 기능은 활성화됨
.usernameParameter("email")
.passwordParameter("pass")
.loginPage("/login").permitAll(); // 이렇게 하면 커스텀한 로그인 페이지를 설정할 수 있음
위와 같이 파라미터를 커스터마이징할 수도 있다.
public class AccountService implements UserDetailsService {
...
@Override
public UserDetails loadUserByUsername(String emailOrNickname) throws UsernameNotFoundException {
Account account = accountRepository.findByEmail(emailOrNickname);
if(account == null) {
account = accountRepository.findByNickname(emailOrNickname);
}
if(account == null) {
throw new UsernameNotFoundException(emailOrNickname);
}
// Principal 을 만들어주면 된다.
// UserDetailsService 를 구현한 객체가 하나만 있으면, SpringSecurity 에 더이상 설정해줄 것이 없다.
// 자동으로 UserDetailsService 를 상속한 빈을 가져다 쓰게 된다.
return new UserAccount(account);
}
위와 같이 UserDetailsService
인터페이스를 상속받고 loadUserByUsername
메소드를 작성해주면 된다.
여기서는 받은 문자열을 기준으로 accountRepository
에서 해당 계정을 이메일 혹은 닉네임으로 찾는다.
그리고 마지막 반환 값은 SpringSecurityContextHolder.getContext().getAuthentication()
의 principal
이 된다.