본문 바로가기

학습 기록/BE (Spring Boot, JPA, JSP, ...)

240125 [Back-end] Spring Security 스프링 시큐리티 -2 (로그인, 회원가입, role ) / JPA / Entity

반응형

[24.01.25]  115차

 

<<진도>>

[Back-end] Spring Security 스프링 시큐리티 -2

(로그인, 회원가입, role ) / JPA / Entity

 

지난시간에 이어 index의 header를 th:replace하여

 

index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>index.html</title>
</head>
<body>
	<div th:replace="~{fragment/header :: header}"></div>

    <h2>index.html</h2>
	<h2>[[${message}]]</h2>
    <h2 th:text="${message}"></h2>
</body>
</html>

 

header.html

<html xmlns:th="http://www.thymeleaf.org"
	xmlns:sec="http://www.thymeleaf.org">
	<div id="header" th:fragment="header" style="background-color: #eee;">
		<div>
			<span><a href="/">index</a></span>
			<span><a href="/info">info</a></span>	
			<span><a href="/dashboard">dashboard</a></span>
			<span><a href="/admin">admin</a></span>
		</div>
		<div>
			<!-- 인증되지 않은(로그인하지 않은) 사용자 -->
			<span><a sec:authorize="isAnonymous()" th:href="@{/login}">로그인</a></span>
		</div>
		<div>			
			<!-- 인증된 사용자  -->
			<span><a sec:authorize="isAuthenticated()" th:href="@{/logout}">로그아웃</a></span>
		</div>
		
		<div>			
			<!-- 인증 시 사용된 객체에 대한 정보  -->
			<span>인증에 사용된 객체 정보 : </span>
			<span sec:authorize="isAuthenticated()" sec:authentication="principal"></span>
		</div>
		
		<div>
			<!-- 인증시 사용된 객체의 username -->
			<span>ㅇ username : </span>
			<span sec:authorize="isAuthenticated()" sec:authentication="name"></span>
			<span> / </span>
			<span sec:authorize="isAuthenticated()" sec:authentication="principal.username"></span>
		</div>
		<div>
			<!-- 인증시 사용된 객체의 권한 -->
			<span>ㅇ role : </span>
			<span sec:authorize="isAuthenticated()" sec:authentication="authorities"></span>
			<span sec:authorize="isAuthenticated()" sec:authentication="principal.authorities"></span>
		</div>
		<div>
			<!-- ROLE_ADMIN 권한 있으면 화면에 표시(출력) -->
			<span sec:authorize="hasRole('ADMIN')"> 관리자 </span>
			<!-- ROLE_USER 권한 있으면 화면에 표시(출력) -->
			<span sec:authorize="hasRole('USER')"> 사용자 </span>
			<span> / </span>
			<!-- ROLE_USER or ROLE_ADMIN 권한 있으면 화면에 표시(출력) -->
			<span sec:authorize="hasAnyRole('ADMIN', 'USER')"> 관리자 또는 사용자 </span>
		</div>
	 
	</div>
</html>

 

 

Configuration 파일에서 사용자 만드는 코드.

//사용자 만들기
@Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
	auth.inMemoryAuthentication()
		.withUser("test").password("{noop}1111").roles("USER") // 비밀번호를 암호화하지 않으면 보안에 취약 스프링 시큐리티는 {암호화기법}사용
		.and()
		.withUser("admin1").password("{noop}1111").roles("ADMIN");
}

 

 

test(USER) 1111로 로그인한 결과 

 

 

 

 

프로젝트 JPA 연결 후 DB 테이블 생성

 

mysql 테이블과 대응되는 java의 개념 Entity

 

Entity는 보통 model 패키지에 생성한다.

 

 

Entity는 RDBMS와 java 와 쓰지만

엔티티를 다른계층에서 dto처럼 사용하는것은 좋지 않다

엔티티에는 세터를 추가하는것을 권장하지않는다 (빌더 사용)

 

Member.java(Entity)

@Entity // 엔티티 어노테이션을 넣는 순간 따로 관리가 됨
@Getter
@Setter // Entity에서는 setter사용 비권장 / 보통 빌더를 사용
@NoArgsConstructor(access = AccessLevel.PRIVATE) 
// JPA에서 테이블과 1:1 맵핑하여 사용하는 용도 (DTO와 다름)
// 1:1대응이므로 이름이 같으면 자동 매칭
public class Member {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY) // 기본키, auto-increment
	private Long id;
	
	@Column(name = "username", unique = true) // 실제 테이블 필드명, 유니크 설정
	private String username;
	
	@Column(name = "password")
	private String password;
	
	@Column(name = "role")
	private String role;
	
}

 

 

MemberRepository.java (interface) JpaRepository 상속

// @Repository 없이도 상속받아 자동으로 인식 <Entity class, 기본키의 Type>
public interface MemberRepository extends JpaRepository<Member, Long>{

