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는 프레임워크 구축 연습이 선행되어야 할것이다)
댓글
댓글 쓰기