Reddit OAuth2とSpring Securityによる認証

1概要

このチュートリアルでは、Spring Security OAuthを使用してReddit APIによる認証を行います。

2 Mavenの設定

まず、Spring Security OAuthを使用するには、 pom.xml に次の依存関係を追加する必要があります(もちろん、他のSpring依存関係も使用します)。

<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.0.6.RELEASE</version>
</dependency>

3 OAuth2クライアントを設定する

次に、OAuth 2クライアント( OAuth2RestTemplate )とすべての認証関連プロパティ用の reddit.properties ファイルを設定します。

@Configuration
@EnableOAuth2Client
@PropertySource("classpath:reddit.properties")
protected static class ResourceConfiguration {

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

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

    @Value("${clientID}")
    private String clientID;

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

    @Bean
    public OAuth2ProtectedResourceDetails reddit() {
        AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
        details.setId("reddit");
        details.setClientId(clientID);
        details.setClientSecret(clientSecret);
        details.setAccessTokenUri(accessTokenUri);
        details.setUserAuthorizationUri(userAuthorizationUri);
        details.setTokenName("oauth__token");
        details.setScope(Arrays.asList("identity"));
        details.setPreEstablishedRedirectUri("http://localhost/login");
        details.setUseCurrentUri(false);
        return details;
    }

    @Bean
    public OAuth2RestTemplate redditRestTemplate(OAuth2ClientContext clientContext) {
        OAuth2RestTemplate template = new OAuth2RestTemplate(reddit(), clientContext);
        AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(
          Arrays.<AccessTokenProvider> asList(
            new MyAuthorizationCodeAccessTokenProvider(),
            new ImplicitAccessTokenProvider(),
            new ResourceOwnerPasswordAccessTokenProvider(),
            new ClientCredentialsAccessTokenProvider())
        );
        template.setAccessTokenProvider(accessTokenProvider);
        return template;
    }

}

そして " reddit.properties ":

clientID=xxxxxxxx
clientSecret=xxxxxxxx
accessTokenUri=https://www.reddit.com/api/v1/access__token
userAuthorizationUri=https://www.reddit.com/api/v1/authorize

あなたはhttps://www.reddit.com/prefs/apps/からRedditアプリを作成することによってあなた自身の秘密のコードを入手することができます。

OAuth2RestTemplate を使用して、

  1. リモートリソースへのアクセスに必要なアクセストークンを取得します.

  2. アクセストークンを取得したら、リモートリソースにアクセスします.

また、後でユーザーアカウント情報を取得できるように、範囲 " identity "をReddit OAuth2ProtectedResourceDetails に追加したことにも注意してください。

4カスタム AuthorizationCodeAccessTokenProvider

Reddit OAuth2の実装は標準とは少し異なります。そのため、 AuthorizationCodeAccessTokenProvider をエレガントに拡張する代わりに、実際にその一部をオーバーライドする必要があります。

これを必要としない改善を追跡するgithubの問題がありますが、これらの問題はまだ行われていません。

Redditが行う非標準的なことの1つは、ユーザーをリダイレクトしてRedditで認証するようにユーザーに指示するときに、リダイレクトURLにいくつかのカスタムパラメータを含める必要があることです。具体的には、Redditからパーマネントアクセストークンを要求する場合は、値「 permanent 」を持つパラメータ「 duration 」を追加する必要があります。

そのため、 AuthorizationCodeAccessTokenProvider を拡張した後 - このパラメーターを getRedirectForAuthorization() メソッドに追加しました。

    requestParameters.put("duration", "permanent");

完全なソースコードはhttps://github.com/Baeldung/reddit-app/tree/master/reddit-common/src/main/java/org/baeldung/security[こちら]から確認できます。

5 ServerInitializer

次に、カスタムの ServerInitializer を作成しましょう。

現在のコンテキストを格納するために使用できるように、id oauth2ClientContextFilter を持つフィルタBeanを追加する必要があります。

public class ServletInitializer extends AbstractDispatcherServletInitializer {

    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext context =
          new AnnotationConfigWebApplicationContext();
        context.register(WebConfig.class, SecurityConfig.class);
        return context;
    }

    @Override
    protected String[]getServletMappings() {
        return new String[]{ "/" };
    }

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        super.onStartup(servletContext);
        registerProxyFilter(servletContext, "oauth2ClientContextFilter");
        registerProxyFilter(servletContext, "springSecurityFilterChain");
    }

    private void registerProxyFilter(ServletContext servletContext, String name) {
        DelegatingFilterProxy filter = new DelegatingFilterProxy(name);
        filter.setContextAttribute(
          "org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
        servletContext.addFilter(name, filter).addMappingForUrlPatterns(null, false, "/** ");
    }
}

6. MVC設定

それでは、単純なWebアプリケーションのMVC設定を見てみましょう。

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "org.baeldung.web" })
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public static PropertySourcesPlaceholderConfigurer
      propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/jsp/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    @Override
    public void configureDefaultServletHandling(
      DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/** ** ").addResourceLocations("/resources/");
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/home.html");
    }
}

7. セキュリティ設定

次に、 Spring Securityの主な設定 を見てみましょう。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
      throws Exception {
        auth.inMemoryAuthentication();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .anonymous().disable()
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/home.html").hasRole("USER")
            .and()
            .httpBasic()
            .authenticationEntryPoint(oauth2AuthenticationEntryPoint());
    }

    private LoginUrlAuthenticationEntryPoint oauth2AuthenticationEntryPoint() {
        return new LoginUrlAuthenticationEntryPoint("/login");
    }
}

注:次のセクションで説明するように、ユーザ情報を取得してそこから認証をロードする「 /login 」にリダイレクトする単純なセキュリティ設定を追加しました。

8 RedditController

それでは、私たちのコントローラ RedditController を見てみましょう。

次の例のように、メソッド redditLogin() を使用して、彼のRedditアカウントからユーザー情報を取得し、そこから認証を読み込みます。

@Controller
public class RedditController {

    @Autowired
    private OAuth2RestTemplate redditRestTemplate;

    @RequestMapping("/login")
    public String redditLogin() {
        JsonNode node = redditRestTemplate.getForObject(
          "https://oauth.reddit.com/api/v1/me", JsonNode.class);
        UsernamePasswordAuthenticationToken auth =
          new UsernamePasswordAuthenticationToken(node.get("name").asText(),
          redditRestTemplate.getAccessToken().getValue(),
          Arrays.asList(new SimpleGrantedAuthority("ROLE__USER")));

        SecurityContextHolder.getContext().setAuthentication(auth);
        return "redirect:home.html";
    }

}

この一見単純な方法の興味深い詳細 - redditテンプレートは、リクエストを実行する前にアクセストークンが利用可能かどうかをチェックします。トークンが利用できない場合は取得します。

次に - 私達は私達の非常に単純化したフロントエンドに情報を提示します。

9 home.jsp

最後に、 home.jsp を見てみましょう。ユーザーのRedditアカウントから取得した情報を表示します。

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<html>
<body>
    <h1>Welcome, <small><sec:authentication property="principal.username"/></small></h1>
</body>
</html>

10結論

この紹介記事では、Reddit OAuth2 APIを使用した認証と、非常に基本的な情報を簡単なフロントエンドに表示する方法について説明しました。

これで認証が完了したので、この新シリーズの次回の記事で、Reddit APIを使用してもっと面白いことをすることを検討します。

このチュートリアルの 完全な実装 はhttps://github.com/eugenp/reddit-app[the github project]にあります - これはEclipseベースのプロジェクトなので、インポートして実行するのは簡単なはずです。 。