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
oBootJpa03에서 복사
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>User 인증 성공</h1>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Manager 인증 성공</h1>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Admin 인증 성공</h1>
</body>
</html>
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";
}
}
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();
}
}
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";
}
}
entity, domain -> JPA
dto -> MyBatis
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
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;
}
package com.oracle.oBootSecurity02.repository;
import com.oracle.oBootSecurity02.entity.User;
public interface SecurityRepository {
void save(User user);
User findByUsername(String username);
}
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;
}
}
package com.oracle.oBootSecurity02.service;
import com.oracle.oBootSecurity02.entity.User;
public interface SecurityService {
User findByUsername(String username);
void save(User user);
}
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;
}
}
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";
}
}
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();
}
}
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);
}
}
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;
}
}
admin, manager, user 전 프로젝트에서 카피
<!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>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Login Fail 되었네요</h1>
Login 권한을 살펴보세요.<p>
</body>
</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>