Skip to content

Commit e90c36f

Browse files
committed
AOP 구현 추가
1 parent 6c4b1be commit e90c36f

File tree

7 files changed

+83
-22
lines changed

7 files changed

+83
-22
lines changed

README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# jwt-redis-ip-protection-backend
22

3-
> #### 배경
4-
> >
3+
> #### 배경
4+
>
55
> > 사내 CRM 프로젝트에서 기존 서버 사이드 JSP 프로젝트를 Cilent를 Vue로 전환하면서 Server가 stateless 형식을 가지고 의존성을 분리 시키는 작업이 필요했다.
66
> > 일반적인 Spring Security, JWT, Redis 방식으로 보안성을 증가 시키고 분리시키는 기획을 가졌지만 항상 Token 탈취 우려가 있어 Client IP 까지 추가적으로 넣어 해당 컴퓨터로 로그인한 사용자만 우리 서버를 이용하는 방식으로 전략 짜보았다. 이를 전면 개발해보고 개선 사항을 기록한다.
77
> > Spring Security + JWT + Redis에 IP 검증을 이용한 보안 필터 개발하기.
88
> > JWT 토큰만으로 보안을 지킬 수 있는가 의문을 시작으로 고도화 작업.
99
>
10-
> #### 목표
11-
> >
10+
> #### 목표
11+
>
1212
> > Client가 JWT 토큰을 발급 시 XSS로 인한 토큰 탈취를 방지하기 위한 Cookie HTTPS 설정 추가 ( 브라우저에서 토큰 접근 불가 )
1313
> > Client가 JWT 토큰을 탈취 당할 시, 토큰에 저장된 IP와 Request IP를 검증을 통해 CSRF를 방지 고도화
1414
@@ -229,7 +229,6 @@ sequenceDiagram
229229
- 등록이 없다면 그 뒤에 AnonymousAuthenticationFilter, ExceptionTranslationFilter, AuthorizationFilter에서 요청이 거부되고 403 혹은 401 에러를 반환한다.
230230
- SecurityContextHolder은 Spring Security 전역 객체라 생각해야한다. 다음 필터들이 인증된 객체를 가지고 security 프로세스를 흘러가기에 요청 쓰레드 마다 인증이 되었다면 인증 객체를 꼭 등록이 필요하다
231231

232-
233232
### LogOut 구현
234233

235234
1. **LogOutFilter**
@@ -297,3 +296,7 @@ private Claims getPayloadFromJwtToken(String token) {
297296
```
298297

299298
3.**AOP 적용하여 메타데이터 생성하기**
299+
300+
- 로그인 한 사용자는 Insert, update 등의 CRUD 작업을 행 할시, DB에 등록 / 수정 사용자, IP, 날짜를 공통적으로 세팅하기 위해 AOP 를 추가 하였다. AOP를 통해 로그, 트랙잰션 관리, 보안 관련 해서 다양한 공통 모듈 처리를 할 수 있지만 여기서는 보안 역할로 AOP를 만들었다.
301+
302+
- 각 Controller 안에서 method 수행 시, 요청 VO가 CMMaster 를 상속 받고 있다면 userId, userIp가 세팅이 되도록 하였다.

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
plugins {
22
id 'java'
3-
id 'org.springframework.boot' version '3.3.2-SNAPSHOT'
3+
id 'org.springframework.boot' version '3.3.2'
44
id 'io.spring.dependency-management' version '1.1.5'
55
}
66

src/main/java/com/develop/backend/PingController.java

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.develop.backend.api.controller;
2+
3+
import org.springframework.web.bind.annotation.PostMapping;
4+
import org.springframework.web.bind.annotation.RestController;
5+
6+
import com.develop.backend.api.req.PingReq;
7+
8+
@RestController
9+
public class PingController {
10+
@PostMapping("/ping")
11+
public String ping(PingReq pingReq) {
12+
13+
return "Hello Spring Security!! I'm " + pingReq.getUserId() + " and " + pingReq.getUserIp();
14+
}
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.develop.backend.api.req;
2+
3+
import com.develop.backend.fw.master.CMMaster;
4+
5+
import lombok.Getter;
6+
7+
@Getter
8+
public class PingReq extends CMMaster {
9+
10+
private String input1;
11+
private String input2;
12+
13+
}
Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,56 @@
11
package com.develop.backend.aspect;
22

3+
import org.aspectj.lang.JoinPoint;
4+
import org.aspectj.lang.ProceedingJoinPoint;
35
import org.aspectj.lang.annotation.Aspect;
6+
import org.aspectj.lang.annotation.Before;
7+
import org.aspectj.lang.annotation.Pointcut;
8+
import org.springframework.security.core.Authentication;
9+
import org.springframework.security.core.context.SecurityContextHolder;
10+
import org.springframework.stereotype.Component;
11+
12+
import com.develop.backend.fw.master.CMMaster;
13+
import com.develop.backend.security.model.vo.AuthenticationToken;
14+
15+
import io.jsonwebtoken.lang.Objects;
16+
import lombok.extern.slf4j.Slf4j;
417

518
/**
619
* <p>
720
* 사용자 정보 CMMaster 사전 세팅
821
* </p>
9-
* <p>
10-
* Controller 진입 전 단계에 해당
11-
* </p>
1222
*
1323
* @author gyeongwooPark
1424
* @version 1.0
1525
* @since 2024.07
1626
*/
1727
@Aspect
28+
@Component
29+
@Slf4j
1830
public class UserAspect {
1931

32+
@Pointcut("execution(* com.develop.backend.api.controller.*.*(..))")
33+
private void inWebLayer() {
34+
}
35+
36+
@Before("inWebLayer()")
37+
public void setUserInfo(JoinPoint joinPoint) {
38+
Object[] args = joinPoint.getArgs();
39+
40+
for (Object arg : args) {
41+
if (arg instanceof CMMaster) {
42+
// 사용자 정보 세팅
43+
44+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
45+
if (!Objects.isEmpty(authentication)) {
46+
AuthenticationToken authenticationToken = (AuthenticationToken) authentication;
47+
((CMMaster) arg).setUserId((String) authenticationToken.getPrincipal());
48+
((CMMaster) arg).setUserIp(authenticationToken.getClientIp());
49+
log.info("접속한 사용자 정보----------------------------------");
50+
log.info("사용자 ID : " + (String) authenticationToken.getPrincipal());
51+
log.info("사용자 IP : " + authenticationToken.getClientIp());
52+
}
53+
}
54+
}
55+
}
2056
}

src/main/java/com/develop/backend/security/model/vo/AuthenticationToken.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.develop.backend.security.model.req.LoginRequest;
99
import com.develop.backend.security.model.res.LoginResponse;
1010

11+
import io.jsonwebtoken.lang.Objects;
1112
import lombok.Getter;
1213

1314
/**
@@ -77,7 +78,12 @@ public Object getCredentials() {
7778

7879
@Override
7980
public Object getPrincipal() {
80-
return null;
81+
82+
if (!Objects.isEmpty(loginResponse)) {
83+
return loginResponse.getUserId();
84+
} else {
85+
return null;
86+
}
8187
}
8288

8389
}

0 commit comments

Comments
 (0)