목차

커스텀 로그인 구현

CustomUserDetails 구현

CustomUserDetailsService 구현

로그인 사용자 정보 조회 방법

프로젝트 구조

Gihub 링크


CustomUserDetails 구현

UserDetails 설명

해당 인터페이스는 사용자 정보를 담는 기본적인 메서드들을 정의하고 있고, 주요 메서드로는 사용자의 이름, 패스워드, 권한 정보를 반환합니다. 커스텀해서 사용하는 이유는 기능이 제한적이고, 추가적인 회원 정보를 가져오기 위함이다.


CustomUserDetails.java

package com.example.login.dto;
 
import java.util.ArrayList;
import java.util.Collection;
 
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
 
import com.example.login.entity.Members;
 
import lombok.Getter;
import lombok.RequiredArgsConstructor;
 
@RequiredArgsConstructor
@Getter
public class CustomUserDetails implements UserDetails {
    
    private final Members members;
    
    // 권한 반환
    @Override
    public Collection getAuthorities() {
 
        Collection collection = new ArrayList<>();
 
        collection.add(new GrantedAuthority() {
 
            @Override
            public String getAuthority() {
                return members.getRole();
            }
        });
 
        return collection;
    }
    
    // 비밀번호 반환
    @Override
    public String getPassword() {
        //return members.getPassword();
        return members.getLoginPwd();
    }
 
    // 아이디 반환
    @Override
    public String getUsername() {
        //return members.getUsername();
        return members.getLoginId();
    }
 
    // 계정 만료 여부 반환
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
 
    // 계정 잠금 여부 반환
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
 
    // 패스워드 만료 여부 반환
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
 
    // 계정 사용 가능 여부 반환
    @Override
    public boolean isEnabled() {
        return true;
    }
 
}


CustomUserDetailsService 구현

UserDetailsService 설명

데이터베이스에서 사용자를 조회하고, 조회된 정보를 기반으로 UserDetails 객체를 생성하여 반환합니다. 반환된 UserDetails 객체는 Spring Security에서 인증 과정에서 사용됩니다.


CustomUserDetailsService.java

package com.example.login.service;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.example.login.dto.CustomUserDetails;
import com.example.login.entity.Members;
import com.example.login.repository.MembersRepository;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final MembersRepository membersRepository;
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 회원 정보 조회
        Members memberInfo = membersRepository.findByLoginId(username);

        if (memberInfo != null) {
            return new CustomUserDetails(memberInfo);
        }

        return null;
    }

}


Controller 추가

LoginController.java

package com.example.login.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginController {

    /**
     * 로그인
     * @return
     */
    @GetMapping("/login")
    public String login() {
        return "login";
    }

}


프론트 페이지 추가

login.mustache

<!DOCTYPE html>
<html lang="ko"> 
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>로그인</title>
        <style>
            .container {
                width: 800px !important ;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <form name="loginForm" method="post" action="/loginProc">
                <div class="row">
                    <div class="col-md-12 pt-5">
                        <div class="card mb-4">
                            <h5 class="card-header text-center">로그인</h5>
                            <div class="card-body">
                                <div class="mb-3 row">
                                    <label for="html5-text-input" class="col-md-2 col-form-label">아이디</label>
                                    <div class="col-md-10">
                                        <input class="form-control" type="text" id="loginId" name="loginId" />
                                    </div>
                                </div>
                                <div class="mb-3 row">
                                    <label for="html5-search-input" class="col-md-2 col-form-label">비밀번호</label>
                                    <div class="col-md-10">
                                        <input class="form-control" id="loginPwd" name="loginPwd" type="password" />
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="row">
                    <div class="col-md-12 text-center">
                        <button type="submit" class="btn btn-outline-danger">로그인</button>
                        <a href="/" class="btn btn-outline-secondary">취소</a>
                    </div>
                </div>
            </form>
        </div>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
    </body>
</html>


로그인 사용자 정보 조회 방법

Controller 수정

MainController.java

package com.example.login.controller;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import com.example.login.dto.CustomUserDetails;

@Controller
public class MainController {

    /**
     * 메인
     * @param customUserDetails
     * @param model
     * @return
     */
    @GetMapping("/")
    public String index(@AuthenticationPrincipal CustomUserDetails customUserDetails, Model model) {
        
        // 로그인 여부 체크
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        
        if (!(principal instanceof String)) {
            // 로그인 아이디
            String id = SecurityContextHolder.getContext().getAuthentication().getName();
            System.out.println("========================");
            System.out.println("아이디 : " + id);
            System.out.println("========================");
            
            // Membars 정보
            System.out.println("========================");
            System.out.println("아이디 : " + customUserDetails.getMembers().getLoginId());
            System.out.println("닉네임 : " + customUserDetails.getMembers().getNickname());
            System.out.println("권한 : " + customUserDetails.getMembers().getRole());
            System.out.println("========================");
            
            model.addAttribute("isLogin", true);
            model.addAttribute("id", customUserDetails.getMembers().getLoginId());
            model.addAttribute("nickname", customUserDetails.getMembers().getNickname());
        }
        else {
            model.addAttribute("isLogin", false);
        }
        
        return "index";
    }

}


프론트 페이지 수정

index.mustache

<!DOCTYPE html>
<html lang="ko"> 
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>메인 페이지</title>
        <style>
            .container {
                width: 800px !important ;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <div class="row">
                <div class="col-md-12 pt-5">
                    <div class="card mb-4">
                        <h5 class="card-header text-center">메인 페이지</h5>
                        <div class="card-body">
                            {{#isLogin}}
                            <h5 class="card-title">{{nickname}}님 환영합니다.</h5>
                            <p class="card-text">접속하신 아이디는 {{id}} 입니다.</p>
                            {{/isLogin}}
                            {{^isLogin}}
                            <div class="col-md-12 text-center">
                                <a href="/join" class="btn btn-outline-primary">회원가입</a>
                                <a href="/login" class="btn btn-outline-success">로그인</a>
                            </div>
                            {{/isLogin}}
                        </div>
                    </div>
                </div>
            </div>
            {{#isLogin}}
            <div class="row">
                <div class="col-md-12 text-center">
                    <a href="/logout" class="btn btn-outline-danger">로그아웃</a>
                </div>
            </div>
            {{/isLogin}}
        </div>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
    </body>
</html>


프로젝트 구조


Gihub 링크

전체 소스 다운로드

©2024, DevDream