1

Spring MVC アプリケーションを構築しようとしており、Spring Security OAuth2 で保護しており、プロバイダーは Google です。セキュリティなしでフォーム ログインを使用して Web アプリを動作させることができました。ただし、Google で OAuth を機能させることができません。コールバックなどをSpring Security以外のアプリで動作させることができるので、Googleアプリのセットアップは問題ありません。

私のセキュリティ設定は次のとおりです。

<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns:sec="http://www.springframework.org/schema/security"
         xmlns:b="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
    <sec:http use-expressions="true" entry-point-ref="clientAuthenticationEntryPoint">
        <sec:http-basic/>
        <sec:logout/>
        <sec:anonymous enabled="false"/>

        <sec:intercept-url pattern="/**" access="isFullyAuthenticated()"/>

        <sec:custom-filter ref="oauth2ClientContextFilter" after="EXCEPTION_TRANSLATION_FILTER"/>
        <sec:custom-filter ref="googleAuthenticationFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
    </sec:http>

    <b:bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"/>

    <sec:authentication-manager alias="alternateAuthenticationManager">
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="user" password="password" authorities="DOMAIN_USER"/>
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</b:beans>

OAuth2 で保護されたリソースは次のとおりです。

@Configuration
@EnableOAuth2Client
class ResourceConfiguration {
    @Autowired
    private Environment env;

    @Resource
    @Qualifier("accessTokenRequest")
    private AccessTokenRequest accessTokenRequest;

    @Bean
    public OAuth2ProtectedResourceDetails googleResource() {
        AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
        details.setId("google-app");
        details.setClientId(env.getProperty("google.client.id"));
        details.setClientSecret(env.getProperty("google.client.secret"));
        details.setAccessTokenUri(env.getProperty("google.accessTokenUri"));
        details.setUserAuthorizationUri(env.getProperty("google.userAuthorizationUri"));
        details.setTokenName(env.getProperty("google.authorization.code"));
        String commaSeparatedScopes = env.getProperty("google.auth.scope");
        details.setScope(parseScopes(commaSeparatedScopes));
        details.setPreEstablishedRedirectUri(env.getProperty("google.preestablished.redirect.url"));
        details.setUseCurrentUri(false);
        details.setAuthenticationScheme(AuthenticationScheme.query);
        details.setClientAuthenticationScheme(AuthenticationScheme.form);
        return details;
    }

    private List<String> parseScopes(String commaSeparatedScopes) {
        List<String> scopes = newArrayList();
        Collections.addAll(scopes, commaSeparatedScopes.split(","));
        return scopes;
    }

    @Bean
    public OAuth2RestTemplate googleRestTemplate() {
        return new OAuth2RestTemplate(googleResource(), new DefaultOAuth2ClientContext(accessTokenRequest));
    }

    @Bean
    public AbstractAuthenticationProcessingFilter googleAuthenticationFilter() {
        return new GoogleOAuthentication2Filter(new GoogleAppsDomainAuthenticationManager(), googleRestTemplate(), "https://accounts.google.com/o/oauth2/auth", "http://localhost:9000");
    }
}

OAuth2 認証を取得するためにリダイレクト例外をスローするように作成したカスタム認証フィルターは次のとおりです。

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        try {
            logger.info("OAuth2 Filter Triggered!! for path {} {}", request.getRequestURI(), request.getRequestURL().toString());
            logger.info("OAuth2 Filter hashCode {} request hashCode {}", this.hashCode(), request.hashCode());
            String code = request.getParameter("code");
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            logger.info("Code is {} and authentication is {}", code, authentication == null ? null : authentication.isAuthenticated());
            // not authenticated
            if (requiresRedirectForAuthentication(code)) {
                URI authURI = new URI(googleAuthorizationUrl);

                logger.info("Posting to {} to trigger auth redirect", authURI);
                String url = "https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + oauth2RestTemplate.getAccessToken();
                logger.info("Getting profile data from {}", url);
                // Should throw RedirectRequiredException
                oauth2RestTemplate.getForEntity(url, GoogleProfile.class);

                // authentication in progress
                return null;
            } else {
                logger.info("OAuth callback received");
                // get user profile and prepare the authentication token object.

                String url = "https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + oauth2RestTemplate.getAccessToken();
                logger.info("Getting profile data from {}", url);
                ResponseEntity<GoogleProfile> forEntity = oauth2RestTemplate.getForEntity(url, GoogleProfile.class);
                GoogleProfile profile = forEntity.getBody();

                CustomOAuth2AuthenticationToken authenticationToken = getOAuth2Token(profile.getEmail());
                authenticationToken.setAuthenticated(false);
                Authentication authenticate = getAuthenticationManager().authenticate(authenticationToken);
                logger.info("Final authentication is {}", authenticate == null ? null : authenticate.isAuthenticated());

                return authenticate;
            }
        } catch (URISyntaxException e) {
            Throwables.propagate(e);
        }
        return null;
    }

Spring Web アプリからのフィルター チェーン シーケンスは次のとおりです。

o.s.b.c.e.ServletRegistrationBean - Mapping servlet: 'dispatcherServlet' to [/] 
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'metricFilter' to: [/*] 
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'oauth2ClientContextFilter' to: [/*] 
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'googleOAuthFilter' to: [/*] 
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'org.springframework.security.filterChainProxy' to: [/*] 
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0' to: [/*] 
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'hiddenHttpMethodFilter' to: [/*] 
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'applicationContextIdFilter' to: [/*] 
o.s.b.c.e.FilterRegistrationBean - Mapping filter: 'webRequestLoggingFilter' to: [/*] 

Google へのリダイレクトは正常に機能し、フィルターへのコールバックが取得され、認証が成功します。ただし、その後、リクエストの結果はリダイレクトになり、フィルターが再度呼び出されます (リクエストは同じです。hasCode を確認しました)。2 番目の呼び出しでの認証SecurityContextnull. 最初の認証呼び出しの一部として、Authentication オブジェクトがセキュリティ コンテキストに取り込まれたのに、なぜ消えてしまうのでしょうか? 私は初めてSpring Securityを使用しているので、初心者の間違いを犯した可能性があります。

4

2 に答える 2