국비학원_72일차(Spring boot Security)

써니·2022년 11월 3일
0

spring

목록 보기
14/23

🚫JPA Entity @Setter 지양하는 이유

참고

1. @Setter는 사용 의도/목적이 분명치 않다.(Update인지 create인지)

2. 무분별한 변경으로 객체의 일관성을 보장하기 어렵다. - @Builder 패턴 적용 권장




👮‍♀️ Security

🔻oBootSecurity01

❌[MAVEN]

로그인




📂src/main/resources

  • application.yml
server:
  port : 8386
  
# Oracle Connect
spring:
  datasource:
    url: jdbc:oracle:thin:@localhost:1521/xe
    username: scottjpa
    password: tiger
    driver-class-name: oracle.jdbc.driver.OracleDriver
    
  # Jpa Setting
  jpa:
    hibernate: 
      ddl-auto: update # none create update
    properties: 
      hibernate:
      show_sql: true      # System.out에 하이버네이트 실행 SQL
      format_sql: true

logging.level:
  org.hibernate.SQL: debug   # logger를 통해 하이버네이트 실행 SQL

📂static

  • index.html

oBootJpa03에서 복사


📂static

  • user.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>User 인증 성공</h1>
</body>
</html>
  • manager.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>Manager 인증 성공</h1>
</body>
</html>
  • admin.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>Admin 인증 성공</h1>
</body>
</html>

[com.oracle.oBootSecurity01.controller]

  • SecurityController01.java
package com.oracle.oBootSecurity01.controller;

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

import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
public class SecurityController01 {
	
	@GetMapping("/user")
	public String user() {
		log.info("SecurityController01 user Start...");
		return "user";
	}
	
	@GetMapping("/manager")
	public String manager() {
		log.info("SecurityController01 manager Start...");
		return "manager";
	}
	
	@GetMapping("/admin")
	public String admin() {
		log.info("SecurityController01 admin Start...");
		return "admin";
	}
}




❌암호화

[com.oracle.oBootSecurity01.configuration]

  • SecurityConfig.java

permitAll 요청이 들어오면 다 permit

package com.oracle.oBootSecurity01.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration //IoC 빈(bean)을 등록
@EnableWebSecurity // 필터 체인 관리 시작 어노테이션
public class SecurityConfig { // 환경작업
	
	// 암호화 빈 설정
	@Bean
	public BCryptPasswordEncoder encodePwd() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http.csrf().disable();
		http.authorizeRequests()
			.anyRequest()
			.permitAll(); // 요청이 들어오면 다 permit
		return http.build();
	}
}


⭕암호화

  • index.html 수정

[com.oracle.oBootSecurity01.controller]

  • SecurityController01.java
package com.oracle.oBootSecurity01.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
public class SecurityController01 {
	
	@Autowired
	private BCryptPasswordEncoder bCryptPasswordEncoder;
	
	@GetMapping("/user")
	public String user(String passwd) {
		log.info("SecurityController01 user Start...");
		log.info("SecurityController01 user passwd->"+passwd);
		//										 Hash 암호화
		String encPasswd = bCryptPasswordEncoder.encode(passwd); // encode 타입이 char(String)
		log.info("SecurityController01 user encPasswd->"+encPasswd);
		return "user";
	}
	
	@GetMapping("/manager")
	public String manager() {
		log.info("SecurityController01 manager Start...");
		return "manager";
	}
	
	@GetMapping("/admin")
	public String admin() {
		log.info("SecurityController01 admin Start...");
		return "admin";
	}
}




🔻oBootSecurity02

⭕[Gradle]

🔑권한별 암호화

entity, domain -> JPA
dto -> MyBatis


시큐리티 설정


📂src/main/resources

  • application.yml
server:
  port : 8387
  
# Oracle Connect
spring:
  datasource:
    url: jdbc:oracle:thin:@localhost:1521/xe
    username: scottjpa
    password: tiger
    driver-class-name: oracle.jdbc.driver.OracleDriver
    
  # Jpa Setting
  jpa:
    hibernate: 
      ddl-auto: update # none create update
    properties: 
      hibernate:
      show_sql: true      # System.out에 하이버네이트 실행 SQL
      format_sql: true

logging.level:
  org.hibernate.SQL: debug   # logger를 통해 하이버네이트 실행 SQL

📂static

  • index.html

[com.oracle.oBootSecurity02.entity]

  • User.java
package com.oracle.oBootSecurity02.entity;

import java.sql.Timestamp;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import org.hibernate.annotations.CreationTimestamp;

import lombok.Data;

@Entity
@Data
@SequenceGenerator(
					name = "user_seq_gen",
					sequenceName = "user_seq_generator",
					initialValue = 1,
					allocationSize = 1
				  )
@Table(name = "user01")
public class User {
	@Id	// primary Key
	@GeneratedValue(
					strategy = GenerationType.SEQUENCE,
					generator = "user_seq_gen"
				   )
	private int id;
	private String username;
	private String password;
	private String email;
	private String role; // ROLE_USER, ROLE_ADMIN, ROLE_MANAGER
	@CreationTimestamp
	private Timestamp createDate;
}

