Giter Site home page Giter Site logo

likelion-securty's Introduction

๐Ÿฆ [๋ฉ‹์‚ฌ] - JWT ์—ฐ๋™ ํ”„๋กœ์ ํŠธ

ํŠน์ • ํด๋ž˜์Šค์— ๋Œ€ํ•œ ์„ค๋ช… ๐Ÿ’ฌ

RsData ํด๋ž˜์Šค

RestController์—์„œ Success/Fail์— ๋Œ€ํ•œ ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ฃผ๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค

public class RsData<T> {
    private String resultCode;
    private String msg;
    private T data;

    public static <T> RsData<T> of(String resultCode, String msg) {
        return new RsData<>(resultCode, msg, null);
    }
}

resultCode, msg์˜ ์‚ฌ์šฉ ์ด์œ 

  • ์•„๋ž˜ ์˜ˆ์‹œ์™€ ๊ฐ™์ด ์„ฑ๊ณต๊ณผ ์‹คํŒจ์— ๋Œ€ํ•ด ๋” ์ž์„ธํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์„ฑ๊ณต : ์ •์ƒ ๋กœ๊ทธ์ธ || ํœด๋จผํšŒ์›(์ผ๋ถ€ ๊ธฐ๋Šฅ ์‚ฌ์šฉ ๊ฐ€๋Šฅ)
    • ์‹คํŒจ : ์•„์ด๋”” ๋ถˆ์ผ์น˜ || ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ถˆ์ผ์น˜ || ์ผ์‹œ ์ค‘์ง€ || ์˜๊ตฌ ์ •์ง€

data์˜ ์‚ฌ์šฉ ์ด์œ 

GET /articles

  • ์œ„ ์š”์ฒญ์ด ๋“ค์–ด์™”์„ ๋•Œ articles์— ์ถœ๋ ฅ์„ ํ•ด์ค„ data๋ฅผ ๋‹ด์•„์„œ ๋ณด๋‚ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

Util ํด๋ž˜์Šค

Json, HTTP๊ณผ ๊ด€๋ จ๋œ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๋Š” ํด๋ž˜์Šค

Generic ์‚ฌ์šฉ

// Util.spring
public static <T> ResponseEntity<RsData> responseEntityOf(RsData<T> rsData) {
    return responseEntityOf(rsData, null);
}
  • ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ์ •์ ์œผ๋กœ ์ง€์ •ํ•ด์„œ ์‚ฌ์šฉํ•ด์ฃผ๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ

๊ฐ€๋ณ€์ธ์ž ์‚ฌ์šฉ

// Util.json
public static <K, V> Map<K, V> mapOf(Object... args) {
    ...
}
{
    "resultCode": "S-1",
    "msg": "๋กœ๊ทธ์ธ ์„ฑ๊ณต, Access Token์„ ๋ฐœ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.",
    "data": {
        "Authentication": "JWT_Access_Token",
        "username": "abc123"
    },
    "success": true,
    "fail": false
}
  • ์œ„์™€ ๊ฐ™์ด data์—๋Š” ์—ฌ๋Ÿฌ ์ •๋ณด๊ฐ€ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ์–ด์•ผํ•œ๋‹ค.
  • ๋•Œ๋ฌธ์— ๊ฐ€๋ณ€์ธ์ž๋ฅผ ์‚ฌ์šฉํ•ด ๊ธธ์ด์˜ ์ œํ•œ์„ ๋‘์ง€ ์•Š๊ณ , ์—ฌ๋Ÿฌ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

ํŠน์ • ๊ธฐ์ˆ ์— ๋Œ€ํ•œ ์„ค๋ช… ๐Ÿ’ฌ

REST API ์‚ฌ์šฉ

์ž์›์„ ์ด๋ฆ„(์ž์›์˜ ํ‘œํ˜„)์œผ๋กœ ๊ตฌ๋ถ„ํ•˜์—ฌ ํ•ด๋‹น ์ž์›์˜ ์ƒํƒœ(์ •๋ณด)๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š” ๋ชจ๋“  ๊ฒƒ์„ ์˜๋ฏธ

๊ธฐ์กด ๋ฐฉ์‹

REST ๋ฐฉ์‹


