Authentifizierung mit Reddit OAuth2 und Spring Security
1. Überblick
In diesem Lernprogramm 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 zunächst unserepom.xml um die folgende Abhängigkeit erweitern (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 - dieOAuth2RestTemplate - und einereddit.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("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;
}
}
Und „reddit.properties“:
clientID=xxxxxxxx
clientSecret=xxxxxxxx
accessTokenUri=https://www.reddit.com/api/v1/access_token
userAuthorizationUri=https://www.reddit.com/api/v1/authorize
Sie können Ihren eigenen Geheimcode erhalten, indem Sie eine Reddit-App aushttps://www.reddit.com/prefs/apps/ erstellen
Wir werden dieOAuth2RestTemplate verwenden, um:
-
Fordern Sie das Zugriffstoken an, das für den Zugriff auf die Remote-Ressource erforderlich ist.
-
Greifen Sie auf die Remote-Ressource zu, nachdem Sie das Zugriffstoken erhalten haben.
Beachten Sie auch, wie wir den Bereich "identity" zu RedditOAuth2ProtectedResourceDetails hinzugefügt haben, damit wir die Benutzerkontoinformationen später abrufen können.
4. BenutzerdefinierteAuthorizationCodeAccessTokenProvider
Die Reddit OAuth2-Implementierung unterscheidet sich ein wenig vom Standard. Und anstatt dieAuthorizationCodeAccessTokenProvider elegant zu erweitern, müssen wir einige Teile davon tatsächlich überschreiben.
Es gibt Github-Probleme bei der Nachverfolgung von Verbesserungen, die dies nicht erforderlich machen. Diese Probleme sind jedoch noch nicht behoben.
Eine der nicht standardmäßigen Aufgaben von Reddit ist: Wenn wir den Benutzer umleiten und ihn auffordern, sich bei Reddit zu authentifizieren, müssen wir einige benutzerdefinierte Parameter in der Umleitungs-URL haben. Genauer gesagt - wenn wir von Reddit nach einem permanenten Zugriffstoken fragen - müssen wir einen Parameter "duration" mit dem Wert "permanent" hinzufügen.
Nach dem Erweitern vonAuthorizationCodeAccessTokenProvider haben wir diesen Parameter in diegetRedirectForAuthorization()-Methode eingefügt:
requestParameters.put("duration", "permanent");
Sie können den vollständigen Quellcode vonhere überprüfen.
5. DieServerInitializer
Als Nächstes erstellen wir unsere benutzerdefiniertenServerInitializer.
Wir müssen eine Filter-Bean mit der IDoauth2ClientContextFilter 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.example.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
Weiter - werfen wir einen Blick aufthe main Spring Security configuration:
@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 ControllerRedditControlleran.
Wir verwenden die MethoderedditLogin(), um die Benutzerinformationen von seinem Reddit-Konto abzurufen und eine Authentifizierung daraus zu laden - wie im folgenden Beispiel:
@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";
}
}
Ein interessantes Detail dieser täuschend einfachen Methode - die reddit-Vorlagechecks if the access token is available before executing any request; Es erhält ein Token, wenn eines nicht verfügbar ist.
Als nächstes präsentieren wir die Informationen unserem sehr simplen Frontend.
9. home.jsp
Schauen wir uns zum Schlusshome.jsp an, um die Informationen anzuzeigen, die vom Reddit-Konto des Benutzers abgerufen wurden:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
Welcome,
10. Fazit
In diesem Einführungsartikel haben wirauthenticating with the Reddit OAuth2 API untersucht und einige sehr grundlegende Informationen in einem einfachen Frontend angezeigt.
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.
Diefull implementation dieses Tutorials finden Sie inthe github project - dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.