[spring Boot]_14일차_Security, REST API

youuu·2022년 11월 4일
0

SPRING

목록 보기
23/33
post-thumbnail

참고링크

user :
해시화


권한설정


⭐ 표현식 :

  • authenticated() : 인증된 사용자의 접근을 허용
  • fullyAuthenticated() : 인증된 사용자의 접근을 허용, rememberMe인증 제외
  • permitAll() : 무조건 허용
  • denyAll() : 무조건 차단
  • anonymous() : 익명사용자 허용
  • rememberMe() : rememberMe 인증 사용자 접근 허용
  • access(String) : 주어진 SpEL표현식의 평가 결과가 true 이면 접근허용
  • hasRole(String) : 사용자가 주어진 역할이 있다면 접근을 허용
  • hasAuthority(String) : 사용자가 주어진 권한이 있다면 허용
  • hasAnyRole(String...) : 사용자가 주어진 어떤권한이라도 있으면 허용
  • hasAnyAuthority(String...) : 사용자가 주어진 권한중 어떤 것이라도 있다면 허용
  • hasIpAddress(String) : 주어진 IP로 부터 요청이 왔다면 접근을 허용

🌿 예시:

http
    .antMatcher("/users/**")
    .authorizeRequests()
      .antMatchers("/users/login", "/users/join").permitAll()
      .antMatchers("users/mypage").hasRole("USER")
      .antMatchers("/users/admin").access("hasRole('ADMIN')")
      .antMatchers("/users/admin/del").access("hasRole('ADMIN') or hasRole('SYS')")
    .anyRequest().authenticated();

💻 결과화면 :



📂 com.oracle.oBootSecurity02.configuration

📌📋 SecurityConfig.java

user 로 접속 ▶ user만 접속가능
manager 로 접속 ▶ user, manager 가능
admin 로 접속 ▶ user, manager, admin가능

.antMatchers("/user/**").authenticated()  // /user/** 은 인증 필요 --> 인증만 되면 들어갈수 있음
			.antMatchers("/manager/**").access("hasRole('ROLE_MANAGER') or hasRole('ADMIN')")
			.antMatchers("/admin/**").hasRole("ADMIN")
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 encodePwd() {
		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('ROLE_MANAGER') or hasRole('ADMIN')")
			.antMatchers("/admin/**").hasRole("ADMIN")
		.and()
		    .formLogin()
		    .loginPage("/loginForm")
		    .loginProcessingUrl("/login")
		    .failureUrl("/loginFail")
			.defaultSuccessUrl("/")
		;
		return http.build();
	}
}




count 만들기


  1. view단

📋 count.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>count 인증성공</h1>
</body>
</html>

📋 index.html

  • index에 count 부분 작성
	 <p class="lead">COUNT 권한</p>
	 <p>
		 <a class="btn btn-lg btn-info" href="/count">count</a>
		 <a class="btn btn-lg btn-info" href="/count/2">count2</a>
	 </p>

  1. Controller 만들기

📌📋 SecurityController02.java

@PreAuthorize는 해당 메서드가 호출되기 이전에 검사한다.
실제로 해당 메서드를 호출할 권한이 있는지를 확인한다.
(PostAuthorize는 메서드 호출 이후)

관리자 권한(ROLE_ADMIN)이 없는데 본인 id(username)이 아닌 남의 id를 입력하면 관리자 권한이 없다는 예외처리를 해줬었는데 @PreAuthorize로 간단히 처리할 수 있게 되었다.

  • count ▶ manager만 가능
  • count2 ▶ manager, admin만 가능
		@Secured("ROLE_MANAGER")
	@GetMapping("/count")
	public String count() {
		System.out.println("SecurityController02 count Start==========");
		return "admin";
	}
	
//	@PreAuthorize("hasRole('ROLE_MANAGER') or hasRole('ADMIN')")
	@PreAuthorize("hasAnyRole('ROLE_MANAGER', 'ROLE_ADMIN')")
//	@PostAuthorize //실행 후에 인증. 잘 안쓴다
	@GetMapping("/count/2")
	public String count2() {
		System.out.println("SecurityController02 count Start===========");
		return "count";
	}


🌱 Rest API

⭐⭐⭐ @RestController을 사용하여 Rest API를 개발한다.
Controller + ResponseBody
사용목적 --> Ajax + RestApi

Postman 설치

🔵 패키지생성 ➡ oBootJpa01
Type :Gradle, java Version : 11, Packaging : Jar

