로그인 검증(인증)
[ 시큐리티를 통해 인증을 진행하는 방법 ]
Login 페이지(loginform.html)를 통해 아이디, 비밀번호를 POST 요청 시, 스프링 시큐리티 인증 절차를 거쳐 로그인 진행
- DB(데이터베이스)에 저장된 회원 정보를 조회하여
- 비밀번호를 검증하고
- 서버 세션 저장소에 해당 아이디에 대한 session을 저장
이때, 로그인 회원 정보를 검증하기 위해서는 UserDetailService , UserDetails 클래스 구현 필요
구조도
- 로그인 회원 정보를 검증하기 위해서는 UserDetailService , UserDetails 클래스 구현 필요
- 해당 클래스는 인터페이스이므로, 이를 상속받아 구현하는 클래스 필요
- DB(데이터베이스)의 회원 정보를 가져와, 로그인된 정보를 비교하여 검증 가능
UserDetailsService
1. 의미
- Spring Security에서 유저의 정보를 가져오는 인터페이스
- 사용자가 로그인을 할 때 기본 회원인지 인증 하고, 인증이 완료되면 로그인한 상태의 사용자의 세션을 유지
시켜주기 위한 인터페이스
- 인증에 성공할 시
▷ 해당 회원에 대한 session에 대한 정보가 생성되고
▷ Authentication 객체나 @AuthenticationPrincipal 애노테이션을 통해서 session에 들어있는 사용자 정보를 사용할 수 있음
▷ 즉, 이 메서드가 정상적으로 완료되어 UserDetails 정보가 반환되면 인증에 성공한 것
- 기본 오버라이드 메서드
▷DB에서 유저 정보를 불러오는 중요한 메서드로, 로그인한 회원이 기본 회원인지 아닌지 인증하는 메서드
2. 코드 구현하기
- UserDetailsService 인터페이스 상속받는 CustomUserDetailsService 클래스를 생성
- security config 설정( http.formLogin() )을 한다면, 로그인 시(loginform.html)에
- Spring Security에서 username 정보를 넘겨줄 것이고
- loadUserByUsername 메서드에서 username을 매개변수로 받아서, DB에 저장되어 있는 유저 정보를 가져옴
package com.example.securitytest.service;
import com.example.securitytest.dto.CustomUserDetails;
import com.example.securitytest.entity.UserEntity;
import com.example.securitytest.repository.UserRepository;
import lombok.RequiredArgsConstructor;
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;
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
// DB에 username이 존재하는지 검증하기 위해서, UserRepository와 연결
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//사용자 로그인 시, spring secutiry에서 유저정보를 가져와 username을 매개변수로 넣어줌
UserEntity entity = userRepository.findByUsername(username);
if( entity == null ) {
throw new UsernameNotFoundException(username);
}
// entity가 존재(DB에 데이터 있음), CustomUserDetails에 회원 데이터 전달
return new CustomUserDetails(entity);
}
}
UserRepository
package com.example.securitytest.repository;
import com.example.securitytest.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Integer> {
UserEntity findByUserName(String username);
}
UserEntity
package com.example.securitytest.entity;
import jakarta.persistence.*;
import lombok.Data;
@Entity // table과 연결 - Entity를 기반으로 테이블을 만들어주는 hibernate 설정
@Data
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // id가 자동으로 생성
private int id;
@Column(unique = true) // username이 중복되어 저장되지 않도록 설정
private String username;
private String password;
private String role; // 권한 저장: 일반인(USER), 관리자(ADMIN)
}
UserDetails
1. 의미
- Spring Security가 사용자 정보를 알 수 있도록 사용자에 대한 정보를 담는 인터페이스
- 이 인터페이스를 구현하면, Spring Security에서 구현한 클래스를 사용자 정보로 인식하고 인증 작업 진행
- 즉, UserDetails 인터페이스는 VO(DTO) 역할을 함
- 인터페이스 구현 시, 기본 오버라이드 메서드
2. 코드 구현하기
- UserDetails 인터페이스를 상속받는 CustomUserDetails 클래스 생성
- Spring Security의 기본 UserDetails로는 필요한 정보를 모두 담을 수 없기 때문에 CustomUserDetails를 구현하여 사용
- userDetailService로부터 전달받은 데이터( return new CustomUserDetails(entity) )
package com.example.securitytest.dto;
import com.example.securitytest.entity.UserEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
@RequiredArgsConstructor
public class CustomUserDetails implements UserDetails { //dto와 같은 역할을 함
private final UserEntity userEntity;
@Override // 사용자의 특정 권한: Role값에 대해 반환
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collection = new ArrayList<>();
collection.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return userEntity.getRole();
}
});
return collection;
}
@Override
public String getPassword() {
return userEntity.getPassword();
}
@Override
public String getUsername() {
return userEntity.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true; // 만료되지 않음
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
MainController
SecurityContextHolder에서 UserDetails(사용자 정보) 불러오기
package com.example.securitytest.controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
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 org.springframework.web.bind.annotation.PostMapping;
import java.util.Collection;
import java.util.Iterator;
@Controller
public class MainController {
@GetMapping("/")
public String main(Model m) {
//SecurityContextHolder에서 UserDetails 불러오기
// session에 담겨있는 로그인한 사용자의 특정한 이름 값
String id = SecurityContextHolder.getContext().getAuthentication().getName();
// session에 담겨있는 로그인한 사용자의 특정한 role 값
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Iterator<? extends GrantedAuthority> iter = authorities.iterator();
GrantedAuthority auth = iter.next();
String role = auth.getAuthority();
// view에서 보여줄 데이터 저장
m.addAttribute("id", id);
m.addAttribute("role", role);
return "main";
}
}
관리자 권한으로 관리자 페이지 접근하는 경우
- 관리자로 회원가입을 하고, 관리자 권한으로 /admin경로에 접근
1. 해당 역할(role) 값을 관리자(admin)으로 회원가입을 진행하고
2. 관리자 권한으로 로그인 3. 관리자 홈페이지에 접근(/admin) |
관리자 권한이 없는 자가 관리자 페이지 접근하는 경우
1. 일반 사용자가 로그인 하고
2. 관리자 페이지 접근한 경우 |
출처
https://programmer93.tistory.com/68
'SPRINGBOOT' 카테고리의 다른 글
[spring security] 6. 회원가입 로직 (0) | 2024.01.10 |
---|---|
[spring security] 5. DB 연결하기 (0) | 2024.01.10 |
[spring security] 3. springconfig 설정 : BCrype 암호화 메서드 (0) | 2024.01.10 |
[spring security] 2. 인증과 인가, securityConfig 설정 (1) | 2024.01.10 |
[SpringBoot] DAO와 Mapper 인터페이스의 차이? (0) | 2024.01.10 |