Spring Doc - Swagger

์ฃผ๋กœ REST API๋ฅผ ๊ตฌ์ถ• ํ–ˆ์„ ๋•Œ, ํ”„๋ก ํŠธ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋‘์—๊ฒŒ ๋ณด์—ฌ์ง€๊ธฐ ๋•Œ๋ฌธ์— ์•„์ด๋””, ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ง€์ •ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

์˜์กด์„ฑ ์ถ”๊ฐ€

implementation 'org.springdoc:springdoc-openapi-ui:1.6.11'

Config ์„ค์ •

  • Security
.authorizeRequests(
        authorizeRequests -> authorizeRequests
                .antMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
)

Swagger์—์„œ ํ™•์ธ์„ ํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ด€๋ จ๋œ URL์€ permitAll์„ ํ•ด์ค€๋‹ค.
๋‹จ, ์‹ค์ œ ์„œ๋น„์Šค๋ฅผ ์šด์˜ํ•  ์‹œ์—๋Š” ๋ณด์•ˆ์„ ๊ฑธ์–ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

  • Spring Doc
@Bean
public OpenAPI springShopOpenAPI() {
    return new OpenAPI()
            .info(new Info().title("SpringShop API")
                    .description("Spring shop sample application")
                    .version("v0.0.1")
                    .license(new License().name("Apache 2.0").url("http://springdoc.org")))
            .externalDocs(new ExternalDocumentation()
                    .description("SpringShop Wiki Documentation")
                    .url("https://springshop.wiki.github.org/docs"));
}

Swagger์— ์ž‘์„ฑํ•ด๋†“์„ ์„ค๋ช…๊ณผ ์ •๋ณด๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.


JWT ์‚ฌ์šฉ

Json ํฌ๋งท์„ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ์†์„ฑ์„ ์ €์žฅํ•˜๋Š” Claim ๊ธฐ๋ฐ˜์˜ Web Token

Spring Security ๋กœ๊ทธ์ธ

  • ํšŒ์› ์ •๋ณด๊ฐ€ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธ
  • ์„ธ์…˜ ๋ณ€์ˆ˜ ์ƒ์„ฑ -> ์‹œํ๋ฆฌํ‹ฐ ๋ฐฉ์‹

@AuthenticationPrincipal MemberContext memberContext๋Š” ์„ธ์…˜์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊บผ๋‚ด์˜ด

JWT ๋กœ๊ทธ์ธ

POST /member/login

  • ํšŒ์› ์ •๋ณด๊ฐ€ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธ
  • JWT ํ˜•์‹์˜ accessToken์„ ๋ฐœ๊ธ‰

header์— Authorization : Bearer accessToken๋กœ ์ €์žฅ๋Œ ์ดํ›„ ์„ธ์„  ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ์ง€ Security์—์„œ ์ œ๊ณตํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

JWT์˜ ๋ฌธ์ œ์ ๊ณผ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

๋ฌธ์ œ 1. ํ๊ธฐ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ , ํ’ˆ๊ณ  ์žˆ๋Š” ์ •๋ณด๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค.

๋‹‰๋„ค์ž„ ๋˜๋Š” ์ด๋ฉ”์ผ ๊ฐ™์€ ์ •๋ณด๊ฐ€ ์ €์žฅ๋˜์–ด ์žˆ๊ณ , ํ† ํฐ ๋ฐœ๊ธ‰ ์ดํ›„์— ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฉ”์ผ, ๋‹‰๋„ค์ž„์„ ๋ณ€๊ฒฝํ–ˆ๋‹ค๋ฉด ์ •๋ณด ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒ

๋ฌธ์ œ 2. ํ† ํฐ ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„์ด ๊ธธ ๋•Œ, ํƒˆ์ทจ๋ฅผ ๋‹นํ–ˆ๋‹ค๋ฉด ์†์„ ์“ธ ์ˆ˜ ์—†๋‹ค.

๋งŒ์•ฝ ํ† ํฐ์˜ ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„์„ 90์ผ๋กœ ์ง€์ •ํ•œ ๋’ค ํƒˆ์ทจ๋ฅผ ๋‹นํ–ˆ๋‹ค๋ฉด, JWT ํ† ํฐ์˜ ๊ตฌ์กฐ์ƒ ํ๊ธฐ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