[com.oracle.oBootSecurity02.repository]

  • SecurityRepository.java( interface )
package com.oracle.oBootSecurity02.repository;

import com.oracle.oBootSecurity02.entity.User;

public interface SecurityRepository {
	void save(User user);
	User findByUsername(String username);
}

[com.oracle.oBootSecurity02.repository]

  • JpaSecurityRepository.java( + interface )
package com.oracle.oBootSecurity02.repository;

import javax.persistence.EntityManager;

import org.springframework.stereotype.Repository;

import com.oracle.oBootSecurity02.entity.User;

import lombok.RequiredArgsConstructor;

@Repository
@RequiredArgsConstructor
public class JpaSecurityRepository implements SecurityRepository {
	
	private final EntityManager em;
	
	@Override
	public void save(User user) {
		em.persist(user);
	}
	
	// unique라 가정 -> username
	@Override
	public User findByUsername(String username) {
		User user = em.createQuery("select u from User u where username = :username", User.class)
					  .setParameter("username", username)
					  .getSingleResult();
		return user;
	}

}

[com.oracle.oBootSecurity02.service]

  • SecurityService.java( interface )
package com.oracle.oBootSecurity02.service;

import com.oracle.oBootSecurity02.entity.User;

public interface SecurityService {
	User findByUsername(String username);
	void save(User user);
}

[com.oracle.oBootSecurity02.service]

  • SecurityServiceImpl.java( +interface )
package com.oracle.oBootSecurity02.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.oracle.oBootSecurity02.entity.User;
import com.oracle.oBootSecurity02.repository.JpaSecurityRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@Transactional
public class SecurityServiceImpl implements SecurityService {
	
	private final JpaSecurityRepository jpaSecurityRepository;
	
	@Override
	public User findByUsername(String username) {
		User user = jpaSecurityRepository.findByUsername(username);
		return user;
	}

	@Override
	public void save(User user) {
		System.out.println("SecurityServiceImpl save Start...");
		jpaSecurityRepository.save(user);
		return;
	}

}

[com.oracle.oBootSecurity02.controller]

  • SecurityController02.java
package com.oracle.oBootSecurity02.controller;

import java.util.Iterator;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.oracle.oBootSecurity02.configuration.auth.PrincipalDetails;
import com.oracle.oBootSecurity02.entity.User;
import com.oracle.oBootSecurity02.service.SecurityServiceImpl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
@RequiredArgsConstructor
public class SecurityController02 {
	
	private final SecurityServiceImpl securityServiceImpl;
	private final BCryptPasswordEncoder bCryptPasswordEncoder;
	
	@GetMapping("/user")
	public String user(@AuthenticationPrincipal PrincipalDetails principal) {
		log.info("SecurityController02 user Start...");
		log.info("SecurityController02 user principal->{}",principal);
		Iterator<? extends GrantedAuthority> iter = principal.getAuthorities().iterator();
		while (iter.hasNext()) {
			GrantedAuthority authority = iter.next();
			log.info("authority.getAuthority()->{}",authority.getAuthority());
			
		}
		return "user";
	}
	
	@GetMapping("/user/2")
	public String user2(String passwd) {
		log.info("SecurityController02 user2 Start...");
		return "user";
	}
	
	@GetMapping("/manager")
	public String manager() {
		log.info("SecurityController02 manager Start...");
		return "manager";
	}
	
	@GetMapping("/manager/2")
	public String manager2() {
		log.info("SecurityController02 manager2 Start...");
		return "manager";
	}
	
	@GetMapping("/admin")
	public String admin() {
		log.info("SecurityController02 admin Start...");
		return "admin";
	}
	
	@GetMapping("/admin/2")
	public String admin2() {
		log.info("SecurityController02 admin2 Start...");
		return "admin";
	}
	
	@GetMapping("/loginFail")
	public String loginFail() {
		log.info("loginFail start...");
		return "loginFail";
	}
	
	@GetMapping("/loginForm")
	public String loginForm() {
		log.info("SecurityController loginForm 진행...");
		return "loginForm";
	}
	
	// User 등록 폼
	@GetMapping("/joinForm")
	public String joinForm() {
		log.info("SecurityController joinForm 진행...");
		return "joinForm";
	}
	
	// User 등록
	@PostMapping("/joinProc")
	public String joinProc(User user) {
		log.info("SecurityController 회원가입 진행 :{}",user);
		// Spring Security 권장 --> password : 암호화
		String encPasswd = bCryptPasswordEncoder.encode(user.getPassword());
		user.setPassword(encPasswd);
		securityServiceImpl.save(user);
		return "redirect:/loginForm";
	}
}

[com.oracle.oBootSecurity02.configuration]

  • SecurityConfig.java
