Mengesahkan dengan Reddit OAuth2 dan Spring Security

1. Gambaran keseluruhan

Dalam tutorial ini, kami akan menggunakan Spring Security OAuth untuk mengesahkan dengan Reddit API.

2. Konfigurasi Maven

Pertama, untuk menggunakan Spring Security OAuth - kita perlu menambahkan kebergantungan berikut ke pom.xml kami (tentu saja di sepanjang pergantungan Spring lain yang mungkin anda gunakan):

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

3. Konfigurasikan Pelanggan OAuth2

Seterusnya - mari konfigurasikan klien OAuth2 kami - OAuth2RestTemplate - dan fail reddit.properties untuk semua sifat berkaitan pengesahan:

@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; } }

Dan " reddit.properties ":

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

Anda boleh mendapatkan kod rahsia anda sendiri dengan membuat aplikasi Reddit dari //www.reddit.com/prefs/apps/

Kami akan menggunakan OAuth2RestTemplate untuk:

  1. Dapatkan token akses yang diperlukan untuk mengakses sumber jauh.
  2. Akses sumber jauh setelah mendapat token akses.

Perhatikan juga bagaimana kami menambahkan skop " identiti " ke Reddit OAuth2ProtectedResourceDetails sehingga kami dapat mengambil maklumat akaun pengguna kemudian.

4. Custom AuthorizationCodeAccessTokenProvider

Pelaksanaan Reddit OAuth2 sedikit berbeza dari standard. Oleh itu - bukannya memperluaskan PengesahanCodeAccessTokenProvider secara elegan - kita perlu benar-benar mengganti beberapa bahagiannya.

Terdapat masalah github yang menjejaki penambahbaikan yang menjadikannya tidak perlu, tetapi masalah ini belum selesai.

Salah satu perkara tidak standard yang dilakukan oleh Reddit adalah - apabila kita mengarahkan pengguna dan memintanya untuk mengesahkan dengan Reddit, kita perlu mempunyai beberapa parameter tersuai dalam URL pengalihan. Lebih khusus lagi - jika kita meminta token akses tetap dari Reddit - kita perlu menambahkan parameter " tempoh " dengan nilai " kekal ".

Oleh itu, setelah memperluas AuthorizationCodeAccessTokenProvider - kami telah menambahkan parameter ini dalam kaedah getRedirectForAuthorization () :

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

Anda boleh menyemak kod sumber penuh dari sini.

5. ServerInitializer

Seterusnya - mari buat ServerInitializer tersuai kami .

Kita perlu menambahkan kacang penapis dengan id oauth2ClientContextFilter , supaya kita dapat menggunakannya untuk menyimpan konteks semasa:

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. Konfigurasi MVC

Sekarang - mari lihat konfigurasi MVC aplikasi web mudah kami:

@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. Konfigurasi Keselamatan

Seterusnya - mari kita lihat konfigurasi Spring Security utama :

@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"); } }

Catatan: Kami menambahkan konfigurasi keselamatan sederhana yang mengalihkan ke " / log masuk " yang mendapatkan maklumat pengguna dan memuatkan pengesahan darinya - seperti yang dijelaskan di bahagian berikut.

8. RedditController

Sekarang - mari lihat pengawal kami RedditController .

Kami menggunakan kaedah redditLogin () untuk mendapatkan maklumat pengguna dari akaun Redditnya dan memuatkan pengesahan daripadanya - seperti dalam contoh berikut:

@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"; } }

Perincian menarik kaedah mudah ini - templat reddit memeriksa apakah token akses tersedia sebelum melaksanakan sebarang permintaan ; ia memperoleh token jika tidak ada.

Seterusnya - kami menyampaikan maklumat tersebut ke bahagian depan yang sangat ringkas

9. rumah.jsp

Akhirnya - mari kita lihat di home.jsp - untuk memaparkan maklumat yang diambil dari akaun Reddit pengguna:

10. Kesimpulannya

Dalam artikel pengenalan ini, kami meneroka pengesahan dengan Reddit OAuth2 API dan memaparkan beberapa maklumat yang sangat asas di bahagian depan yang mudah.

Setelah kami disahkan, kami akan meneroka melakukan perkara yang lebih menarik dengan Reddit API dalam artikel seterusnya dari siri baru ini.

The pelaksanaan penuh tutorial ini boleh didapati dalam projek github - ini adalah projek berasaskan Eclipse, jadi ia harus mudah untuk import dan berjalan kerana ia adalah.