到目前为止,在我的应用程序中,我已经使用OAuth2密码授予流为使用Spring Security和Spring OAuth向提供其用户名和密码的客户端生成JWT访问令牌。然后,他们在对我的Spring Boot REST API的所有请求中都使用该令牌。
我的一些客户现在想改用SAML身份验证。我的想法是创建一个单独的端点/saml/accessToken
并使用Spring SAML对其进行保护。完成SAML身份验证后,将使用有效的身份验证将用户重定向回/saml/accessToken
,并为用户提供一个JWT,客户端可以使用该JWT与我的REST API进行进一步的通信。
我需要一个控制器方法,该方法接受经过身份验证的SAMLAuthenticationToken,使用其凭据生成JWT,并将其返回给客户端:
@RequestMapping(value = "/saml/accessToken")
public String getAccessToken(SAMLAuthenticationToken authentication) {
return accessTokenFactory.create(authentication);
}
这是我需要帮助的上述示例中的accessTokenFactory
。我想尽可能地遵循Spring编码生态系统,并避免使用“ hack”解决方案,以便可以利用现有的TokenEnhancers等。
从SAMLAuthenticationToken创建JWT访问令牌的最佳方法是什么?
答案 0 :(得分:1)
事实证明,由成功的SAML身份验证流产生的SAML cookie的Authentication
对象实际上是ExpiringUsernameAuthenticationToken
,而不是SAMLAuthenticationToken
。 ExpiringUsernameAuthenticationToken#principal
是User
的实现(我们称其为CustomerUser
),是我在SAMLUserDetailsService
实现中的SAML身份验证期间设置的,它与User
的类型相同我在OAuth2密码授予流程中使用的密码。
由于我找不到使用默认的Spring OAuth方法为ExpiringUsernameAuthenticationToken
创建JWT的任何方法,因此我最终使用JwtFactory#create(ExpiringUsernameAuthenticationToken)
编写了单独的jjwt
。这样就可以得到一种干净且容易的解决方案。
这样做的主要缺点是JwtFactory
无法利用我的TokenEnhancer
bean,它们负责向JWT添加附加参数。因此,在TokenEnhancer
(由Spring OAuth使用)和JwtFactory
(在SAML身份验证之后手动使用)中都存在用于添加其他JWT参数的代码和逻辑复制的som级。这是应避免的代码气味。但是将Spring OAuth特定功能入侵我的自定义JwtFactory
似乎更加糟糕,所以这是我必须忍受的。
答案 1 :(得分:0)
我已经尝试过下面的代码,它对我有用。
SAML (Okta) 登录成功后,用户重定向到我们在 SAML 配置类中配置的以下方法
@Bean
@Qualifier("saml")
public SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() {
SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successRedirectHandler.setDefaultTargetUrl("/oauth/saml/token");
return successRedirectHandler;
}
@RequestMapping("/oauth/saml/token")
public String home(ExpiringUsernameAuthenticationToken userToken) throws JsonProcessingException {
SAMLCredential credential = (SAMLCredential) userToken.getCredentials();
Map<String, Object> map = new HashMap<>();
map.put("group", credential.getAttributeAsStringArray("group"));
Assertion authenticationAssertion = credential.getAuthenticationAssertion();
String access_token = getSamlJWTToken(authenticationAssertion, map);
map.put("access_token", access_token);
ObjectMapper mapper = new ObjectMapper();
String token = mapper.writeValueAsString(map);
return token;
}
private String getSamlJWTToken(Assertion authenticationAssertion, Map<String, Object> map) {
String SECRET_KEY = Base64.getUrlEncoder().encodeToString(samlKeystorePassword.getBytes());
String id = authenticationAssertion.getID();
String issuer = authenticationAssertion.getIssuer().getValue();
String subject = authenticationAssertion.getSubject().getNameID().getValue();
DateTime notBefore = authenticationAssertion.getConditions().getNotBefore();
DateTime notOnOrAfter = authenticationAssertion.getConditions().getNotOnOrAfter();
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET_KEY);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
JwtBuilder builder = Jwts.builder()
.setClaims(map)
.setId(id)
.setIssuedAt(notBefore.toDate())
.setSubject(subject)
.setIssuer(issuer)
.signWith(signatureAlgorithm, signingKey)
.setExpiration(notOnOrAfter.toDate());
return builder.compact();
}