package com.oracle.oBootSecurity02.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration // IoC  빈(Bean)을 등록
@EnableWebSecurity	// 필터 체인 관리 시작 어노테이션
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) // 특정 주소 접근시 권한 및 인증을 위한 어노테이션 활성화
public class SecurityConfig {
	
	@Bean
	public BCryptPasswordEncoder encoderPwd() {
		return new BCryptPasswordEncoder();
	}
	
	// 스프링 버전이 올라가면서 WebSecurityConfigurerAdapter 에도 deprecate
	@Bean
	protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http.csrf().disable();
		http.authorizeRequests()
			.antMatchers("/user/**")
			.authenticated() // /user/** 은 인증 필요 --> 인증만 되면 들어갈 수 있다.
		.and()
			.formLogin()
			.loginPage("/loginForm")
			.loginProcessingUrl("/login")
			.failureUrl("/loginFail")
			.defaultSuccessUrl("/");
		return http.build();
	}
}

[com.oracle.oBootSecurity02.configuration.auth]

  • PrincipalDetailsService.java
    UserDetailsService interface
package com.oracle.oBootSecurity02.configuration.auth;

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 org.springframework.transaction.annotation.Transactional;

import com.oracle.oBootSecurity02.entity.User;
import com.oracle.oBootSecurity02.repository.SecurityRepository;

import lombok.RequiredArgsConstructor;

@Service
@Transactional
@RequiredArgsConstructor // DB에 담긴 사용자 인증정보와 비교하기 위해 UserDetailsService에 사용자 정보를 넘겨줌
public class PrincipalDetailsService implements UserDetailsService {
	
	private final SecurityRepository jpaSecurityRepository;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		System.out.println("PrincipalDetailsService username :"+username);
		User user = jpaSecurityRepository.findByUsername(username);
		if(user==null) return null;
		else 		   return new PrincipalDetails(user);
	}

}

[com.oracle.oBootSecurity02.configuration.auth]

  • PrincipalDetails.java
    UserDetails interface
package com.oracle.oBootSecurity02.configuration.auth;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.oracle.oBootSecurity02.entity.User;

import lombok.Data;
//Authentication 객체에 저장할 수 있는 유일한 타입
//login이라는 주소가 호출이 되면, Security가 낚아 채서 login 진행
//login 진행 완료후 Security Session 생성 (Security ContextHolder에 정보 저장)
//Security Session -> Authentication -> UserDetails[PrincipalDetails]

@Data
public class PrincipalDetails implements UserDetails {
	
	private User user;
	
	public PrincipalDetails(User user) {
		this.user = user;
		System.out.println("PrincipalDetails 생성자 getUsername->"+user.getUsername());
	}

	@Override
	// 해당 User의 권한을 리턴하는 곳
	public Collection<? extends GrantedAuthority> getAuthorities() {
		Collection<GrantedAuthority> collect = new ArrayList<>();
		collect.add(new GrantedAuthority() {
			
			@Override
			public String getAuthority() {
				System.out.println("getAuthorities() getAuthority() user.getRole()->"+user.getRole());
				return user.getRole();
			}
		});
		return collect;
	}

	@Override
	public String getPassword() {
		// TODO Auto-generated method stub
		return user.getPassword();
	}

	@Override
	public String getUsername() {
		// TODO Auto-generated method stub
		return user.getUsername();
	}

	@Override
	public boolean isAccountNonExpired() { // 계정의 유효기간
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isAccountNonLocked() { // 계정에 Lock
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isEnabled() { // 사용가능한지
		// 예시->1년동안 Login 안한 사람 휴먼 계정 -> return false
		return true;
	}

}

[templates]

admin, manager, user 전 프로젝트에서 카피

  • loginForm.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>로그인페이지</h1>
	<div class="container">
		<form action="/login" method="post">
			<div class="form-group">
				<label for="username">이름</label>
				<input type="text" name="username" placeholder="이름을 입력하세요"><p>
				<label for="password">password</label>
				<input type="password" name="password" placeholder="password을 입력하세요">
			</div>
			<button type="submit">등록</button>
		</form><p>
		<a href="/joinForm">User 신규 생성</a><p>
	</div>
</body>
</html>
  • loginFail.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>Login Fail 되었네요</h1>
	Login 권한을 살펴보세요.<p>
</body>
</html>
  • joinForm.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>JPA Security 회원 등록(ID 자동생성)</h1>
	<div class="container">
		<form action="/joinProc" method="post">
			이름(회원코드) : <input type="text" id="username" name="username" required="required"><p>
					비번 : <input type="text" id="password" name="password" placeholder="비밀번호를 입력하세요"><p>
				 email : <input type="text" id="email" name="email" placeholder="이메일을 입력하세요"><p>
					권한 : <select id="role" name="role">
							<option value="ROLE_USER" autofocus="autofocus">일반유저</option>
							<option value="ROLE_MANAGER">관리자</option>
							<option value="ROLE_ADMIN">Admin</option>
						 </select>
			<button type="submit">회원가입</button>
		</form>
	</div>
</body>
</html>

0개의 댓글