
Web Server 쪽에 위치하여 클라이언트의 접근을 최초로 받아 리퀘스트에 해당하는 Web Server에 배분해주는 역할을 한다.
클라이언트는 웹 서비스에 접근할 때 프록시로 요청하게 되고, 프록시가 배후(reverse)의 서버로부터 데이터를 가져오는 방식

리버스 프록시는 서버가 누구인지를 감추는 역할을 한다. 클라이언트는 리버스 프록시 서버를 먼저 호출하게 되기에 실제 서버의 IP를 알 수 없다.
내부 서버가 직접 서비스를 제공해도 되지만 이렇게 구성하는 이유는 보안 때문
→ 흔히 서버 아키텍처로 구성하는 web server(nginx) - WAS(tomcat) 분리 형태를 reverse proxy라고 보면 된다.
스프링 시큐리티에서 권한들과 역할은 기본적으로 GrantedAuthority에 저장된다.
GrantedAuthority는 권한이나 역할의 이름을 반환하는 메소드를 제공public interface GrantedAuthority extends Serializable {
String getAuthority();
}
SimpleGrantedAuthority 클래스는 GrantedAuthority 인터페이스의 기본으로 구현되는 객체Request for Comments"의 약자로, 인터넷 기술에 대한 연구, 혁신, 기법 등을 담은 문서
Json Web Token
사용자가 누구인지 기억하기 위한 수단
헤더.페이로드(본문).시그니처(서명)
Signature에서 사용하는 알고리즘은 대표적으로 RS256(공개키/개인키)와 HS256(비밀키(대칭키))가 있다.
사용자 정보의 한 조각인 클레임(claim)이 들어있다.
Signature는 헤더와 페이로드의 문자열을 합친 후에, 헤더에서 선언한 알고리즘과 key를 이용해 암호한 값
public String issue() {
String token = Jwts.builder()
.subject("Hello World") // 페이로드에 제목
.claim("spring", "easy") // 키, 값
.issuedAt(new Date()) //유효기간 지정 -> 언제 발급 되었는지
.expiration(new Date(new Date().getTime() + 60000L)) // 만료기간
.signWith(Keys.hmacShaKeyFor("A5A10BC17B4E653C78BDD55FFFD9DEE3BE5790CD6F3F64DC1E82E791F9821C17".getBytes()), Jwts.SIG.HS256) // 서명 -> 어떻게 암호화 할지를 기재
.compact();
return "";
}
Jwts → Jwt 객체를 만듬
custom:
jwt:
validation:
exp: 60000
yml 파일에서 생성하고 Service파일에서 @Value 를 통해 사용가능(경로 지정)
@Value("${custom.jwt.validation.exp}")
private Long exp;
@Getter
@RequiredArgsConstructor
@ConfigurationProperties(prefix = "custom.jwt")
public class JwtConfiguration {
그리고 메인 애플리케이션 실행 클래스에 @ConfigurationPropertiesScan를 붙여준다.
서버에서 정보를 관리하지 않기 때문에 서버는 정상적인 유저의 토큰인지, 해커에게 탈취된 토큰인지 알 수가 없다. → 추가적인 보안 강화가 필요
💡Http Only, Secure, Refresh Token 등 다양한 방법이 존재
인증용 token(Access Token)과 갱신용 Token(Refresh Token)으로 관리
→ Refresh Token은 DB에 들어가 있어야됨 → 나중에 죽여야(?)되기 때문에 (Black List)
인증용 token(Access Token)는 OAuth에서 사용중

사용자가 로그아웃을 요청했을 때, 클라이언트 측에서는 캐시에 저장된 jwt를 제거하지만 서버에서는 jwt를 무력화할 수단이 없다.
Vault에 Key와 Value를 맞길 수 있다.
(값을 동적으로 넣어줄 수 있음)
→ 외부 API에게 secret key 를 맡기는 것.
public String issue(Long id, Role role) {
String token = Jwts.builder()
.subject(id.toString()) // 페이로드에 제목
.claim("Role", role) // 키, 값
.issuedAt(new Date()) //유효기간 지정 -> 언제 발급 되었는지
.expiration(new Date(new Date().getTime() + jwtConfiguration.getValidation().getExp())) // 만료기간
.signWith(getSecretKey(), Jwts.SIG.HS256) // 서명 -> 어떻게 암호화 할지를 기재
.compact();
return token;
}
private SecretKey getSecretKey() {
return Keys.hmacShaKeyFor(jwtConfiguration.getSecrets().getAppKey().getBytes());
}
try{
Jwts.parser()
.verifyWith(getSecretKey())
.build()
.parseSignedClaims(token);
return true;
} catch (JwtException e) {
log.error("token: {}", token);
}
verifyWith 에 어떤걸로 서명했는지를 넣어주면 검사 가능 → parser 생성
Jws<Claims> claimsJws = parser.parseSignedClaims(token);
token 문자열을 읽어서 서명 검증, 안에 있는 Claims (payload) 를 꺼냄.
Claims →JWT에 담긴 key-value
JwtException
토큰 자체가 맛이 갔다고(?) 알려주는 에러
Jws<Claims> parsed = Jwts.parser()
.verifyWith(getSecretKey())
.build()
.parseSignedClaims(token);
parsed는 분석되어서 나온 결과
parsed.getPayload().getSubject();
parsed.getPayload().get("Role");
get()안에 키를 넣어주면 내가 원하는 값을 꺼내올 수 있다.
//토큰 생성
public String issue(Long id, Role role) {
String token = Jwts.builder()
.subject(id.toString()) // 페이로드에 제목
.claim("Role", role) // 키, 값
.issuedAt(new Date()) //유효기간 지정 -> 언제 발급 되었는지
.expiration(new Date(new Date().getTime() + jwtConfiguration.getValidation().getExp())) // 만료기간
.signWith(getSecretKey(), Jwts.SIG.HS256) // 서명 -> 어떻게 암호화 할지를 기재
.compact();
return token;
}
이 상태에서는 한 종류의 토큰만 생성이 가능 → refresh Token과 Access Token을 생성하기 위해서는 구조를 바꿔줘야함
@Getter
@RequiredArgsConstructor
public static class Validation {
private final Long access;
private final Long refresh;
}
유효 시간을 다르게 설정해주고
public String issueAccessToken(Long id, Role role) {
return issue(id, role, jwtConfiguration.getValidation().getAccess());
}
public String issueRefreshToken(Long id, Role role) {
return issue(id, role, jwtConfiguration.getValidation().getRefresh());
}
expTime 매개변수를 추가로 받아주고.expiration(new Date(new Date().getTime() + expTime)) 만료 시간을 expTime 으로 설정해준다.successHandler() → 인증이 성공했을 때 어떤 작업을 수행할지
SimpleUrlAuthenticationSuccessHandler 을 상속받는다.
onAuthenticationSuccess 를 오버라이드 해서 인증 성공했을때
FilterChain
Chain을 넣어주지 않으면 중단됨 → doChain이 꼭 필요
success handler부터 멘탈 터져서 작성 불가…ㅎ