我将Spring安全应用程序插入IDP / OP( IDe ntity P rovider,或 O penid连接身份 P rovider根据OpenID连接术语)
我正在使用授权代码流程。 我用这个实现来启动我的代码: https://github.com/gazbert/openid-connect-spring-client
它正在使用几个IDP,直到我找到一个需要nonce参数的IDP。 但是我无法设置我的应用程序生成一个nonce,并将其添加到url中(我知道这是nonce,因为当我手动添加它时:它工作)
当应用程序将用户重定向到我希望拥有nonce的IDP(授权端点)时。 如果可以在返回时验证nonce,那将是完美的。
我在网上搜索了2个小时,我发现这可能是使用的东西 org.springframework.security.oauth.provider.nonce 但没有找到任何例子,或者如何在我的代码中添加它的线索
以下是代码中有趣的部分,我认为我必须告诉Spring使用nonce:
public OAuth2RestTemplate getOpenIdConnectRestTemplate(@Qualifier("oauth2ClientContext")
OAuth2ClientContext clientContext) {
return new OAuth2RestTemplate(createOpenIdConnectCodeConfig(), clientContext);
}
public OAuth2ProtectedResourceDetails createOpenIdConnectCodeConfig() {
final AuthorizationCodeResourceDetails resourceDetails = new AuthorizationCodeResourceDetails();
resourceDetails.setClientAuthenticationScheme(AuthenticationScheme.form); // include client credentials in POST Content
resourceDetails.setClientId(clientId);
resourceDetails.setClientSecret(clientSecret);
resourceDetails.setUserAuthorizationUri(authorizationUri);
resourceDetails.setAccessTokenUri(tokenUri);
final List<String> scopes = new ArrayList<>();
scopes.add("openid"); // always need this
scopes.addAll(Arrays.asList(optionalScopes.split(",")));
resourceDetails.setScope(scopes);
resourceDetails.setPreEstablishedRedirectUri(redirectUri);
resourceDetails.setUseCurrentUri(false);
return resourceDetails;
}
如果有修改我相信它就在那里。 如果这是重复的我道歉,我永远不会再羞辱自己。
任何帮助将不胜感激,我可以发布更多细节,如果需要,我不想通过张贴太多来混淆
感谢您的阅读
答案 0 :(得分:1)
我也为此感到挣扎。幸运的是,Spring Security documentation中有一些最新的开发,在与一位GitHub开发人员来回交流后,我想到了Kotlin的解决方案(转换成Java应该相当容易)。可以在here中找到原始讨论。
最终,我的SecurityConfig
类最终看起来像这样:
@EnableWebSecurity
class SecurityConfig @Autowired constructor(loginGovConfiguration: LoginGovConfiguration) : WebSecurityConfigurerAdapter() {
@Autowired
lateinit var clientRegistrationRepository: ClientRegistrationRepository
private final val keystore: MutableMap<String, String?> = loginGovConfiguration.keystore
private final val keystoreUtil: KeystoreUtil = KeystoreUtil(
keyStore = keystore["file"],
keyStorePassword = keystore["password"],
keyAlias = keystore["alias"],
keyPassword = null,
keyStoreType = keystore["type"]
)
private final val allowedOrigin: String = loginGovConfiguration.allowedOrigin
companion object {
const val LOGIN_ENDPOINT = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL
const val LOGIN_SUCCESS_ENDPOINT = "/login_success"
const val LOGIN_FAILURE_ENDPOINT = "/login_failure"
const val LOGIN_PROFILE_ENDPOINT = "/login_profile"
const val LOGOUT_ENDPOINT = "/logout"
const val LOGOUT_SUCCESS_ENDPOINT = "/logout_success"
}
override fun configure(http: HttpSecurity) {
http.authorizeRequests()
// login, login failure, and index are allowed by anyone
.antMatchers(
LOGIN_ENDPOINT,
LOGIN_SUCCESS_ENDPOINT,
LOGIN_PROFILE_ENDPOINT,
LOGIN_FAILURE_ENDPOINT,
LOGOUT_ENDPOINT,
LOGOUT_SUCCESS_ENDPOINT,
"/"
)
.permitAll()
// any other requests are allowed by an authenticated user
.anyRequest()
.authenticated()
.and()
// custom logout behavior
.logout()
.logoutRequestMatcher(AntPathRequestMatcher(LOGOUT_ENDPOINT))
.logoutSuccessUrl(LOGOUT_SUCCESS_ENDPOINT)
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.logoutSuccessHandler(LoginGovLogoutSuccessHandler())
.and()
// configure authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider
.oauth2Login()
.authorizationEndpoint()
.authorizationRequestResolver(LoginGovAuthorizationRequestResolver(clientRegistrationRepository))
.authorizationRequestRepository(authorizationRequestRepository())
.and()
.tokenEndpoint()
.accessTokenResponseClient(accessTokenResponseClient())
.and()
.failureUrl(LOGIN_FAILURE_ENDPOINT)
.successHandler(LoginGovAuthenticationSuccessHandler())
}
@Bean
fun corsFilter(): CorsFilter {
// fix OPTIONS preflight login profile request failure with 403 Invalid CORS request
val config = CorsConfiguration()
config.addAllowedOrigin(allowedOrigin)
config.allowCredentials = true
config.allowedHeaders = listOf("x-auth-token", "Authorization", "cache", "Content-Type")
config.addAllowedMethod(HttpMethod.OPTIONS)
config.addAllowedMethod(HttpMethod.GET)
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration(LOGIN_PROFILE_ENDPOINT, config)
return CorsFilter(source)
}
@Bean
fun authorizationRequestRepository(): AuthorizationRequestRepository<OAuth2AuthorizationRequest> {
return HttpSessionOAuth2AuthorizationRequestRepository()
}
@Bean
fun accessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {
val accessTokenResponseClient = DefaultAuthorizationCodeTokenResponseClient()
accessTokenResponseClient.setRequestEntityConverter(LoginGovTokenRequestConverter(clientRegistrationRepository, keystoreUtil))
return accessTokenResponseClient
}
}
还有我的自定义授权解析器LoginGovAuthorizationRequestResolver
:
class LoginGovAuthorizationRequestResolver(clientRegistrationRepository: ClientRegistrationRepository) : OAuth2AuthorizationRequestResolver {
private val REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId"
private var defaultAuthorizationRequestResolver: OAuth2AuthorizationRequestResolver = DefaultOAuth2AuthorizationRequestResolver(
clientRegistrationRepository, OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI
)
private val authorizationRequestMatcher: AntPathRequestMatcher = AntPathRequestMatcher(
OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{" + REGISTRATION_ID_URI_VARIABLE_NAME + "}")
override fun resolve(request: HttpServletRequest?): OAuth2AuthorizationRequest? {
val authorizationRequest: OAuth2AuthorizationRequest? = defaultAuthorizationRequestResolver.resolve(request)
return if(authorizationRequest == null)
{ null } else { customAuthorizationRequest(authorizationRequest) }
}
override fun resolve(request: HttpServletRequest?, clientRegistrationId: String?): OAuth2AuthorizationRequest? {
val authorizationRequest: OAuth2AuthorizationRequest? = defaultAuthorizationRequestResolver.resolve(request, clientRegistrationId)
return if(authorizationRequest == null)
{ null } else { customAuthorizationRequest(authorizationRequest) }
}
private fun customAuthorizationRequest(authorizationRequest: OAuth2AuthorizationRequest?): OAuth2AuthorizationRequest {
val registrationId: String = this.resolveRegistrationId(authorizationRequest)
val additionalParameters = LinkedHashMap(authorizationRequest?.additionalParameters)
// set login.gov specific params
// https://developers.login.gov/oidc/#authorization
if(registrationId == LOGIN_GOV_REGISTRATION_ID) {
additionalParameters["nonce"] = "1234567890" // generate your nonce here (should actually include per-session state and be unguessable)
// add other custom params...
}
return OAuth2AuthorizationRequest
.from(authorizationRequest)
.additionalParameters(additionalParameters)
.build()
}
private fun resolveRegistrationId(authorizationRequest: OAuth2AuthorizationRequest?): String {
return authorizationRequest!!.additionalParameters[OAuth2ParameterNames.REGISTRATION_ID] as String
}
}