reopository 는 인터페이스 만들기 필수!
service에선 선택


Rest API를 사용하면 json으로 돌려준다.
▶ json으로 돌려주려면 EAGER 를 써야한다.
(@ManyToOne(fetch = FetchType.LAZY)하면 오류)
Json : {key: value}
key ➡ id / value ➡ 1
key ➡ name / value ➡ 대조영

이때, Postman을 통해 실행

💻 결과화면(Bad API) :

💻 결과화면(Good API) :

🙄❓ Postman?

API 개발을 보다 빠르고 쉽게 구현 할 수 있도록 도와주며, 개발된 API를 테스트하여 문서화 또는 공유 할 수 있도록 도와 주는 플랫폼이다.
▶ URL을 통해서 테스트를 하는것은 한계가 있어 Postman을 사용한다.

  • 작업을 할 수 있도록 인터페이스를 구축해놓은 툴이기 때문에 누구나 쉽게 사용이 가능하다.
  • OS에 상관없이 어디에서나 사용이 가능하고, 가벼운 툴이여서 가용성이 뛰어 나다.

Postman 설명

📌📋 JpaRestApiController .java

<T> 를 사용해 totCount를 보여준다.❓😬 T는 인스턴스를 생성할때 구체적인 타입으로 -> 유연성

💡ver22:

stream을 쓰고 .map을 쓰면 향상형 for문으로 계속 돌아간다.
.map(m->new MemberRtnDto(m.getName(), m.getSal())) 람다식.

  • 모든 데이터를 보여주는 것보다 사용자가 필요한 데이터만 보여주는것이 좋다.
    • 반드시 필요한 데이터만 보여주기 :
      List<Member> findMembers ▶▶ List<MemberRtnDto> resultList
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 + ResponseBody
//사용목적 --> Ajax + RestApi
@RestController
@RequiredArgsConstructor
@Slf4j
public class JpaRestApiController {

	private final MemberService memberService;
	
	//Bad API
	@GetMapping("/restApi/vl/members")
	public List<Member> membersVer1(){
		log.info("JpaRestApiController membersVer1 START ====");
		List<Member> listMember = memberService.getListAllMember();
		return listMember;
	}
	
	//Good API
	//목표 : 이름 & 급여만 전송
	@GetMapping("/restApi/v21/members")
	public Result membersVer21() { // API를 위한 내장객체를 만든다. (inner class)
		List<Member> findMembers 	  = memberService.getListAllMember();
		List<MemberRtnDto> resultList = new ArrayList<MemberRtnDto>();
		for(Member member : findMembers) {
			MemberRtnDto memberRtnDto = new MemberRtnDto(member.getName(), member.getSal());
			log.info("restApi/vl/members memberRtnDto.getName() --->" + memberRtnDto.getName());
			log.info("restApi/vl/members memberRtnDto.getSal() --->" + memberRtnDto.getSal());
			
			resultList.add(memberRtnDto);
		}
		log.info("restApi/vl/members resultList.size() --->" + resultList.size());
		
		return new Result(resultList.size(), resultList);
	}
	
	//Good API - 람다 버전 (실무에서 많이사용)
	//목표 : 이름 & 급여만 전송
	@GetMapping("/restApi/v22/members")
	public Result membersVer22() { // API를 위한 내장객체를 만든다. (inner class)
		List<Member> findMembers 	  = memberService.getListAllMember();
		log.info("restApi/v22/members findMembers.size() --->" + findMembers.size());
		//  자바 8에서 추가한 스트림(Streams)은 람다를 활용할 수 있는 기술 중 하나
		List<MemberRtnDto> memberCollect = findMembers.stream()
										   .map(m->new MemberRtnDto(m.getName(), m.getSal()))
										   .collect(Collectors.toList());
		log.info("restApi/v22/members memberCollect.size() --->" + memberCollect.size());
	
		return new Result(memberCollect.size(), memberCollect);
	}
	
	// T는 인스턴스를 생성할 때 구체적인 타입으로 변경
	@Data
	@AllArgsConstructor
	class Result<T> {  //<T>를 쓰면 어떤 객체든 받을 수 있다.
		private final int totCout;
		private T data;
		
	}
	
	@Data
	@AllArgsConstructor
	class MemberRtnDto {  //<T>를 쓰면 어떤 객체든 받을 수 있다.
		private String name;
		private Long   sal;
		
	}
}

💻 결과화면(totCount) :

⭐⭐ 주말에 정리하기

profile
공부중인 주니어 개발자

0개의 댓글