Spring Sercurity Oauth2 에서 인증방법 중 jwt인증을 쉽게하는 방법이 있다. @EnableAuthorizationServer 를 설정하는 곳에서 JwtAccessTokenConverter 를 추가하면 된다. 설정은 다음과 같다.
...
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
...
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
...
endpoints.tokenStore(tokenStore());
endpoints.accessTokenConverter(accessTokenConverter());
}
...
private JwtAccessTokenConverter accessTokenConverter () {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
return converter;
}
이렇게 한 뒤 token을 요청하면 다음처럼 결과를 받는다.
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjY0NTI2MTAsInVzZXJfbmFtZSI6IlRSQUlORUUtMDEwMTIzNCIsImF1dGhvcml0aWVzIjpbIlJPTEVfVFJBSU5FRSJdLCJqdGkiOiI1NmYxNzNhYi0wNjVhLTQ2MDQtODZjZC1lOWViNzRkMWIzOWMiLCJjbGllbnRfaWQiOiJlZGl5YU9hdXRoMlNlcnZpY2UiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXX0.Ac8FlIDOPitYRAoFmr-8k-cR1BPrpw7j9EIOYsToLnQ",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJUUkFJTkVFLTAxMDEyMzQiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiNTZmMTczYWItMDY1YS00NjA0LTg2Y2QtZTllYjc0ZDFiMzljIiwiZXhwIjoxNjI5MDAxNDEwLCJhdXRob3JpdGllcyI6WyJST0xFX1RSQUlORUUiXSwianRpIjoiOTE1OTg3YjUtZWM4Zi00MmJjLTkwMGQtNDNjYTJmNGViM2I4IiwiY2xpZW50X2lkIjoiZWRpeWFPYXV0aDJTZXJ2aWNlIn0.FuvzRDIJGaWzo5wQoocw9BR4x3dLAwVJOljldS5_HhA",
"expires_in": 43199,
"scope": "read write",
"jti": "56f173ab-065a-4604-86cd-e9eb74d1b39c"
}
그럼 이것을 가지고 인증페이지를 접속해보았다.여전히 Bearer 방식이므로 Header Authorization 에 값을 넣을때 포함해야 한다.
curl --location --request GET 'http://localhost:10000/api/token/valid' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjY0NTI2MTAsInVzZXJfbmFtZSI6IlRSQUlORUUtMDEwMTIzNCIsImF1dGhvcml0aWVzIjpbIlJPTEVfVFJBSU5FRSJdLCJqdGkiOiI1NmYxNzNhYi0wNjVhLTQ2MDQtODZjZC1lOWViNzRkMWIzOWMiLCJjbGllbnRfaWQiOiJlZGl5YU9hdXRoMlNlcnZpY2UiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXX0.Ac8FlIDOPitYRAoFmr-8k-cR1BPrpw7j9EIOYsToLnQ' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=DD49D822503EE7270B94A65CE54EC97F' \
--data-raw '{}'
문제는 다음과 같은 에러가 발생했다.
{
"error": "invalid_token",
"error_description": "Cannot convert access token to JSON"
}
그래서 해당 오류를 발생시키는 파일을 찾아갔는데, decode 하면서 NullPointException이 발생한 것이다. 에러문구가 제대로 표기되지 않아 한참을 찾았는데 알고보니 verifier 에 null 로 들어가 있어서 그랬다.
그럼 verifier 에는 왜 Null이 들어있을까? JwtAccessTokenConverter 클래스에서 keyPair를 생성하면서 삽입되는데 내 경우 그것을 생략해서 그렇다. 아래 사이트 경우 keyPair를 구성하여 삽입하는 구절이 있다.
https://yookeun.github.io/java/2017/07/23/spring-jwt/
그런데 내 경우는 KeyPair 를 삽입하지 않기 때문에 다른방법을 사용해야 했다. 확인해본 결과 afterPropertiesSet 를 실행함으로써 verifier 에 삽입해야 한다.
public JwtAccessTokenConverter accessTokenConverter () {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
try {
converter.afterPropertiesSet();
} catch (Exception e) {
e.printStackTrace();
}
return converter;
}
아래는 afterPropertiesSet의 내부구조다
...
public void afterPropertiesSet() throws Exception {
if (verifier != null) {
// Assume signer also set independently if needed
return;
}
SignatureVerifier verifier = new MacSigner(verifierKey);
try {
verifier = new RsaVerifier(verifierKey);
}
catch (Exception e) {
logger.warn("Unable to create an RSA verifier from verifierKey (ignoreable if using MAC)");
}
// Check the signing and verification keys match
if (signer instanceof RsaSigner) {
byte[] test = "test".getBytes();
try {
verifier.verify(test, signer.sign(test));
logger.info("Signing and verification RSA keys match");
}
catch (InvalidSignatureException e) {
logger.error("Signing and verification RSA keys do not match");
}
}
else if (verifier instanceof MacSigner) {
// Avoid a race condition where setters are called in the wrong order. Use of
// == is intentional.
Assert.state(this.signingKey == this.verifierKey,
"For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key");
}
this.verifier = verifier;
}
...
그런데 이 설정이 문제인 것은 새로 시작할때마다 signingKey 가 랜덤으로 생성된다는 것이다. 이럴경우 이전에 발행된 키가 모두 무용지물이 될 수 있기 때문에 위험할 수 있다. 때문에 키를 설정해주는 것이 좋다. 아래 사진을 보면 verifierKey 가 랜덤으로 생성된 것이다
때문에 key를 생성해주는게 좋다. 다음 설정을 추가해주자.
public JwtAccessTokenConverter accessTokenConverter () {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("12345");
try {
converter.afterPropertiesSet();
} catch (Exception e) {
e.printStackTrace();
}
return converter;
}
그럼 이제 호출해보면 잘 통과되는걸 확인할 수 있었다.
끝.
'공부 > 프로그래밍' 카테고리의 다른 글
[React, PWA] 클라이언트에서 웹 푸시(fcm) 설정하기 (0) | 2021.07.28 |
---|---|
[springboot] feign 설정하기 (0) | 2021.07.26 |
[postman] 결과값 변수에 자동 설정하기 (0) | 2021.07.02 |
[java] 요청한 IP주소 받기(nginx proxy 환경, AWS ELB 등) (0) | 2021.07.02 |
[springboot, jwt] JWT 사용하기 (0) | 2021.06.23 |
댓글