국비학원_73일차(Spring Security, API)

써니·2022년 11월 4일
0

spring

목록 보기
15/23

👮‍♀️ Spring Security

스프링 시큐리티 동작구조 이해
예시




🤝권한부여

[com.oracle.oBootSecurity02.configuration]

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일때 -> 모든 권한에 접근가능




🤝권한부여2

🔧메소드 시큐리티

  • @EnableGlobalMethodSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
    // 특정 주소 접근시 권한 및 인증을 위한 어노테이션 활성화
  • @Secured 와 @RollAllowed -> 위에 선언을 true를 안하면 사용❌( 하나의 권한)
    메소드 호출 이전에 권한을 확인한다.
  • @PreAuthorize 와 @PostAuthorize ( 여러권한 )
    메소드 호출 이전 / 이후에 권한을 확인할 수 있다.

[com.oracle.oBootSecurity02.controlelr]

  • SecurityControlelr.java
@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에 들어갔을 때




🌞Spring boot

Rest API

⚙Postman 설치

위치 : C:\spring\Lec -> Postman-win64-7.36.1-Setup.exe

실행 시킨 후 구글 로그인으로 연동








🔻oBootJpaApi01

  • application.yml
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 VS Controller

@RestController( @Controller에 @ResponseBody 가 추가된 것)

  • 주용도는 Json 형태로 객체 데이터를 반환
  • @Controller는 반환 값이 String 이면 뷰 이름으로 인식 / 그래서 뷰를 찾고 뷰가 랜더링
  • @RestController는 반환 값이 뷰를 찾지 않음 / HTTP 메시지 바디에 바로 입력
    -> 따라서 실행결과로 OK 메시지를 받을 수 있다. 참고

👿Bad API

[com.oracle.oBootJpaApi01.controller]

  • JpaRestApiController.java
    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;
	}
}

[com.oracle.oBootJpaApi01.domain]

  • Member.java
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
}

[com.oracle.oBootJpaApi01.domain]

  • Team.java
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;
}

[com.oracle.oBootJpaApi01.service]

  • MemberService.java
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;
	}
	
}

[com.oracle.oBootJpaApi01.repository]

  • MemberRepository.java( interface )
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();
}

  • JpaMemberRepository.java( +interface )
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에서 실행




😇Good API

Bad Api -> Good Api 이전 목적
-> 반드시 필요한 Data 만 보여준다(외부 노출 최대한 금지)


[com.oracle.oBootJpaApi01.controller]

내부 클래스 씀

  • JpaRestApiController.java
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(람다)

  • 람다식(Lambda Expression) 이란?
    -> Stream 연산들은 매개변수로 함수형 인터페이스를 받도록 되어있다. 그리고 람다식은 반환값으로 함수형 인터페이스를 반환하고 있다. 람다식이란 함수를 하나의 식으로 표현한 것이다. 함수를 람다식으로 표현하면 메소드의 이름이 필요없기 때문에 람다식은 익명함수(Anonymous Function)의 한 종류라고 볼 수 있다. 익명함수란 함수의 이름이 없는 함수로, 익명함수들은 모두 일급 객체다. 일급 객체 함수는 변수처럼 사용가능하며 매개변수로 전달이 가능하는 등의 특징을 가졌다.
  • 자바 스트림(Stream) : 컬렉션에 저장되어 있는 요소들을 하나씩 참조하여 람다식으로 처리할 수 있도록 해주는 코드패턴(반복자)이다.
    스트림은 람다식과 함께 사용되기 때문에 데이터에 대한 처리를 매우 간결하게 작성할 수 있는 점과 내부 반복자라는 것을 사용하기 때문에 병렬처리가 쉽다는 장점
  • Stream().map().collect()
    -> map()은 인자로 함수를 받으며, Stream의 요소를 다른 형태로 변경. 인자로 전달되는 함수를 구현하여 요소를 어떻게 변경할지 설정할 수 있다.
    -> collect() : Stream 데이터를 원하는 자료형으로 변환 해준다. 예를 들어 map()으로 데이터를 추출했고 컬렉션이나 배열로 변환해서 반환 할 수 있는 예제를 보자
    stream-map/collect 사용방법

[com.oracle.oBootJpaApi01.controller]

  • JpaRestApiController.java
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에서 입력

회원가입

0개의 댓글