如何在 OpenSaml4AuthenticationProvider 中包含来自旧版 UserDetailsS​​ervice 的用户详细信息?

Dan*_*elf 5 spring-security spring-boot spring-security-saml2

我希望有人能给我一个比我在文档中找到的更具体的例子。

使用 SpringBoot/Spring Security 5.6.0。我正在将基于 SpringSecurity/SAML 的身份验证过程迁移到SAML2

我需要将根据responseToken信息构建的UserDetails添加到身份验证中。就像我们在文档中可以读到的内容: https: //docs.spring.io/spring-security/reference/5.6.0-RC1/servlet/saml2/index.html#servlet-saml2login-opensamlauthenticationprovider-userdetailsservice

但我不明白第三点:返回包含用户详细信息的自定义身份验证:“return MySaml2Authentication(userDetails,authentication);”

在任何情况下,您都必须执行“return new MySaml2Authentication(userDetails,authentication);” 正确的?

无论如何,当该过程继续时,它会被执行:

身份验证authenticate =provider.authenticate(authentication);

我们可以看到,它用原始值替换了“详细信息”值。

authenticationResponse.setDetails(authentication.getDetails());

/**
 * @param authentication the authentication request object, must be of type
 * {@link Saml2AuthenticationToken}
 * @return {@link Saml2Authentication} if the assertion is valid
 * @throws AuthenticationException if a validation exception occurs
 */
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    try {
        Saml2AuthenticationToken token = (Saml2AuthenticationToken) authentication;
        String serializedResponse = token.getSaml2Response();
        Response response = parse(serializedResponse);
        process(token, response);
        AbstractAuthenticationToken authenticationResponse = this.responseAuthenticationConverter
                .convert(new ResponseToken(response, token));
        if (authenticationResponse != null) {
            authenticationResponse.setDetails(authentication.getDetails());
        }
        return authenticationResponse;
    }
    catch (Saml2AuthenticationException ex) {
        throw ex;
    }
    catch (Exception ex) {
        throw createAuthenticationException(Saml2ErrorCodes.INTERNAL_VALIDATION_ERROR, ex.getMessage(), ex);
    }
}
Run Code Online (Sandbox Code Playgroud)

如何添加依赖于从令牌获取的信息的 UserDetails?我无法扩展 OpenSaml4AuthenticationProvider 因为它是“final”

我唯一能想到的是选项:https://docs.spring.io/spring-security/reference/5.6.0-RC1/servlet/saml2/index.html#servlet-saml2login-authenticationmanager-custom

并在 MySaml2AuthenticationManager 中执行身份验证后设置详细信息authenticate =provider.authenticate(authentication); 但这对我来说似乎不对。

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider();

    provider.setResponseAuthenticationConverter(responseToken -> {

        Saml2Authentication auth = OpenSaml4AuthenticationProvider
                .createDefaultResponseAuthenticationConverter()// First, call the default converter, which extracts attributes and authorities from the response
                .convert(responseToken);

        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        String role = getRole(auth.getName());
        grantedAuthorities.add(new SimpleGrantedAuthority(role));


        Saml2Authentication saml2Authentication = new Saml2Authentication((AuthenticatedPrincipal) auth.getPrincipal(), auth.getSaml2Response(), grantedAuthorities);
        
        /*
        //The details are replaced by the authentication.getDetails().
        MyUserDetails userDetails = new MyUserDetails(authenticate.getName(),auth.getPrincipal());
        saml2Authentication.setDetails(userDetails);
        */
        return saml2Authentication;
    });

    Authentication authenticate = provider.authenticate(authentication);

    //Doesn't sound like a good idea
    if ((authenticate.getPrincipal() instanceof DefaultSaml2AuthenticatedPrincipal)) {
        DefaultSaml2AuthenticatedPrincipal samlPrincipal = (DefaultSaml2AuthenticatedPrincipal) authenticate.getPrincipal();
        MyUserDetails userDetails = new MyUserDetails(authenticate.getName(),
                samlPrincipal.getFirstAttribute("SMFIRSTNAME")
                , samlPrincipal.getFirstAttribute("SMLASTNAME")
                , samlPrincipal.getFirstAttribute("SMEMAIL"));
        ((Saml2Authentication)authenticate).setDetails(defaultDISUserDetails);
    }
    return authenticate;
Run Code Online (Sandbox Code Playgroud)

还有更好的选择吗?

非常感谢您的帮助

此致

小智 1

我想通这一点也太晚了。但您实际上应该使用自己的 AbstractAuthenticationToken 实现,并使用额外的字段来填充您自己的对象。这是我的:

public class CustomSaml2Authentication extends Saml2Authentication {

private User user;

public CustomSaml2Authentication(AuthenticatedPrincipal principal, String saml2Response,
        Collection<? extends GrantedAuthority> authorities) {
    super(principal, saml2Response, authorities);
}

public User getUser() {
    return user;
}

public void setUser(User user) {
    this.user = user;
}

}
Run Code Online (Sandbox Code Playgroud)