1. Обзор
В этом кратком руководстве мы будем работать с реализацией Spring Security OAuth2 и узнаем, как проверять утверждения JWT с помощью нового JwtClaimsSetVerifier,
представленного в Spring Security OAuth 2.2.0.RELEASE .
2. Конфигурация Maven
Во- первых, нам нужно добавить последнюю версию spring-security-oauth2
в наш pom.xml
:
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
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
см. в статье об использовании JWT с Spring Security OAuth .
Теперь, в следующих разделах, мы обсудим различные типы верификаторов утверждений и то, как заставить их работать вместе.
4. Проверка Заявления Эмитента
Мы начнем с простого — с проверки утверждения эмитента « 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<String, Object> 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
, а затем предоставляем полностью настраиваемую реализацию метода проверки, что дает нам полную гибкость для любого вида проверки, который нам нужен.
6. Объедините несколько верификаторов утверждений
Наконец, давайте посмотрим, как объединить несколько верификаторов утверждений с помощью DelegatingJwtClaimsSetVerifier
— следующим образом:
@Bean
public JwtClaimsSetVerifier jwtClaimsSetVerifier() {
return new DelegatingJwtClaimsSetVerifier(Arrays.asList(
issuerClaimVerifier(), customJwtClaimVerifier()));
}
DelegatingJwtClaimsSetVerifier
берет список объектов JwtClaimsSetVerifier
и делегирует процесс проверки утверждений этим верификаторам.
7. Простой интеграционный тест
Теперь, когда мы закончили с реализацией, давайте проверим наши верификаторы утверждений с помощью простого интеграционного теста :
@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.
Как всегда, полный исходный код доступен на GitHub .