servlet-saml2-login-authentication-requests.md 10.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
# 产生`<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)