了解Spring Security @ EnableOAuth2Client注释

时间:2019-04-01 18:23:21

标签: spring spring-boot spring-security spring-security-oauth2

在实现基于Spring Security Oauth2库构建的OpenID连接时遇到问题。 (阅读有关problem in a separate question的更多信息。)在研究它时,我读了documentation for the @EnableOauth2Client annotation,其中说:

  

在使用Spring Security并希望使用来自一个或多个OAuth2授权服务器的授权代码授予的Web应用程序中为OAuth2客户端启用配置。要利用此功能,您需要在DelegatingFilterProxy的应用程序中使用全局servlet过滤器,该过滤器委派给名为“ oauth2ClientContextFilter”的bean。过滤器到位后,您的客户端应用即可使用此批注提供的另一个bean(一个AccessTokenRequest)来创建OAuth2RestTemplate,例如

@Configuration
@EnableOAuth2Client
public class RemoteResourceConfiguration {

    @Bean
    public OAuth2RestOperations restTemplate(OAuth2ClientContext oauth2ClientContext) {
           return new OAuth2RestTemplate(remote(), oauth2ClientContext);
    }

}
     

使用客户端凭据授予的客户端应用程序不需要AccessTokenRequest或作用域的RestOperations(状态对于应用程序是全局的),但是它们仍应使用过滤器来触发OAuth2RestOperations,以在必要时获取令牌。使用 [sic] 密码授予的应用需要在使用RestOperations之前在OAuth2ProtectedResourceDetails中设置身份验证属性,这意味着资源详细信息本身也必须针对每个会话(假设在系统)。

关于版本和文档的注意事项:本文档是2.0.4版本,即使我的项目正在使用较新的2.3.5链接,也可以从Spring Security project page链接到该文档。我们的其他Spring版本:Spring Boot 1.3.0,Spring Security 3.2.5,Spring Framework 4.2.3。

我不太明白这是什么意思,

  

您的DelegatingFilterProxy应用程序中的全局servlet过滤器,该过滤器委派给名为“ oauth2ClientContextFilter”的bean

这是我们配置休息模板的方式。

@Configuration
@EnableOAuth2Client
public class OpenIdConnectConfig {

    @Bean
    public OAuth2ProtectedResourceDetails openIdResourceDetails() {
        AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
        details.setClientId(clientId);
        details.setClientSecret(clientSecret);
        details.setAccessTokenUri(accessTokenUri);
        details.setUserAuthorizationUri(userAuthorizationUri);
        details.setClientAuthenticationScheme(AuthenticationScheme.form);
        details.setScope(oidcScopes);
        details.setPreEstablishedRedirectUri(redirectUri);
        details.setUseCurrentUri(false);
        return details;
    }

    @Bean(name = "my.company.ui.security.OpenIdRestTemplate")
    // ToDo: fix org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'session' is not active for the current thread
    public OAuth2RestTemplate OpenIdRestTemplate(OAuth2ClientContext clientContext) {
        return new OAuth2RestTemplate(openIdResourceDetails(), clientContext);
    }

    @Bean
    public RequestContextListener requestContextListener() {
        return new RequestContextListener();
    }

    @Value("${oidc.clientId}")
    private String clientId;

    @Value("${oidc.clientSecret}")
    private String clientSecret;

    @Value("${oidc.accessTokenUrl}")
    private String accessTokenUri;

    @Value("${oidc.userAuthorizationUri}")
    private String userAuthorizationUri;

    @Value("${oidc.redirectUri}")
    private String redirectUri;

    @Value("#{'${oidc.scopes}'.split(',')}")
    private List<String> oidcScopes;
}

执行身份验证的过滤器(删除了一些异常处理和用户处理代码):

public class OpenIdConnectFilter extends AbstractAuthenticationProcessingFilter {

    public OpenIdConnectFilter(
            RequestMatcher requiresAuthenticationRequestMatcher,
            AuthenticationService authenticationService
    ) {
        super(requiresAuthenticationRequestMatcher);
        setAuthenticationManager(new NoopAuthenticationManager());
    }

