# 产生`<saml2:AuthnRequest>`s

如前所述， Spring Security的SAML2.0支持生成一个`<saml2:AuthnRequest>`，以开始与主张方进行身份验证。

Spring 安全性在一定程度上通过在过滤器链中注册`Saml2WebSsoAuthenticationRequestFilter`来实现这一点。默认情况下，此筛选器响应端点`/saml2/authenticate/{registrationId}`。

例如，如果你被部署到`[https://rp.example.com](https://rp.example.com)`，并且你的注册ID为`okta`，那么你可以导航到：

`[https://rp.example.org/saml2/authenticate/ping](https://rp.example.org/saml2/authenticate/ping)`

结果将是一个重定向，该重定向包含一个`SAMLRequest`参数，该参数包含有符号的、已泄气的和编码的`<saml2:AuthnRequest>`。

## 更改`<saml2:AuthnRequest>`的存储方式

`Saml2WebSsoAuthenticationRequestFilter`使用`Saml2AuthenticationRequestRepository`在[将`<saml2:AuthnRequest>`]（# Servlet-saml2login-sp-initiated-factory）发送给断言方前持久化`AbstractSaml2AuthenticationRequest`实例。

此外，`Saml2WebSsoAuthenticationFilter`和`Saml2AuthenticationTokenConverter`使用`Saml2AuthenticationRequestRepository`来加载任何`AbstractSaml2AuthenticationRequest`作为[验证`<saml2:Response>`]（authentication.html# Servlet-saml2login-attenticate-responses）的一部分。

默认情况下， Spring Security使用`HttpSessionSaml2AuthenticationRequestRepository`，它将`AbstractSaml2AuthenticationRequest`存储在`HttpSession`中。

如果你有`Saml2AuthenticationRequestRepository`的自定义实现，则可以通过将其公开为`@Bean`来配置它，如以下示例所示：

Java

```
@Bean
Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository() {
	return new CustomSaml2AuthenticationRequestRepository();
}
```

Kotlin

```
@Bean
open fun authenticationRequestRepository(): Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> {
    return CustomSaml2AuthenticationRequestRepository()
}
```

## 更改`<saml2:AuthnRequest>`的发送方式

默认情况下， Spring Security对每个`<saml2:AuthnRequest>`进行签名，并将其作为get发送给主张方。

许多有主张的当事人不需要签署`<saml2:AuthnRequest>`。这可以通过`RelyingPartyRegistrations`自动配置，也可以手动提供，例如：

例1.不需要签名的authnrequests

引导

```
spring:
  security:
    saml2:
      relyingparty:
        okta:
          identityprovider:
            entity-id: ...
            singlesignon.sign-request: false
```

Java

```
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
        // ...
        .assertingPartyDetails(party -> party
            // ...
            .wantAuthnRequestsSigned(false)
        )
        .build();
```

Kotlin

```
var relyingPartyRegistration: RelyingPartyRegistration =
    RelyingPartyRegistration.withRegistrationId("okta")
        // ...
        .assertingPartyDetails { party: AssertingPartyDetails.Builder -> party
                // ...
                .wantAuthnRequestsSigned(false)
        }
        .build();
```

否则，你将需要为`RelyingPartyRegistration#signingX509Credentials`指定一个私钥，以便 Spring Security可以在发送之前对`<saml2:AuthnRequest>`进行签名。

默认情况下， Spring Security将使用`rsa-sha256`对`<saml2:AuthnRequest>`进行签名，尽管一些主张方将需要不同的算法，如其元数据中所示。

你可以基于断言一方的[元数据使用`RelyingPartyRegistrations`]来配置算法（Overview.html# Servlet-SAML2Login-RelyingPartyRegistrationRepository）。

或者，你可以手动提供它：

Java

```
String metadataLocation = "classpath:asserting-party-metadata.xml";
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
        // ...
        .assertingPartyDetails((party) -> party
            // ...
            .signingAlgorithms((sign) -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512))
        )
        .build();
```

Kotlin

```
var metadataLocation = "classpath:asserting-party-metadata.xml"
var relyingPartyRegistration: RelyingPartyRegistration =
    RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
        // ...
        .assertingPartyDetails { party: AssertingPartyDetails.Builder -> party
                // ...
                .signingAlgorithms { sign: MutableList<String?> ->
                    sign.add(
                        SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512
                    )
                }
        }
        .build();
```

|   |上面的代码片段使用OpenSAML`SignatureConstants`类来提供算法名称。<br/>但是，这只是为了方便。<br/>因为数据类型是`String`，所以你可以直接提供算法的名称。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

一些主张的缔约方要求张贴`<saml2:AuthnRequest>`。这可以通过`RelyingPartyRegistrations`自动配置，也可以手动提供，例如：

Java

```
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
        // ...
        .assertingPartyDetails(party -> party
            // ...
            .singleSignOnServiceBinding(Saml2MessageBinding.POST)
        )
        .build();
```

Kotlin

