Spring Boot 로 MSA 방식 JWT Sliding Token 구현해보기

 

https://github.com/najongjine/SpringBootSlidingTokenSample


*Spring Boot Security 출저 - 향단코드

*Spring JWT 출저- 향단코드

*Sliding Token 출저-박종진(najongjine)

 

*선행지식 : MVC 패턴, Spring Framework에서 primitive data CRUD


*Sliding Token이 필요한 이유

- React에서 localstorage 방식을 쓰면 일반 Token, Refresh Token 저장된것까지 다보임.

 React 개발자에게 localstorage 방식을 쓰지 말라고 할수는 없음. React 자체가 패턴이 없는 툴이기 때문에 React 개발자가 편한 코드로 가야함.


*Sliding Token 개념:유저토큰이 탈취 되더라도 일정 시간이 지나면 자동으로 폐기처분 시킨다


*Sliding Token의 보안 담당부분

Front(Client) - Backend Controller - Backend Service - DB Connection

 

*Sliding Token의 보안 Level

상 - 중 -

    없는거보단 낫지만 sliding Token 자체가 실무에서의 모든 보안을 책임져주지 않는다.

    실무에서의 보안수준은 유저정보와 실 데이터 무결성 검증까지 같이 있어야 한다.

 

 

*패키지, 소스파일 구성도. - 이해하기전에 그냥 복붙하자

    오픈되어 있는 패키지만 복붙하면 된다.

    소스코드가 장황한건 자바 특성상 어쩔수 없다.




!!! 주의 !!!

com.jong.DevProject1Application.java,

com.jong.ServletInitializer.java

이 둘은 복붙하는게 아니다.

DevProject1Application 이쪽에 mapper(repository) 경로 설정하는게 있어서 수정하라고 넣어둔거나

@MapperScan(basePackages = "com.jong.mapper")

이 부분을 자신의 프로젝트에 맞게끔 바꿔야한다.

 

MemberMapper.xml 부분은 쿼리부분인데, 이것도 자신의 회원 테이블과 작동할수 있게 수정하자. (tbl_member,tbl_member_auth)

 

*Dependencies

<dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.10.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.10.5</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.10.5</version>
            <scope>runtime</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.11.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple -->
        <dependency>
            <groupId>com.googlecode.json-simple</groupId>
            <artifactId>json-simple</artifactId>
            <version>1.1.1</version>
        </dependency>

<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

 

Nodejs나 .Net 5 처럼 편하게 설치 못한다는게 흠이다...

지저분하면 필자가 올린 github 소스코드에서 보면 더 깔끔하게 볼수 있을 것이다.

 

 

*테이블 구조 - RDB




회원 테이블과 권한 테이블은 필수다

username, password 칼럼 부분을 틀리지 않고그대로 쓰면 복붙한 소스코드가 잘 작동할 것이다.

tbl_member_auth 에서 FK를 username 으로 안하고 tbl_member의 idx로 하였는데

MySQL 특성상 JOIN에서의 퍼포먼스를 높이고, 필자의 버릇상 idx를 만드는 버릇때문이다.

NoSQL 방식은 설명하지 않는다.

 

 

* 핵심 소스파일

com.jong.filter.JwtAuthorizationFilter.java

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response){

...

String username = request.getParameter("username");

String password = request.getParameter("password");

로그인 api 주소는 /api/authenticate

 username, password 를 query string 으로 받는다.

참고로 저 api는 컨트롤러에 존재하지 않는다.


public static String getAuthenticationSlidingToken(String oldToken) 

Front(client) 로부터 받은 토큰에서 유저 정보를 꺼내는 함수이다.


 

 com.jong.filter 에 있는 소스파일들이 핵심이다.

com.jong.filter.JwtAuthenticationFilter.java

public static String signSlidingToken(String username,List<MemberAuth> authList)

 새로운 슬라이딩 토큰을 만들어주는 함수이다.


com.jong.config.SecurityConfig.java (초보용 옵션)

public CorsConfigurationSource corsConfigurationSource()

spring security는 헤더에 토큰을 숨겨서 보내는데

필자는 react에서 숨겨진 토큰을 꺼내는 방법을 모르고,

어짜피 localstorage에 토큰 저장하면 다보이는데 뭔 소용이란 생각에

public CorsConfigurationSource corsConfigurationSource() 함수 안에 

토큰을 헤더에 노출시켜서 보내도록 하였다.


com.jong.controller.member

public Map signSlidingToken(@RequestBody Map<String,String> oldUserToken) throws Exception 

 새로운 유저 토큰을 만들어주는 컨트롤러이다.


*실행 흐름도(자세히는 나도 모름)

로그인시:

    ## JwtAuthenticationFilter. attemptAuthentication

    ## JwtAuthenticationFilter. successfulAuthentication

랜덤 컨트롤러 진입시:


    ## JwtAuthorizationFilter. doFilterInternal
    ## JwtAuthorizationFilter. getAuthentication
    ## AuthUtil. getMemberByUsernameJWT

슬라이딩 토큰 발행시:

   ##MemberController. signSlidingToken 

   ## JwtAuthorizationFilter.getAuthenticationSlidingToken

   ## MemberService .getMemberByUsername

   ## JwtAuthenticationFilter.signSlidingToken

 

 

* 이미 프로젝트가 커져버려서 포팅이 안되는 경우:

필자가 스프링 세큐리티+jwt+sliding token 구성을 위해  복붙하라는 java 파일들

안에 주석을 달아놨다.

customize it!!! 이라는 주석이 보이면 그 부분을 자신의 프로젝트와 맞게끔 고치길 바란다.

 

 

 

슬라이딩 토큰 개념을 이해한다면 NodeJS에서 구현은 더 쉬울것이다.

(참고로 Nodejs는 프레임워크 구축 연습이 선행되어야 할것이다)

댓글

이 블로그의 인기 게시물

[토이강의] Blazor C# 으로 유튜브 동영상 나의 플레이리스트 만들기

[ref game]