    @SuppressWarnings("RedundantThrows") // Matching overridden method
    @Override
    public Authentication attemptAuthentication(
            HttpServletRequest request,
            HttpServletResponse response
    ) throws AuthenticationException, IOException, ServletException {

        // Required parameters (one-time access code, state) are retrieved from the context
        OAuth2AccessToken oAuth2AccessToken = restTemplate.getAccessToken();

        // Process the token, get the user details, return an Authentication object.
    }

    public void setRestTemplate(OAuth2RestTemplate restTemplate) {
        this.restTemplate = restTemplate;

    }

    private static class NoopAuthenticationManager implements AuthenticationManager {

        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            throw new UnsupportedOperationException("No authentication should be done with this AuthenticationManager");
        }

    }


    private static final Logger LOGGER = LoggerFactory.getLogger(OpenIdConnectFilter.class);

    @Value("${oidc.clientId}")
    private String clientId;

    @Value("${oidc.issuer}")
    private String issuer;

    @Value("${oidc.jwt.jwk.url}")
    private String jwkUrl;

    private final AuthenticationService authenticationService;

    private OAuth2RestTemplate restTemplate;
}

以及用于设置Spring Security FilterProxyChain的安全配置:

@Configuration
@EnableWebSecurity
@EnableOAuth2Client
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Override
    @SuppressWarnings("unchecked")
    protected void configure(HttpSecurity http)
            throws Exception {

        http
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
            .and()
            .csrf()
                .disable()
            .authorizeRequests()
                .expressionHandler(securityExpressionHandler)
                .antMatchers("/asset/**").access("permitAll")
                .antMatchers("/ws/ssoEnabled").access("permitAll")
                .antMatchers("/**").access("hasRole('ROLE_USER') or hasRole('ROLE_TOKEN_ACCESS')")
                .and()
            .httpBasic()
                .authenticationEntryPoint(ajaxAwareLoginUrlAuthenticationEntryPoint)
                .and()
            // Handles unauthenticated requests, catching UserRedirectRequiredExceptions and redirecting to OAuth provider
            .addFilterAfter(new OAuth2ClientContextFilter(), SecurityContextPersistenceFilter.class)
            // Handles the oauth callback, exchanging the one-time code for a durable token
            .addFilterAfter(openIdConnectFilter, OAuth2ClientContextFilter.class)
            .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/logincheck")
                .usernameParameter("username")
                .passwordParameter("password")
                .successHandler(ajaxAwareAuthenticationSuccessHandler)
                .failureHandler(ajaxAwareAuthenticationFailureHandler)
                .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login")
                .and()
            .rememberMe()
                .rememberMeServices(rememberMeServices)
                // Even though this key has been added directly to the rememberMeServices instance, the RememberMeConfigurer
                // can instantiate a new RememberMeServices with a made-up key if the same key is not provided.
                .key("the key value")
        ;

        // We do not configure a bean for the SessionAuthenticationStrategy. We want to use the Spring default strategy,
        // which is configured by the above builder chain. In order to share the correct, configured instance with our
        // custom OpenIdConnectFilter, we first tell the builder to perform the configuration (normally this would be
        // done long after this method returns)...
        http.getConfigurer(SessionManagementConfigurer.class).init(http);
        // ... then we get the shared object by interface (SessionAuthenticationStrategy) class name...
        final SessionAuthenticationStrategy sessionAuthenticationStrategy = http.getSharedObject(SessionAuthenticationStrategy.class);
        // ... then set it in our custom filter.
        openIdConnectFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
    }
}

您会发现其中有多种身份验证机制。我们正在从表单登录迁移到Oauth登录,初始发行版中有一个功能标记。该标志运行得很好,除了在顶部的链接问题中描述的问题之外,该问题在翻转标志后会出现一段时间,然后似乎可以自行解决。


上面显示的配置是否足以满足@EnableOauth2Client注释的文档说明?

或者我还需要对DelegationFilterProxy做些什么?如果可以,怎么办?

0 个答案:

没有答案