Authentifizierung mit Reddit OAuth2 und Spring Security

1. Übersicht

In diesem Tutorial verwenden wir Spring Security OAuth zur Authentifizierung bei der Reddit-API.

2. Maven-Konfiguration

Um Spring Security OAuth verwenden zu können, müssen wir unserer pom.xml die folgende Abhängigkeit hinzufügen (natürlich zusammen mit jeder anderen Spring-Abhängigkeit, die Sie möglicherweise verwenden):

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

3. Konfigurieren Sie den OAuth2-Client

Als Nächstes konfigurieren wir unseren OAuth2-Client - das OAuth2RestTemplate - und eine reddit.properties- Datei für alle authentifizierungsbezogenen Eigenschaften:

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

Und „ reddit.properties “:

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

Sie können Ihren eigenen Geheimcode erhalten, indem Sie eine Reddit-App unter //www.reddit.com/prefs/apps/ erstellen.

Wir werden die OAuth2RestTemplate verwenden, um:

  1. Erwerben Sie das Zugriffstoken, das für den Zugriff auf die Remote-Ressource erforderlich ist.
  2. Greifen Sie auf die Remote-Ressource zu, nachdem Sie das Zugriffstoken erhalten haben.

Beachten Sie auch, wie wir Reddit OAuth2ProtectedResourceDetails den Bereich " Identität " hinzugefügt haben, damit wir die Kontoinformationen des Benutzers später abrufen können.

4. Benutzerdefinierter AuthorizationCodeAccessTokenProvider

Die Reddit OAuth2-Implementierung unterscheidet sich ein wenig vom Standard. Anstatt den AuthorizationCodeAccessTokenProvider elegant zu erweitern, müssen wir einige Teile davon tatsächlich überschreiben.

Es gibt Github-Probleme bei der Verfolgung von Verbesserungen, die dies nicht erforderlich machen, aber diese Probleme sind noch nicht behoben.

Eine der nicht standardmäßigen Aufgaben von Reddit ist: Wenn wir den Benutzer umleiten und ihn zur Authentifizierung bei Reddit auffordern, müssen einige benutzerdefinierte Parameter in der Umleitungs-URL enthalten sein. Genauer gesagt - wenn wir von Reddit nach einem permanenten Zugriffstoken fragen - müssen wir einen Parameter " Dauer " mit dem Wert " permanent " hinzufügen .

Nach der Erweiterung von AuthorizationCodeAccessTokenProvider haben wir diesen Parameter in die Methode getRedirectForAuthorization () eingefügt :

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

Sie können den vollständigen Quellcode von hier aus überprüfen.

5. Der ServerInitializer

Als Nächstes erstellen wir unseren benutzerdefinierten ServerInitializer .

Wir müssen eine Filter-Bean mit der ID oauth2ClientContextFilter hinzufügen , damit wir damit den aktuellen Kontext speichern können:

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-Konfiguration

Schauen wir uns jetzt unsere MVC-Konfiguration unserer einfachen Web-App an:

@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. Sicherheitskonfiguration

Als nächstes werfen wir einen Blick auf die Hauptkonfiguration von 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"); } }

Hinweis: Wir haben eine einfache Sicherheitskonfiguration hinzugefügt, die zu " / login " umleitet, um die Benutzerinformationen abzurufen und die Authentifizierung daraus zu laden - wie im folgenden Abschnitt erläutert.

8. RedditController

Schauen wir uns jetzt unseren Controller RedditController an .

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.

Nachdem wir uns authentifiziert haben, werden wir im nächsten Artikel dieser neuen Serie untersuchen, wie Sie mit der Reddit-API interessantere Dinge tun können.

Die vollständige Implementierung dieses Tutorials finden Sie im Github-Projekt. Dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.