Новое в Spring Security OAuth2 - Проверка заявок
1. обзор
В этом кратком руководстве мы будем работать с реализацией Spring Security OAuth2 и узнаем, как проверять утверждения JWT с помощью новогоJwtClaimsSetVerifier, представленного вSpring Security OAuth 2.2.0.RELEASE.
2. Конфигурация Maven
Во-первых, нам нужно добавить последнюю версиюspring-security-oauth2 в нашpom.xml:
org.springframework.security.oauth
spring-security-oauth2
2.2.0.RELEASE
3. Конфигурация хранилища токенов
Затем давайте настроим нашTokenStore на сервере ресурсов:
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
converter.setJwtClaimsSetVerifier(jwtClaimsSetVerifier());
return converter;
}
Обратите внимание, как мы добавляем новый верификатор к нашемуJwtAccessTokenConverter.
Для получения дополнительных сведений о том, как настроитьJwtTokenStore, ознакомьтесь с описаниемusing JWT with Spring Security OAuth.
Теперь, в следующих разделах, мы обсудим различные типы верификаторов заявок и способы их совместной работы.
4. IssuerClaimVerifierс
Начнем с простого - с проверки утверждения эмитента «iss» с помощьюIssuerClaimVerifier - следующим образом:
@Bean
public JwtClaimsSetVerifier issuerClaimVerifier() {
try {
return new IssuerClaimVerifier(new URL("http://localhost:8081"));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
В этом примере мы добавили простойIssuerClaimVerifier для проверки нашего эмитента. Если токен JWT содержит другое значение для утверждения «iss» эмитента, будет выдан простойInvalidTokenException.
Естественно, если токен содержит утверждение эмитента «iss», исключение не будет выдано, и токен будет считаться действительным.
5. Пользовательский верификатор утверждений
Но что интересно, мы также можем создать собственный верификатор утверждений:
@Bean
public JwtClaimsSetVerifier customJwtClaimVerifier() {
return new CustomClaimVerifier();
}
Вот простая реализация того, как это может выглядеть - проверить, существует ли утверждениеuser_name в нашем токене JWT:
public class CustomClaimVerifier implements JwtClaimsSetVerifier {
@Override
public void verify(Map claims) throws InvalidTokenException {
String username = (String) claims.get("user_name");
if ((username == null) || (username.length() == 0)) {
throw new InvalidTokenException("user_name claim is empty");
}
}
}
Обратите внимание, как мы просто реализуем здесь интерфейсJwtClaimsSetVerifier, а затем предоставляем полностью настраиваемую реализацию для метода verify, что дает нам полную гибкость для любого вида проверки, которая нам нужна.
6. Объедините несколько средств проверки утверждений
Наконец, давайте посмотрим, как объединить несколько проверяющих утверждений с помощьюDelegatingJwtClaimsSetVerifier - следующим образом:
@Bean
public JwtClaimsSetVerifier jwtClaimsSetVerifier() {
return new DelegatingJwtClaimsSetVerifier(Arrays.asList(
issuerClaimVerifier(), customJwtClaimVerifier()));
}
DelegatingJwtClaimsSetVerifier принимает список объектовJwtClaimsSetVerifier и делегирует этим верификаторам процесс проверки утверждения.
7. Простой интеграционный тест
Теперь, когда мы закончили реализацию, давайте протестируем наши верификаторы утверждений с помощью простогоintegration test:
@RunWith(SpringRunner.class)
@SpringBootTest(
classes = ResourceServerApplication.class,
webEnvironment = WebEnvironment.RANDOM_PORT)
public class JwtClaimsVerifierIntegrationTest {
@Autowired
private JwtTokenStore tokenStore;
...
}
Мы начнем с токена, который не содержит эмитента (но содержитuser_name) - который должен быть действительным:
@Test
public void whenTokenDontContainIssuer_thenSuccess() {
String tokenValue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....";
OAuth2Authentication auth = tokenStore.readAuthentication(tokenValue);
assertTrue(auth.isAuthenticated());
}
Причина, по которой это действительно так, проста - первый верификатор активен только в том случае, если в токене есть утверждение эмитента. Если этого утверждения не существует - проверяющий не вмешивается.
Затем давайте посмотрим на токен, который содержит действительного эмитента (http://localhost:8081), а такжеuser_name. Это также должно быть действительным:
@Test
public void whenTokenContainValidIssuer_thenSuccess() {
String tokenValue = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9....";
OAuth2Authentication auth = tokenStore.readAuthentication(tokenValue);
assertTrue(auth.isAuthenticated());
}
Если токен содержит недопустимого эмитента (http://localhost:8082), он будет проверен и признан недействительным:
@Test(expected = InvalidTokenException.class)
public void whenTokenContainInvalidIssuer_thenException() {
String tokenValue = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9....";
OAuth2Authentication auth = tokenStore.readAuthentication(tokenValue);
assertTrue(auth.isAuthenticated());
}
Затем, если токен не содержит утвержденияuser_name, он будет недействительным:
@Test(expected = InvalidTokenException.class)
public void whenTokenDontContainUsername_thenException() {
String tokenValue = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9....";
OAuth2Authentication auth = tokenStore.readAuthentication(tokenValue);
assertTrue(auth.isAuthenticated());
}
И, наконец, когда токен содержит пустое утверждениеuser_name, он также недействителен:
@Test(expected = InvalidTokenException.class)
public void whenTokenContainEmptyUsername_thenException() {
String tokenValue = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9....";
OAuth2Authentication auth = tokenStore.readAuthentication(tokenValue);
assertTrue(auth.isAuthenticated());
}
8. Заключение
В этой быстрой статье мы рассмотрели новые функции верификатора в Spring Security OAuth.
Как всегда, доступен полный исходный кодover on GitHub.