Reddit OAuth2 및 Spring Security로 인증

1. 개요

이 튜토리얼에서는 Spring Security OAuth를 사용하여 Reddit API로 인증합니다.

2. Maven 구성

먼저, Spring Security OAuth를 사용하기 위해 -pom.xml에 다음과 같은 종속성을 추가해야합니다 (물론 사용할 수있는 다른 Spring 종속성과 함께).

 org.springframework.security.oauth spring-security-oauth2 2.0.6.RELEASE 

3. OAuth2 클라이언트 구성

다음으로 OAuth2 클라이언트 인 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("//localhost/login"); details.setUseCurrentUri(false); return details; } @Bean public OAuth2RestTemplate redditRestTemplate(OAuth2ClientContext clientContext) { OAuth2RestTemplate template = new OAuth2RestTemplate(reddit(), clientContext); AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain( Arrays. asList( new MyAuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider()) ); template.setAccessTokenProvider(accessTokenProvider); return template; } }

그리고 " reddit.properties ":

clientID=xxxxxxxx clientSecret=xxxxxxxx accessTokenUri=//www.reddit.com/api/v1/access_token userAuthorizationUri=//www.reddit.com/api/v1/authorize

//www.reddit.com/prefs/apps/에서 Reddit 앱을 생성하여 자신의 비밀 코드를 얻을 수 있습니다.

OAuth2RestTemplate 을 사용하여 다음 을 수행합니다.

  1. 원격 리소스에 액세스하는 데 필요한 액세스 토큰을 얻습니다.
  2. 액세스 토큰을 얻은 후 원격 리소스에 액세스합니다.

또한 나중에 사용자 계정 정보를 검색 할 수 있도록 Reddit OAuth2ProtectedResourceDetails에 " identity " 범위를 추가 한 방법에 유의하십시오 .

4. 사용자 지정 AuthorizationCodeAccessTokenProvider

Reddit OAuth2 구현은 표준과 약간 다릅니다. 따라서 AuthorizationCodeAccessTokenProvider 를 우아하게 확장하는 대신 실제로 일부를 재정의해야합니다.

이것이 필요하지 않게 만드는 개선 사항을 추적하는 github 문제가 있지만 이러한 문제는 아직 완료되지 않았습니다.

Reddit이 수행하는 비표준 작업 중 하나는 사용자를 리디렉션하고 Reddit으로 인증하라는 메시지를 표시 할 때 리디렉션 URL에 몇 가지 맞춤 매개 변수가 있어야한다는 것입니다. 보다 구체적으로-Reddit에서 영구 액세스 토큰을 요청 하는 경우 " 영구적 " 값으로 " 기간 " 매개 변수를 추가해야합니다 .

따라서 AuthorizationCodeAccessTokenProvider 를 확장 한 후 getRedirectForAuthorization () 메서드 에이 매개 변수를 추가했습니다 .

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

여기에서 전체 소스 코드를 확인할 수 있습니다.

5. ServerInitializer

다음으로 사용자 정의 ServerInitializer를 만들어 보겠습니다 .

현재 컨텍스트를 저장하는 데 사용할 수 있도록 ID가 oauth2ClientContextFilter 인 필터 빈을 추가해야합니다 .

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 구성

이제 간단한 웹 앱의 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를 살펴 보겠습니다 .

We use method redditLogin() to get the user information from his Reddit account and load an authentication from it – as in the following example:

@Controller public class RedditController { @Autowired private OAuth2RestTemplate redditRestTemplate; @RequestMapping("/login") public String redditLogin() { JsonNode node = redditRestTemplate.getForObject( "//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"; } }

An interesting detail of this deceptively simple method – the reddit template checks if the access token is available before executing any request; it acquires a token if one is not available.

Next – we present the information to our very simplistic front end.

9. home.jsp

Finally – let's take a look at home.jsp – to display the information retrieved form user's Reddit account:

10. Conclusion

In this introductory article, we explored authenticating with the Reddit OAuth2 API and displaying some very basic information in a simple front end.

이제 인증을 받았으므로이 새 시리즈의 다음 기사에서 Reddit API로 더 흥미로운 작업을 수행하는 방법을 살펴볼 것입니다.

이 튜토리얼 의 전체 구현 은 github 프로젝트에서 찾을 수 있습니다. 이것은 Eclipse 기반 프로젝트이므로 그대로 가져 와서 실행하기 쉽습니다.