#spring-security #jwt #spring-security-oauth2
#spring-security #jwt #spring-security-oauth2
Вопрос:
Хотя у нас есть решение для этого, мы хотим знать, есть ли решение получше, потому что наше уродливое и кажется очень неправильным. Итак, кто-нибудь знает о лучшем решении с использованием Spring Security?
Проблема
Мы поддерживаем приложение, которое управляет учетными записями пользователей. При входе в систему мы создаем два JWT — токен доступа и токен обновления. Мы хотели бы, чтобы полезная нагрузка токена доступа содержала другой набор полей, чем токен обновления. Например, если полезная нагрузка токена доступа равна:
{
"a" : "foo",
"b" : "bar",
}
мы хотели бы, чтобы полезная нагрузка токена обновления была:
{
"a" : "foo",
"x" : "baz",
}
Что мы сделали
Единственное решение, которое мы могли придумать, — это реализовать пользовательский усилитель токена, который расширяет JwtAccessTokenConverter
и переопределяет enhance
метод таким образом, что содержимое метода суперкласса немного модифицируется для управления содержимым каждой полезной нагрузки. Вот код:
public class CustomJwtAccessTokenConverter extends JwtAccessTokenConverter {
@Override
public OAuth2AccessToken enhance(
OAuth2AccessToken accessToken,
OAuth2Authentication authentication
) {
DefaultOAuth2AccessToken result = new DefaultOAuth2AccessToken(accessToken);
Map<String, Object> info = new LinkedHashMap<>(accessToken.getAdditionalInformation());
// <custom-code>
info.remove("x");
// </custom-code>
String tokenId = result.getValue();
if (!info.containsKey(TOKEN_ID)) {
info.put(TOKEN_ID, tokenId);
} else {
tokenId = (String) info.get(TOKEN_ID);
}
result.setAdditionalInformation(info);
result.setValue(encode(result, authentication));
OAuth2RefreshToken refreshToken = result.getRefreshToken();
if (refreshToken != null) {
DefaultOAuth2AccessToken encodedRefreshToken = new DefaultOAuth2AccessToken(accessToken);
encodedRefreshToken.setValue(refreshToken.getValue());
// Refresh tokens do not expire unless explicitly of the right type
encodedRefreshToken.setExpiration(null);
try {
Map<String, Object> claims = jsonParser
.parseMap(JwtHelper.decode(refreshToken.getValue()).getClaims());
if (claims.containsKey(TOKEN_ID)) {
encodedRefreshToken.setValue(claims.get(TOKEN_ID).toString());
}
} catch (IllegalArgumentException e) {
}
Map<String, Object> refreshTokenInfo = new LinkedHashMap<>(
accessToken.getAdditionalInformation());
// <custom-code>
refreshTokenInfo.remove("b");
// </custom-code>
refreshTokenInfo.put(TOKEN_ID, encodedRefreshToken.getValue());
refreshTokenInfo.put(ACCESS_TOKEN_ID, tokenId);
encodedRefreshToken.setAdditionalInformation(refreshTokenInfo);
DefaultOAuth2RefreshToken token = new DefaultOAuth2RefreshToken(
encode(encodedRefreshToken, authentication));
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
Date expiration = ((ExpiringOAuth2RefreshToken) refreshToken).getExpiration();
encodedRefreshToken.setExpiration(expiration);
token = new DefaultExpiringOAuth2RefreshToken(encode(encodedRefreshToken, authentication), expiration);
}
result.setRefreshToken(token);
}
return resu<
}
}
Контекст для разных полезных нагрузок
У нас есть мобильное приложение, которому необходимо передать свои токены доступа и обновления в веб-представление. Приложение, запущенное в веб-представлении, не имеет резервного сервера (приложение React Javascript, обслуживаемое с CDN). Наши токены доступа и обновления содержат вложенные токены, выпущенные из отдельной системы единого входа, в которой мы фактически являемся расширенным прокси. В результате токены, которые мы выпускаем, довольно большие — я сгенерировал токен доступа на 2000 символов в моем собственном тестировании. Чтобы передать токены в веб-представление, мы передаем их через параметр запроса. Поскольку некоторые браузеры имеют ограничения по URL, мы хотим, чтобы токен доступа единого входа присутствовал только в нашем токене доступа, а токен обновления единого входа присутствовал только в нашем токене обновления, поскольку это уменьшит раздувание URL-адреса — токены доступа единого входа являются полноценными JWT, в то время как их токены обновления являются простыми идентификаторами GUID.
Мы знаем, что передача JWT через параметр запроса не является лучшей практикой, и прочитали RFC. У нас жесткие сроки, и это было признано самым быстрым возможным решением. Прямо сейчас мы разрабатываем микросервис обмена токенами, чтобы обойти это в будущем.
Комментарии:
1. Чтобы включить разные полезные нагрузки в
access
иrefresh
токен с этой библиотекой, вы следовали подходящему подходу, то есть создали свой пользовательскийJwtAccessTokenConverter
. Возможно, этот пример поможет вам сделать это более чистым способом: github.com/doctore/Spring5Microservices/blob/master /…