I usually post about Security on Twitter - you can follow me there:
1. Overview
In this article, we’ll build some simple functionality to post on Reddit from our application, via their API.
2. Necessary Security
First – let’s get the security aspect out of the way.
In order to Submit a Link to Reddit, we need to define an OAuth protected Resource with the scope of “submit“:
@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", "submit")); details.setGrantType("authorization_code"); return details; }
Note that we’re also specifying the scope “identity” because we also need access the user account information.
3. Is Captcha Needed?
Users that are new to Reddit have to fill in a Captcha in order to submit; that is before they pass a certain karma threshold within Reddit.
For these users, we first need to check if the Captcha is needed:
private String needsCaptcha() { String result = redditRestTemplate.getForObject( "https://oauth.reddit.com/api/needs_captcha.json", String.class); return result; } private String getNewCaptcha() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity req = new HttpEntity(headers); Map<String, String> param = new HashMap<String, String>(); param.put("api_type", "json"); ResponseEntity<String> result = redditRestTemplate.postForEntity( "https://oauth.reddit.com/api/new_captcha", req, String.class, param); String[] split = result.getBody().split("\""); return split[split.length - 2]; }
4. The “Submit Post” Form
Next, let’s create the main form for submitting new posts to Reddit. Submitting a Link requires the following details:
- title – the title of the article
- url – the URL of the article
- subreddit – the sub-reddit to submit the link to
So let’s see how we can display this simple submission page:
@RequestMapping("/post") public String showSubmissionForm(Model model) throws JsonProcessingException, IOException { String needsCaptchaResult = needsCaptcha(); if (needsCaptchaResult.equalsIgnoreCase("true")) { String iden = getNewCaptcha(); model.addAttribute("iden", iden); } return "submissionForm"; }
And of course the basic submissionForm.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Spring Security OAuth</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> </head> <body> <div class="container"> <h1>Submit to Reddit</h1> <form action="submit" method="post"> <div class="row"> <div class="form-group"> <label class="col-sm-3">Title</label> <span class="col-sm-9"> <input name="title" placeholder="title" class="form-control" /> </span> </div> <br><br> <div class="form-group"> <label class="col-sm-3">Url</label> <span class="col-sm-9"> <input name="url" placeholder="url" class="form-control" /> </span> </div> <br><br> <div class="form-group"> <label class="col-sm-3">Subreddit</label> <span class="col-sm-9"> <input name="sr" placeholder="Subreddit" class="form-control" /> </span> </div> <br><br> <c:if test="${iden != null}"> <input type="hidden" name="iden" value="${iden}"/> <div class="form-group"> <label class="col-sm-3">Captcha</label> <span class="col-sm-9"> <input name="captcha" placeholder="captcha" class="form-control"/> </span> </div> <br><br> <img src="http://www.reddit.com/captcha/${iden}" alt="captcha" width="200"/> </c:if> <br><br> <button type="submit" class="btn btn-primary">Post</button> </div> </form> </div> </body> </html>
5. Submit a Link to Reddit
Now – let’s take a look at the final step – submitting the actual link via the Reddit API.
We’ll post a submit request to Reddit using the parameters from our submissionForm:
@RequestMapping("/submit") public String submit(Model model, @RequestParam Map<String, String> formParams) { MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>(); param.add("api_type", "json"); param.add("kind", "link"); param.add("resubmit", "true"); param.add("sendreplies", "false"); param.add("then", "comments"); for (Map.Entry<String, String> entry : formParams.entrySet()) { param.add(entry.getKey(), entry.getValue()); } logger.info("Submitting Link with these parameters: " + formParams.entrySet()); String result = redditRestTemplate.postForObject( "https://oauth.reddit.com/api/submit", param, String.class); logger.info("Submitted Link - Full Response from Reddit" + result); String responseMsg = parseResponseMessage(result); model.addAttribute("msg", responseMsg); return "submissionResponse"; }
And the simple parseResponse() logic:
private String parseResponse(String responseBody) throws JsonProcessingException, IOException { String result = ""; JsonNode node = new ObjectMapper().readTree(responseBody); JsonNode errorNode = node.get("json").get("errors").get(0); for (JsonNode child : errorNode) { result = result + child.toString().replaceAll("\"|null", "") + "<br>"; } if (result.length() == 0) { if (node.get("json").get("data") != null && node.get("json").get("data").get("url") != null) { return "Post submitted successfully <a href=\"" + node.get("json").get("data").get("url").asText() + "\"> check it out </a>"; } else { return "Error Occurred"; } } return result; }
Finally – the submissionResponse.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Spring Security OAuth</title> <link rel="stylesheet" href= "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> </head> <body> <div class="container"> <h1>${msg}</h1> <a href="post" class="btn btn-primary">Submit another link to Reddit</a> </div> </body> </html>
6. Conclusion
In this quick tutorial we implemented some basic Submit to Reddit functionality – simplistic but fully functional.
In the next part of this case study, we’ll implement a Schedule Post for Later functionality into our app.
The full implementation of this tutorial can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.