	/*
	 * - 인터페이스 생성 후, JpaRepository<Entity 클래스, PK 타입>을 상속하면,
	 * 기본적인 CRUD 메서드가 자동으로 생성
	 * - @Repository 설정 필요 없음
	 * - but 조회는 없어서 만들어줘야함
	 */
	Member findByUsername(String usernaString);
	
}

 

인터페이스 상속 후 서비스 생성 비즈니스 로직용

 

MemberSevice.java  UserDetailsServic 인터페이스 상속

@Service
@RequiredArgsConstructor
public class MemberService implements UserDetailsService{ 
	// UserDetailsService는 기본적으로 DAO(like.Entity)를 가지고 있어 DB에 관계없이 다룰 수 있다(username을 기반으로 동작/ readonly)

	// final이 아니라 변할수있는 위험이있다
	// @Autowired 
	// private MemberRepository memberRepository;
	
	// 변화가 없어 좀 더 안정적인 방식
	private final MemberRepository memberRepository;

	// UserDetailsService 저장이 된 사용자 정보를 가져오는 로직 (UserDetailsService의 오버라이딩 메서드)
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 
		
		
		Member member = memberRepository.findByUsername(username);
		
		// 사용자가 없으면
		if(member == null) {
			throw new UsernameNotFoundException(username);
		}
		// 사용자가 있다면
		// Builer Pattern (생성 패턴)
		// 이미 User 라는 객체(UserDetails를 상속받은 클래스:다형성 가능 반환형-return)가 시큐리티에 있어 사용가능
		return User
				.builder() // new User() 같은 객체생성을 대신함.(생성자의 인수순서 규칙없어 편리, 
						   // 안정성 높음, 어떤필드에 어떤 값이 들어가는 지 명시가 가능)
				.username(member.getUsername())
				.password(member.getPassword())
				.roles(member.getRole())
				.build();
				
			//사용자 정보를 리턴
			
		}

 

UserBuilder 내부

 


회원가입 기능

 

(**회원가입같은 기능별로 하나의 Domain( Member, Item, Order 등등)으로 묶어 여러개의 컨트롤러를 관리해야한다.)

 

회원가입, 성공페이지 url인  /join, /join/USER는 기존 config의 필터체인에 url 지정이 안돼서 검증을 하므로(로그인화면 넘어감) 넣어줘야 한다.

/join 이후 모든 url

 

 

 

 

 

dto나 다른 데이터필드를 Entity로 바꿔주는 라이브러리  modelmapper

https://modelmapper.org/

 

ModelMapper - Simple, Intelligent, Object Mapping.

Why ModelMapper? The goal of ModelMapper is to make object mapping easy, by automatically determining how one object model maps to another, based on conventions, in the same way that a human would - while providing a simple, refactoring-safe API for handli

modelmapper.org

 

 

 

그래들 의존성 복사해서 사용 build.gradle에 붙여넣기

https://mvnrepository.com/artifact/org.modelmapper/modelmapper/3.2.0

ModelMapper Bean 생성

 

 

MemberController.java

ModelMapper를 활용하여 DTO  to Entity / save 후 / Entity를  DTO로 다시 불러오는 로직

: 회원가입을 하면 DB에 저장되고  DB에서 데이터를 다시 불러온다

 

이때! DTO로 입력받은 비밀 번호를 저장할때 암호화

 

암호화를 거친 비밀번호는 조회되지 않음.

(**cf 평문으로 저장한 비밀번호는 spring secutiry에서 원칙적으로 로그인 불가 처리시킴)

 

MemberService.java

	// 비밀번호 암호화 Encoding
	public Member joinMember(Member member) {
		// member.encodePassword(); 
		member.encodePassword(passwordEncoder); 
		// 우리가 만들고(PasswordEncoderCOnfig.java) 의존성 주입해둔 pwEncoder를 넣었다. 
		return memberRepository.save(member);
	}

 

PasswordEncoder는 PasswordEncoderConfig.java에 정의 후 Service에서 DI후사용한 구조

 

암호화하는 PasswordEncoder의 경우 다양한 내부 암호화 기법들이 있음

createDelegatingPasswordEncoder()

deprecate 된것들이 많다

 

 

회원가입 후 해당 정보가 뜨는 VIEW를 구성하고 회원가입을 진행하면

회원가입 결과

{bcrypt} 는 암호화 기법

 

 

 

 

로그인 시 자동실행이 되는 아이디(username)조회 쿼리 sql문 확인하는 법

(해당 id(username), password가 DB 테이블에 있는지 )

 

*application.properties 파일에서 지정

: m1_0은 member 테이블의 alias 명

 

 

 

바인딩되는 paramerter값 출력

 

1은 첫번째 ? (쿼리파라미터)

 

위의 쿼리문 정렬 출력

반응형