๋ฐฉ์•ˆ 1. ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„์„ ์งง๊ฒŒ 5๋ถ„ ์ •๋„๋กœ ์žก๋Š”๋‹ค.

// MemberService.java
public String generateAccessToken(Member member) {
    return jwtProvider.generateAccessToken(member.getAccessTokenClaims(), 60 * 5);
}

5๋ถ„๋งˆ๋‹ค 1๋ฒˆ์”ฉ ๋กœ๊ทธ์ธ์„ ํ•ด์ค˜์•ผํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์žˆ์ง€๋งŒ, Refresh Token์„ ์‚ฌ์šฉํ•ด์„œ ํ•ด๊ฒฐํ•œ๋‹ค.
ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” Access Token๊ณผ Refresh Token์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผํ•œ๋‹ค.

  • Refresh Token
    • JWT ํ˜•์‹์ผ ํ•„์š”๋Š” ์—†์œผ๋ฉฐ, DB์—์„œ ๊ด€๋ฆฌ๋˜๋Š” ์ž„์‹œ ํ‚ค(๋น„๋ฐ€๋ฒˆํ˜ธ)๋ผ๊ณ  ์ƒ๊ฐํ•˜์ž.
    • ํ•ด๋‹น Refresh Token์œผ๋กœ ์žฌ๋กœ๊ทธ์ธ ์ฆ‰, Access Token์„ ๋ฐœ๊ธ‰ ๋ฐ›๋Š”๊ฒŒ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

JWT Access Token ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹

๋ธ”๋ž™ ๋ฆฌ์ŠคํŠธ โžก๏ธ ๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ์• ๋“ค์„ ๊ธฐ์–ต
ํ™”์ดํŠธ ๋ฆฌ์ŠคํŠธ โžก๏ธ ๋‚ด๊ฐ€ ์ธ์ •ํ•œ ์• ๋“ค๋งŒ ๊ธฐ์–ต

  • ์ตœ์ดˆ์— ๋ฐœ๊ธ‰๋œ ์•ก์„ธ์Šค ํ† ํฐ์„ ๊ธฐ๋ก
  • 2๋ฒˆ์งธ ์ดํ›„๋กœ ์š”์ฒญ๋œ ๋กœ๊ทธ์ธ์—๋Š” ๊ธฐ์กด ์—‘์„ธ์Šค ํ† ํฐ์„ ๋ฐ˜ํ™˜
  • ์„œ๋ฒ„์—์„œ ๋งค ์š”์ฒญ๋งˆ๋‹ค, ์—‘์„ธ์Šค ํ† ํฐ์ด ์œ ํšจํ•œ์ง€ ์ฒดํฌํ•˜๋Š” ๊ฒƒ์„ ๋„˜์–ด์„œ, DB์— ์ €์žฅ๋˜์–ด ์žˆ๋Š” ์ธ์ฆ๋œ ์—‘์„ธ์Šค ํ† ํฐ์ธ์ง€๋„ ํ™•์ธ

Cache

Spring Boot๋Š” ์™ธ๋ถ€ ์บ์‹œ๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด Map๊ณผ ๊ฐ™์€ ๋‚ด๋ถ€ ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. Application์—์„œ @EnableCaching ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค.

Setting

SpringBoot์—์„œ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ฐ„๋‹จํ•œ ์ž‘์—… ํ•˜๋‚˜๋งŒ ์ง„ํ–‰ํ•˜๋ฉด ๋œ๋‹ค.

// Application.java
@...
@EnableCaching
public class SecurtyExamApplication {
  ...
}

์บ์‹œ ์ƒ์„ฑ ์˜ˆ์ œ

// MemberService
@Cacheable("key1")
public int getCachedInt() {
    System.out.println("ํ˜ธ์ถœ๋จ");
    return 5;
}
// CacheTests
@Test
@DisplayName("์บ์‹œ ์‚ฌ์šฉ")
void t1() {
    int rs = memberService.getCachedInt();
    assertThat(rs).isGreaterThan(0);
    System.out.println("rs = " + rs);
    
    rs = memberService.getCachedInt();
    assertThat(rs).isGreaterThan(0);
    System.out.println("rs = " + rs);
}

