1. 概述
本快速教程將介紹 Spring Security OAuth2 的實現,並學習如何使用新的 JwtClaimsSetVerifier 來驗證 JWT 聲明——該功能是在 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. 令牌存儲配置
接下來,讓我們配置 Resource Server 中的 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. 發行人聲明驗證器 (IssuerClaimVerifier)
我們將從簡單開始——通過使用 IssuerClaimVerifier 來驗證發行人聲明 "iss":
@Bean
public JwtClaimsSetVerifier issuerClaimVerifier() {
try {
return new IssuerClaimVerifier(new URL("http://localhost:8081"));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}在本示例中,我們添加了一個簡單的 IssuerClaimVerifier 用於驗證我們的 issuer。如果 JWT 令牌包含 issuer “iss” 聲明的不同值,則會拋出一個簡單的 InvalidTokenException 異常。
自然地,如果令牌確實包含 issuer “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 接口,然後提供了一個完全自定義的 verify 方法的實現——這為我們提供了對任何需要的檢查類型進行完全靈活性的能力。
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 中新引入的驗證器功能。