代码之家  ›  专栏  ›  技术社区  ›  triton oidc

使用OpenID connect在Spring安全应用程序中生成nonce

  •  4
  • triton oidc  · 技术社区  · 6 年前

    我正在将Spring安全应用程序插入IDP/OP( IDe 数量 P 流浪者 O penid连接标识 P rovider(根据OpenID连接术语)

    我正在使用授权代码流。 我使用此实现来启动代码: https://github.com/gazbert/openid-connect-spring-client

    它与几个IDP一起工作,直到我找到一个需要nonce参数的IDP。 但是,我无法将我的应用程序配置为生成nonce,并将其添加到url中(我知道这是nonce,因为当我手动添加它时:它可以工作)

    当应用程序将用户重定向到IDP(授权端点)时,我希望有一个nonce。 如果能在返回时验证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;
        }
    

    如果有修改,我相信它就在那里。 如果这是重复的,我道歉,我再也不会让自己感到羞愧了。

    任何帮助都将不胜感激,如果需要,我可以发布更多详细信息,我不想因为发布太多而混淆

    谢谢你读我的书

    1 回复  |  直到 6 年前
        1
  •  4
  •   forgo    5 年前

    我也在努力解决这个问题。幸运的是,在 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
        }
    
    }