Рубрики
Без рубрики

Аутентификация с помощью Reddit OAuth2 и Spring Security

Краткое введение в то, как аутентифицировать ваше приложение с помощью Spring Security и API Reddit OAuth2.

Автор оригинала: Eugen Paraschiv.

1. Обзор

В этом уроке мы будем использовать Spring Security OAuth для аутентификации с помощью API Reddit.

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("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. 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

Вы можете получить свой собственный секретный код, создав приложение Reddit из https://www.reddit.com/prefs/apps/

Мы собираемся использовать OAuth2RestTemplate для:

  1. Получите маркер доступа, необходимый для доступа к удаленному ресурсу.
  2. Доступ к удаленному ресурсу после получения маркера доступа.

Также обратите внимание, как мы добавили область ” identity ” в Reddit OAuth2ProtectedResourceDetails , чтобы мы могли позже получить информацию об учетной записи пользователя.

4. Пользовательский AuthorizationCodeAccessTokenProvider

Реализация Reddit OAuth2 немного отличается от стандарта. И поэтому – вместо того, чтобы элегантно расширять AuthorizationCodeAccessTokenProvider – нам нужно фактически переопределить некоторые его части.

Есть проблемы с отслеживанием улучшений на github, которые сделают это ненужным, но эти проблемы еще не решены.

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

Итак, после расширения 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 :

@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"%>


    

Welcome,

10. Заключение

В этой вводной статье мы исследовали аутентификацию с помощью API Reddit OAuth2 и отображение некоторой очень базовой информации в простом интерфейсе.

Теперь, когда мы прошли проверку подлинности, мы рассмотрим более интересные вещи с помощью API Reddit в следующей статье этой новой серии.

полную реализацию этого учебника можно найти в проекте github – это проект на основе Eclipse, поэтому его должно быть легко импортировать и запускать как есть.