์œ„์™€ ๊ฐ™์ด @Cachable("keyName")์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฒ˜์Œ ํ˜ธ์ถœ์ด ๋  ๋•Œ keyName์— return ๊ฐ’์„ ์ €์žฅํ•˜๊ณ ,
๋‹ค์Œ ํ˜ธ์ถœ์ด ๋  ๋•Œ๋ถ€ํ„ฐ๋Š” ํ•จ์ˆ˜๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์ €์žฅ๋œ keyName์˜ value๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ฒŒ ๋œ๋‹ค.

์บ์‹œ ์‚ญ์ œ ์˜ˆ์ œ

@CacheEvict("key1")
public void deleteCachedInt() {
    System.out.println("์‚ญ์ œ๋จ");
}
@Test
@DisplayName("์บ์‹œ ์‚ฌ์šฉ, ์‚ญ์ œ, ์ƒ์„ฑ")
void t2() {
    // ์บ์‹œ ์ƒ์„ฑ(rs = 5)
    int rs = memberService.getCachedInt();
    assertThat(rs).isGreaterThan(0);
    System.out.println("rs = " + rs);

    // ์บ์‹œ ์‚ฌ์šฉ(rs = 5)
    rs = memberService.getCachedInt();
    assertThat(rs).isGreaterThan(0);
    System.out.println("rs = " + rs);

    // ์บ์‹œ ์‚ญ์ œ
    memberService.deleteCachedInt();

    // ์บ์‹œ ์ƒ์„ฑ(rs = 5)
    rs = memberService.getCachedInt();
    assertThat(rs).isGreaterThan(0);
    System.out.println("rs = " + rs);
}

์บ์‹œ ์ˆ˜์ • ์˜ˆ์ œ

@CachePut("key1")
public int updateCachedInt() {
    System.out.println("์ˆ˜์ •๋จ");
    return 10;
}
@Test
@DisplayName("์บ์‹œ ์‚ฌ์šฉ, ์‚ญ์ œ, ์ƒ์„ฑ")
void t2() {
    // ์บ์‹œ ์ƒ์„ฑ(rs = 5)
    int rs = memberService.getCachedInt();
    assertThat(rs).isGreaterThan(0);
    System.out.println("rs = " + rs);
    
    // ์บ์‹œ ์‚ฌ์šฉ(rs = 5)
    rs = memberService.getCachedInt();
    assertThat(rs).isGreaterThan(0);
    System.out.println("rs = " + rs);

    // ์บ์‹œ ์ˆ˜์ •(rs = 10)
    rs = memberService.updateCachedInt();
    assertThat(rs).isGreaterThan(0);
    System.out.println("rs = " + rs);

    // ์บ์‹œ ์‚ฌ์šฉ(rs = 10)
    rs = memberService.getCachedInt();
    assertThat(rs).isGreaterThan(0);
    System.out.println("rs = " + rs);
}

Redis ์—ฐ๋™

Redis๋Š” key : value๋กœ ์ด๋ฃจ์–ด์ง„ NoSQL์ด๋‹ค.

SpringBoot ๋‚ด๋ถ€ ์บ์‹œ : ์ž๋ฐ” ์ž๋ฃŒ๊ตฌ์กฐ 100% ํ™œ์šฉ ๊ฐ€๋Šฅ
SpringBoot Redis ์บ์‹œ : List, Map์„ ์ œ์™ธํ•œ ์‚ฌ์šฉ์ž ์ •์˜ ๊ฐ์ฒด๋Š” ์žฌ์ •์˜ ํ•ด์ฃผ์–ด์•ผ ํ•จ

Redis ์„ค์น˜

brew install redis

Redis ์‹คํ–‰ ๋ฐ ์‚ฌ์šฉ

brew services start redis
redis-cli

Spring ํ”„๋กœ์ ํŠธ์™€ ์—ฐ๋™

# build.gradle
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
# application.yml
spring:
  cache:
    type: redis
  redis:
    host: 127.0.0.1
    port: 6379

likelion-securty's People

Contributors

jwhyee avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.