Spring Security 2장 시큐리트 버젼 업그레이드

버젼 업그레이드

최근 스프링부트 메이져 버젼을 업데이트 하며 가장 고생하였던 부분이 있어 집에서 간단하게 정리하고자 샘플 프로젝트를 만들었다. 스프링 부트 버젼 업데이트하며 세션에 저장되어 있는 값이 deserialize가 안되는 현상이 발생하였기 때문이다.

구성 및 테스트 환경 구성

1장에서 스프링부트 버젼을 2.1.5과 함께 셋팅을 하였다. 1장 링크

필수 체크 사항

  1. 인터넷 브라우저에서 localhost:8080/users
  2. 개발자도구 네트워크 탭 확인
  3. 오른쪽 마우스 버튼 누르고 copy as cURL

1

  1. postman실행 왼쪽 상단 import 버튼 클릭
  2. paste Raw text에서 import클릭

2

  1. postman에서 send를 누르게 되면 1번과 동일한 값을 받을 수 있다.

위의 모든 작업들이 끝나고 아래 build.gradle변경

build.gradle

2.1.5를 주석 후 2.2.4의 주석을 풀어준다

1
2
3
4
5
6
plugins {
// id 'org.springframework.boot' version '2.1.5.RELEASE'
id 'org.springframework.boot' version '2.2.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}

부트 실행

  1. 2.2.4환경에서 실행 후
  2. 동일하게 포스트맨으로 send
  3. 아래의 에러 로그 확인
1
2

org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.InvalidClassException: org.springframework.security.core.context.SecurityContextImpl; local class incompatible: stream classdesc serialVersionUID = 510, local class serialVersionUID = 520

SecurityContextImpl; local class incompatible: stream classdesc serialVersionUID = 510, local class serialVersionUID = 520

510???? 520??? 무슨 차이?

SecurityContextImpl.java

아래의 파일에서 SerialVersionId가 스프링시큐리티 코어 버젼에 따라 주입됨을 볼수가 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class SecurityContextImpl implements SecurityContext {

private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

// ~ Instance fields
// ================================================================================================

private Authentication authentication;

public SecurityContextImpl() {}

public SecurityContextImpl(Authentication authentication) {
this.authentication = authentication;
}

// ~ Methods
// ========================================================================================================

@Override
public boolean equals(Object obj) {
if (obj instanceof SecurityContextImpl) {
SecurityContextImpl test = (SecurityContextImpl) obj;

if ((this.getAuthentication() == null) && (test.getAuthentication() == null)) {
return true;
}

if ((this.getAuthentication() != null) && (test.getAuthentication() != null)
&& this.getAuthentication().equals(test.getAuthentication())) {
return true;
}
}

return false;
}

@Override
public Authentication getAuthentication() {
return authentication;
}

@Override
public int hashCode() {
if (this.authentication == null) {
return -1;
}
else {
return this.authentication.hashCode();
}
}

@Override
public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());

if (this.authentication == null) {
sb.append(": Null authentication");
}
else {
sb.append(": Authentication: ").append(this.authentication);
}

return sb.toString();
}
}

SpringSecurityCoreVersion.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

public class SpringSecurityCoreVersion {
private static final String DISABLE_CHECKS = SpringSecurityCoreVersion.class.getName()
.concat(".DISABLE_CHECKS");

private static final Log logger = LogFactory.getLog(SpringSecurityCoreVersion.class);

/**
* Global Serialization value for Spring Security classes.
*
* N.B. Classes are not intended to be serializable between different versions. See
* SEC-1709 for why we still need a serial version.
*/
public static final long SERIAL_VERSION_UID = 520L;

static final String MIN_SPRING_VERSION = getSpringVersion();

static {
performVersionChecks();
}

public static String getVersion() {
Package pkg = SpringSecurityCoreVersion.class.getPackage();
return (pkg != null ? pkg.getImplementationVersion() : null);
}

/**
* Performs version checks
*/
private static void performVersionChecks() {
performVersionChecks(MIN_SPRING_VERSION);
}

/**
* Perform version checks with specific min Spring Version
*
* @param minSpringVersion
*/
private static void performVersionChecks(String minSpringVersion) {
if (minSpringVersion == null) {
return;
}
// Check Spring Compatibility
String springVersion = SpringVersion.getVersion();
String version = getVersion();

if (disableChecks(springVersion, version)) {
return;
}

logger.info("You are running with Spring Security Core " + version);
if (new ComparableVersion(springVersion)
.compareTo(new ComparableVersion(minSpringVersion)) < 0) {
logger.warn("**** You are advised to use Spring " + minSpringVersion
+ " or later with this version. You are running: " + springVersion);
}
}

/**
* Disable if springVersion and springSecurityVersion are the same to allow working
* with Uber Jars.
*
* @param springVersion
* @param springSecurityVersion
* @return
*/
private static boolean disableChecks(String springVersion,
String springSecurityVersion) {
if (springVersion == null || springVersion.equals(springSecurityVersion)) {
return true;
}
return Boolean.getBoolean(DISABLE_CHECKS);
}

/**
* Loads the spring version or null if it cannot be found.
* @return
*/
private static String getSpringVersion() {
Properties properties = new Properties();
try {
properties.load(SpringSecurityCoreVersion.class.getClassLoader().getResourceAsStream("META-INF/spring-security.versions"));
} catch (IOException | NullPointerException e) {
return null;
}
return properties.getProperty("org.springframework:spring-core");
}
}

결론

  1. 세션에 userDetail정보가 저장될 때 별도의 userDetail을 Json으로 저장하도록 구현하지 않으면… 마이그레이션이 힘들다.
  2. 아무 생각 없이 세션을 쓰고 있는 프로젝트에서 스프링 시큐리티 버젼을 올릴 경우 위의 에러 문구를 만날수 있다.
  3. 스프링시큐리티코어(스프링프레임워크 버젼) 버젼에 따라 SERIAL_VERSION_UID이 바뀜을 인지하자.

찾다보니 우아한형제 블로그에서 자바 직렬화 관련 자세한 설명이 나와 있다.

우형 기술블로그 자바 직렬화