본문 바로가기

Dot Programming/Spring Clone

[스프링 웹앱 프로젝트 #18] 로그인 로그아웃

18. 로그인 로그아웃

커스텀 로그인 페이지 만들기
 > 로그인 유지 체크
 > id : 이메일/닉네임 , pw 
 > 회원가입 링크

스프링 시큐리티 로그인/로그아웃 설정
 > http.formLogin().loginPage("/login").permitAll();
 > http.logout().logoutSuccessUrl("/");

스프링 시큐리티 로그인 기본값
 > username
 > password
 > POST "/login"

 

Spring security 로그인/ 로그아웃 설정

SecurityConfig.java

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/", "/login","/sign", "/sign-up", "/check-email-token",
                        "/email-login", "/check-email-login", "/login-link").permitAll()
                .mvcMatchers(HttpMethod.GET, "/profile/*").permitAll()
                .anyRequest().authenticated();

        http.formLogin()
                .loginPage("/login").permitAll();

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

 

configure 설정 완료 후 이를 처리할 핸들러를 만들어보자

Maincontroller.java

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

templates/login.html로 이동  (viewController로 코드를 줄일 수 있지만 나중에 적용)

 

 

로그인 뷰 만들기

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace ="fragments.html :: head"></head>
<body class="bg-light">
    <nav th:replace="fragments.html :: main-nav"></nav>

    <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의 기능은 따로 controller설정 안하여도 security에서 제공-->
            <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>

                <!-- submit-->
                <div class="form-group">
                    <button class="btn btn-success btn-block" type="submit"
                            aria-describedby="submitHelp">로그인</button>
                    <small id="submitHelp" class="form-text text-muted">
                        스터디올래에 처음 오신거라면 <a href="#" th:href="@{/signup}">계정을 먼저 만드세요.</a>
                    </small>
                </div>

            </form>

        </div>
    <footer th:replace="fragments.html :: footer"></footer>
    </div>
    <script th:replace="fragments.html :: form-validation"></script>
</body>
</html>

 

form login을 처리하는 post 메소드는 Spring Security가 지원해준다 ( ID, PW값을 받고 제출 -> user ? yes : no)

 

로그인이 완료되기 위해서는 id / pw을 입력 후 로그인 버튼을 눌렀을 때 DB에 있는 user data를 조회하여 매칭이 되는지 확인해야 하는데 그러려면 security에 있는 UserDetailsService를 사용하여야 한다.

@Service
@RequiredArgsConstructor
public class AccountService implements UserDetailsService {

/**
     * DB에 있는 user정보 조회
     */
    @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);
        }

        //User Principal 반환
        return new UserAccount(account);
    }
 ]

 

 

결과화면

  • 로그아웃하면 "/" 기본화면으로 이동

 

1. 로그인 뷰

 

2. 잘못 입력시 에러메시지 노출

 

 

3. 로그인 성공 화면 -> "/index"로 이동 

 

 


+ 로그인할 때 입력한 현재 비밀번호 값은 plain text로 들어온다

  @Bean
    public PasswordEncoder passwordEncoder(){
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

 

원래는 AuthenticationManager에다 userDetails와 PasswordEncoder를 아래와 같이 설정해주어야 한다.

    @Override
    public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception{
        authenticationManagerBuilder
                .userDetailsService(userService)
                .passwordEncoder(passwordEncoder());
    }

 

그런데 Springboot기본 설정을 따른다면 PasswordEncoder나 UserDetailsService는 Bean으로 등록이 되어있으면 spring에서 알아서 픽업해가니 따로 명시적으로 설정은 하지 않아도 된다.

 (만약 PasswordEncoder나 UserDetailsService가 여러 개다? 그러면 다른 방법으로 풀어나가야 한다.)

 

 > UserDetailsService가 여러개면 위와 같이 해당 service와 passwordencode를 같이 authenticationManager에 넣어주는 방식으로 하는 것 같다
(뇌피셜)

 

 


참고

인프런 강의 - 스프링과 JPA 기반 웹 애플리케이션 개발