```
var relyingPartyRegistration: RelyingPartyRegistration? =
    RelyingPartyRegistration.withRegistrationId("okta")
        // ...
        .assertingPartyDetails { party: AssertingPartyDetails.Builder -> party
            // ...
            .singleSignOnServiceBinding(Saml2MessageBinding.POST)
        }
        .build()
```

## 定制OpenSAML的`AuthnRequest`实例

你可能需要调整`AuthnRequest`的原因有很多。例如，你可能希望将`ForceAuthN`设置为`true`，而 Spring Security将其默认设置为`false`。

如果你不需要来自`HttpServletRequest`的信息来做出决定，那么最简单的方法是[用OpenSAML注册一个自定义`AuthnRequestMarshaller`]（overview.html# Servlet-saml2login-opensaml-customization）。这将使你能够在序列化之前访问后处理`AuthnRequest`实例。

但是，如果你确实需要来自该请求的某些内容，那么你可以使用创建一个自定义`Saml2AuthenticationRequestContext`实现，然后创建一个`Converter<Saml2AuthenticationRequestContext, AuthnRequest>`来构建自己的`AuthnRequest`，就像这样：

Java

```
@Component
public class AuthnRequestConverter implements
        Converter<Saml2AuthenticationRequestContext, AuthnRequest> {

    private final AuthnRequestBuilder authnRequestBuilder;
    private final IssuerBuilder issuerBuilder;

    // ... constructor

    public AuthnRequest convert(Saml2AuthenticationRequestContext context) {
        MySaml2AuthenticationRequestContext myContext = (MySaml2AuthenticationRequestContext) context;
        Issuer issuer = issuerBuilder.buildObject();
        issuer.setValue(myContext.getIssuer());

        AuthnRequest authnRequest = authnRequestBuilder.buildObject();
        authnRequest.setIssuer(issuer);
        authnRequest.setDestination(myContext.getDestination());
        authnRequest.setAssertionConsumerServiceURL(myContext.getAssertionConsumerServiceUrl());

        // ... additional settings

        authRequest.setForceAuthn(myContext.getForceAuthn());
        return authnRequest;
    }
}
```

Kotlin

```
@Component
class AuthnRequestConverter : Converter<Saml2AuthenticationRequestContext, AuthnRequest> {
    private val authnRequestBuilder: AuthnRequestBuilder? = null
    private val issuerBuilder: IssuerBuilder? = null

    // ... constructor
    override fun convert(context: Saml2AuthenticationRequestContext): AuthnRequest {
        val myContext: MySaml2AuthenticationRequestContext = context
        val issuer: Issuer = issuerBuilder.buildObject()
        issuer.value = myContext.getIssuer()
        val authnRequest: AuthnRequest = authnRequestBuilder.buildObject()
        authnRequest.issuer = issuer
        authnRequest.destination = myContext.getDestination()
        authnRequest.assertionConsumerServiceURL = myContext.getAssertionConsumerServiceUrl()

        // ... additional settings
        authRequest.setForceAuthn(myContext.getForceAuthn())
        return authnRequest
    }
}
```

然后，可以构造自己的`Saml2AuthenticationRequestContextResolver`和`Saml2AuthenticationRequestFactory`，并将它们发布为`@Bean`s:

Java

```
@Bean
Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver() {
    Saml2AuthenticationRequestContextResolver resolver =
            new DefaultSaml2AuthenticationRequestContextResolver();
    return request -> {
        Saml2AuthenticationRequestContext context = resolver.resolve(request);
        return new MySaml2AuthenticationRequestContext(context, request.getParameter("force") != null);
    };
}

@Bean
Saml2AuthenticationRequestFactory authenticationRequestFactory(
        AuthnRequestConverter authnRequestConverter) {

    OpenSaml4AuthenticationRequestFactory authenticationRequestFactory =
            new OpenSaml4AuthenticationRequestFactory();
    authenticationRequestFactory.setAuthenticationRequestContextConverter(authnRequestConverter);
    return authenticationRequestFactory;
}
```

Kotlin

```
@Bean
open fun authenticationRequestContextResolver(): Saml2AuthenticationRequestContextResolver {
    val resolver: Saml2AuthenticationRequestContextResolver = DefaultSaml2AuthenticationRequestContextResolver()
    return Saml2AuthenticationRequestContextResolver { request: HttpServletRequest ->
        val context = resolver.resolve(request)
        MySaml2AuthenticationRequestContext(
            context,
            request.getParameter("force") != null
        )
    }
}

@Bean
open fun authenticationRequestFactory(
    authnRequestConverter: AuthnRequestConverter?
): Saml2AuthenticationRequestFactory? {
    val authenticationRequestFactory = OpenSaml4AuthenticationRequestFactory()
    authenticationRequestFactory.setAuthenticationRequestContextConverter(authnRequestConverter)
    return authenticationRequestFactory
}
```

[SAML2日志概览](overview.html)[SAML2身份验证响应](authentication.html)