Автентифікація за допомогою Reddit OAuth2 та Spring Security

1. Огляд

У цьому посібнику ми будемо використовувати Spring Security OAuth для автентифікації за допомогою Reddit API.

2. Конфігурація Maven

По-перше, для того, щоб використовувати OAuth Spring Security - нам потрібно додати наступну залежність до нашого 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

Ви можете отримати свій власний секретний код, створивши додаток Reddit за адресою //www.reddit.com/prefs/apps/

Ми будемо використовувати OAuth2RestTemplate для:

  1. Отримати маркер доступу, необхідний для доступу до віддаленого ресурсу.
  2. Доступ до віддаленого ресурсу після отримання маркера доступу.

Також зауважте, як ми додали область « ідентичність » до Reddit OAuth2ProtectedResourceDetails, щоб ми могли пізніше отримувати інформацію про обліковий запис користувачів.

4. Спеціальний AuthorizationCodeAccessTokenProvider

Реалізація Reddit OAuth2 трохи відрізняється від стандартної. І тому - замість того, щоб елегантно розширювати AuthorizationCodeAccessTokenProvider - нам потрібно фактично перевизначити деякі його частини.

Існують проблеми з відстеженням github, які роблять це непотрібним, але ці проблеми ще не зроблені.

Одна з нестандартних речей, які робить Reddit, - коли ми перенаправляємо користувача та пропонуємо йому пройти автентифікацію за допомогою Reddit, нам потрібно мати деякі користувацькі параметри в URL-адресі перенаправлення. Більш конкретно - якщо ми просимо маркер постійного доступу від Reddit - нам потрібно додати параметр “ duration ” зі значенням “ permanent ”.

Отже, після розширення AuthorizationCodeAccessTokenProvider - ми додали цей параметр у метод getRedirectForAuthorization () :

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

Ви можете перевірити повний вихідний код тут.

5. Ініціалізатор сервера

Далі - давайте створимо наш власний ServerInitializer .

Нам потрібно додати компонент фільтра з ідентифікатором 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 .

Ми використовуємо метод redditLogin (), щоб отримати інформацію про користувача з його облікового запису Reddit і завантажити з нього аутентифікацію - як у наступному прикладі:

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

Цікава деталь цього оманливо простого методу - шаблон reddit перевіряє, чи доступний маркер доступу перед виконанням будь-якого запиту ; він отримує маркер, якщо такий недоступний.

Далі - ми представляємо інформацію нашому дуже спрощеному інтерфейсу.

9. home.jsp

Нарешті - давайте заглянемо до home.jsp - щоб відобразити інформацію, отриману з облікового запису Reddit користувача:

10. Висновок

У цій вступній статті ми дослідили автентифікацію за допомогою API RedDit OAuth2 та відобразили дуже просту інформацію в простому інтерфейсі.

Тепер, коли ми пройшли автентифікацію, ми збираємось дослідити, щоб зробити ще цікаві речі за допомогою Reddit API у наступній статті цієї нової серії.

Повне здійснення цього уроку можна знайти в проекті GitHub - це проект , заснований Затемнення, тому вона повинна бути легко імпортувати і працювати , як це.