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/** 은 인증 필요 --> 인증만 되면 들어갈 수 있다.
.antMatchers("/manager/**").access("hasRole('MANAGER') or hasRole('ADMIN')") // manager에 admin, manager 접근
.antMatchers("/admin/**").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/loginForm")
.loginProcessingUrl("/login")
.failureUrl("/loginFail")
.defaultSuccessUrl("/");
return http.build();
}
}
user일때 -> user만 접근가능
manager일때 -> user, manager 접근가능
admin일때 -> 모든 권한에 접근가능
🔧메소드 시큐리티
- @EnableGlobalMethodSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
// 특정 주소 접근시 권한 및 인증을 위한 어노테이션 활성화- @Secured 와 @RollAllowed -> 위에 선언을 true를 안하면 사용❌( 하나의 권한)
메소드 호출 이전에 권한을 확인한다.- @PreAuthorize 와 @PostAuthorize ( 여러권한 )
메소드 호출 이전 / 이후에 권한을 확인할 수 있다.
@Secured("ROLE_MANAGER")
@GetMapping("/count")
public String count() {
log.info("SecurityController02 count Start...");
return "count";
}
@PreAuthorize("hasRole('MANAGER') or hasRole('ADMIN')")
@GetMapping("/count/2")
public String count2() {
log.info("SecurityController02 count2 Start...");
return "count";
}
Manager로 들어 왔을 때
Admin으로 들어와 count2에 들어갔을 때
위치 : C:\spring\Lec -> Postman-win64-7.36.1-Setup.exe
실행 시킨 후 구글 로그인으로 연동
server:
port : 8388
# 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
@RestController( @Controller에 @ResponseBody 가 추가된 것)
- 주용도는 Json 형태로 객체 데이터를 반환
- @Controller는 반환 값이 String 이면 뷰 이름으로 인식 / 그래서 뷰를 찾고 뷰가 랜더링
- @RestController는 반환 값이 뷰를 찾지 않음 / HTTP 메시지 바디에 바로 입력
-> 따라서 실행결과로 OK 메시지를 받을 수 있다. 참고
Bad API
package com.oracle.oBootJpaApi01.controller;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.oracle.oBootJpaApi01.domain.Member;
import com.oracle.oBootJpaApi01.service.MemberService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
// Controller + Repository
// 사용 목적 -> Ajax + RestApi
@RestController
@Slf4j
@RequiredArgsConstructor
public class JpaRestApiController {
private final MemberService memberService;
// Bad Api
@GetMapping("/restApi/v1/members")
public List<Member> membersVer1(){
log.info("/restApi/v1/members Start...");
List<Member> listMember = memberService.getListAllMember();
return listMember;
}
}
package com.oracle.oBootJpaApi01.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import lombok.Data;
@Entity
@Data
@SequenceGenerator(
name = "member_seq_gen5",
sequenceName = "member_seq_generator5",
initialValue = 1,
allocationSize = 1
)
@Table(name = "member5")
public class Member {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "member_seq_gen5"
)
@Column(name = "member_id")
private Long id;
@Column(name = "userName")
private String name;
private Long sal;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team; // join
}
package com.oracle.oBootJpaApi01.domain;
import javax.persistence.Column;
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 lombok.Data;
@Entity
@Data
@SequenceGenerator(
name = "team_seq_gen5",
sequenceName = "team_seq_generator5",
initialValue = 1,
allocationSize = 1
)
@Table(name = "team5")
public class Team {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "team_seq_gen5")
private Long teamId;
@Column(name = "teamname", length = 50)
private String name;
}
package com.oracle.oBootJpaApi01.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oracle.oBootJpaApi01.domain.Member;
import com.oracle.oBootJpaApi01.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Service
@Slf4j
@RequiredArgsConstructor
@Transactional
public class MemberService {
private final MemberRepository memberRepository;
// 전체회원조회
public List<Member> getListAllMember(){
List<Member> listMember = memberRepository.findAll();
log.info("getListAllMember listMember.size->{}",listMember.size());
return listMember;
}
}
package com.oracle.oBootJpaApi01.repository;
import java.util.List;
import com.oracle.oBootJpaApi01.domain.Member;
public interface MemberRepository {
Long save(Member member);
List<Member> findAll();
}
package com.oracle.oBootJpaApi01.controller;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.oracle.oBootJpaApi01.domain.Member;
import com.oracle.oBootJpaApi01.service.MemberService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
// Controller + Repository
// 사용 목적 -> Ajax + RestApi
@RestController
@Slf4j
@RequiredArgsConstructor
public class JpaRestApiController {
private final MemberService memberService;
// Bad Api
@GetMapping("/restApi/v1/members")
public List<Member> membersVer1(){
log.info("/restApi/v1/members Start...");
List<Member> listMember = memberService.getListAllMember();
return listMember;
}
}
데이터를 넣어놓은 상태에서 실행(Json -> 배열의 형태)
Postman에서 실행
Bad Api -> Good Api 이전 목적
-> 반드시 필요한 Data 만 보여준다(외부 노출 최대한 금지)
내부 클래스 씀
package com.oracle.oBootJpaApi01.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.oracle.oBootJpaApi01.domain.Member;
import com.oracle.oBootJpaApi01.service.MemberService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
// Controller + Repository
// 사용 목적 -> Ajax + RestApi
@RestController
@Slf4j
@RequiredArgsConstructor
public class JpaRestApiController {
private final MemberService memberService;
// Bad Api
@GetMapping("/restApi/v1/members")
public List<Member> membersVer1(){
log.info("/restApi/v1/members Start...");
List<Member> listMember = memberService.getListAllMember();
return listMember;
}
// Good Api
// 목표 : 이름 & 급여 만 전송
@GetMapping("/restApi/v21/members")
public Result membersVer21() {
List<Member> findMembers = memberService.getListAllMember();
log.info("/restApi/v21/members Start findMembers.size->{}...",findMembers.size());
List<MemberRtnDto> resultList = new ArrayList<MemberRtnDto>();
for(Member member : findMembers) {
MemberRtnDto memberRtnDto = new MemberRtnDto(member.getName(), member.getSal());
log.info("/restApi/v21/members getName->{}",memberRtnDto.getName());
log.info("/restApi/v21/members getSal->{}",memberRtnDto.getSal());
resultList.add(memberRtnDto);
}
log.info("/restApi/v21/members resultlist.size->{}",resultList.size());
return new Result(resultList.size(),resultList);
}
// T는 인스턴스를 생성할 때 구체적인 타입으로 변경
@Data
@AllArgsConstructor // 알아서 생성자를 만들어줌
class Result<T> { // T 어떤 객체든 받을 수 있음
private final int totCnt; // 총 인원 수
private final T data;
}
@Data
@AllArgsConstructor // 알아서 생성자를 만들어줌
class MemberRtnDto {
private String name;
private Long sal;
}
}
RestAPI -> RestAPI
- 람다식(Lambda Expression) 이란?
-> Stream 연산들은 매개변수로 함수형 인터페이스를 받도록 되어있다. 그리고 람다식은 반환값으로 함수형 인터페이스를 반환하고 있다. 람다식이란 함수를 하나의 식으로 표현한 것이다. 함수를 람다식으로 표현하면 메소드의 이름이 필요없기 때문에 람다식은 익명함수(Anonymous Function)의 한 종류라고 볼 수 있다. 익명함수란 함수의 이름이 없는 함수로, 익명함수들은 모두 일급 객체다. 일급 객체 함수는 변수처럼 사용가능하며 매개변수로 전달이 가능하는 등의 특징을 가졌다.- 자바 스트림(Stream) : 컬렉션에 저장되어 있는 요소들을 하나씩 참조하여 람다식으로 처리할 수 있도록 해주는 코드패턴(반복자)이다.
스트림은 람다식과 함께 사용되기 때문에 데이터에 대한 처리를 매우 간결하게 작성할 수 있는 점과 내부 반복자라는 것을 사용하기 때문에 병렬처리가 쉽다는 장점- Stream().map().collect()
-> map()은 인자로 함수를 받으며, Stream의 요소를 다른 형태로 변경. 인자로 전달되는 함수를 구현하여 요소를 어떻게 변경할지 설정할 수 있다.
-> collect() : Stream 데이터를 원하는 자료형으로 변환 해준다. 예를 들어 map()으로 데이터를 추출했고 컬렉션이나 배열로 변환해서 반환 할 수 있는 예제를 보자
stream-map/collect 사용방법
package com.oracle.oBootJpaApi01.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.oracle.oBootJpaApi01.domain.Member;
import com.oracle.oBootJpaApi01.service.MemberService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
// Controller + Repository
// 사용 목적 -> Ajax + RestApi
@RestController
@Slf4j
@RequiredArgsConstructor
public class JpaRestApiController {
private final MemberService memberService;
// Bad Api
@GetMapping("/restApi/v1/members")
public List<Member> membersVer1(){
log.info("/restApi/v1/members Start...");
List<Member> listMember = memberService.getListAllMember();
return listMember;
}
// Good Api
// 목표 : 이름 & 급여 만 전송
@GetMapping("/restApi/v21/members")
public Result membersVer21() {
List<Member> findMembers = memberService.getListAllMember();
log.info("/restApi/v21/members Start findMembers.size->{}...",findMembers.size());
List<MemberRtnDto> resultList = new ArrayList<MemberRtnDto>();
for(Member member : findMembers) {
MemberRtnDto memberRtnDto = new MemberRtnDto(member.getName(), member.getSal());
log.info("/restApi/v21/members getName->{}",memberRtnDto.getName());
log.info("/restApi/v21/members getSal->{}",memberRtnDto.getSal());
resultList.add(memberRtnDto);
}
log.info("/restApi/v21/members resultlist.size->{}",resultList.size());
return new Result(resultList.size(),resultList);
}
// Good Api 람다 Version
// 목표 : 이름 & 급여 만 전송
@GetMapping("/restApi/v22/members")
public Result membersVer22() {
List<Member> findMembers = memberService.getListAllMember();
log.info("/restApi/v22/members Start findMembers.size->{}...",findMembers.size());
List<MemberRtnDto> memberCollect = findMembers.stream()
.map(m->new MemberRtnDto(m.getName(), m.getSal()))
.collect(Collectors.toList())
;
log.info("/restApi/v22/members resultlist.size->{}",memberCollect.size());
return new Result(memberCollect.size(),memberCollect);
}
// T는 인스턴스를 생성할 때 구체적인 타입으로 변경
@Data
@AllArgsConstructor // 알아서 생성자를 만들어줌
class Result<T> { // T 어떤 객체든 받을 수 있음
private final int totCnt; // 총 인원 수
private final T data;
}
@Data
@AllArgsConstructor // 알아서 생성자를 만들어줌
class MemberRtnDto {
private String name;
private Long sal;
}
}
chrome에서 입력X -> Postman에서 입력