├── README.md ├── client ├── pom.xml └── src │ └── main │ ├── java │ └── client │ │ ├── ConferenceException.java │ │ ├── Session.java │ │ ├── Speaker.java │ │ ├── config │ │ ├── ServletInitializer.java │ │ └── WebMvcConfig.java │ │ ├── converter │ │ └── AccessTokenRequestConverter.java │ │ └── mvc │ │ └── ConferenceController.java │ ├── resources │ ├── client.properties │ ├── commons-logging.properties │ └── simplelog.properties │ └── webapp │ ├── WEB-INF │ └── jsp │ │ ├── message.jsp │ │ ├── sessions.jsp │ │ └── speakers.jsp │ ├── index.jsp │ ├── js │ └── libs │ │ └── jso.js │ └── oauth_error.jsp ├── conference ├── pom.xml └── src │ └── main │ ├── java │ └── conference │ │ ├── Session.java │ │ ├── Speaker.java │ │ ├── config │ │ ├── AuthorizationServerConfig.java │ │ ├── MethodSecurityConfig.java │ │ ├── OAuth2ServerConfig.java │ │ ├── ResourceServerConfig.java │ │ ├── SecurityConfiguration.java │ │ ├── ServletInitializer.java │ │ └── WebMvcConfig.java │ │ ├── oauth │ │ ├── AccessConfirmationController.java │ │ ├── AdminController.java │ │ └── UserApprovalHandler.java │ │ └── rest │ │ ├── Application.java │ │ ├── ConferenceService.java │ │ └── JerseyResource.java │ ├── resources │ ├── applicationContext.xml │ ├── commons-logging.properties │ ├── sample-oauth.xml │ └── simplelog.properties │ └── webapp │ ├── WEB-INF │ └── jsp │ │ ├── access_confirmation.jsp │ │ └── oauth_error.jsp │ ├── index.jsp │ └── login.jsp └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 | rest-oauth2-sample 2 | ================== 3 | 4 | ### Presentation Slides 5 | http://www.slideshare.net/rcandidosilva/javaone-2014-securing-restful-resources-with-oauth2 6 | -------------------------------------------------------------------------------- /client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | demo 7 | oauth2 8 | 1.0.0 9 | 10 | 11 | client 12 | war 13 | Client 14 | 15 | 16 | 17 | 18 | org.springframework.security.oauth 19 | spring-security-oauth2 20 | 2.0.4.RELEASE 21 | 22 | 23 | 24 | org.springframework.security 25 | spring-security-taglibs 26 | ${spring.security.version} 27 | 28 | 29 | 30 | org.springframework 31 | spring-webmvc 32 | ${spring.version} 33 | 34 | 35 | 36 | org.springframework 37 | spring-web 38 | ${spring.version} 39 | 40 | 41 | 42 | org.springframework 43 | spring-jdbc 44 | ${spring.version} 45 | 46 | 47 | 48 | org.springframework 49 | spring-context 50 | ${spring.version} 51 | 52 | 53 | 54 | org.springframework 55 | spring-aop 56 | ${spring.version} 57 | 58 | 59 | 60 | org.springframework 61 | spring-expression 62 | ${spring.version} 63 | 64 | 65 | 66 | org.springframework 67 | spring-tx 68 | ${spring.version} 69 | 70 | 71 | 72 | org.springframework 73 | spring-test 74 | ${spring.version} 75 | test 76 | 77 | 78 | 79 | com.fasterxml.jackson.core 80 | jackson-databind 81 | 2.3.2 82 | 83 | 84 | 85 | javax.servlet 86 | javax.servlet-api 87 | 3.0.1 88 | provided 89 | 90 | 91 | 92 | javax.servlet 93 | jstl 94 | 1.2 95 | 96 | 97 | 98 | org.webjars 99 | bootstrap 100 | 3.0.3 101 | 102 | 103 | 104 | junit 105 | junit 106 | 4.11 107 | test 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /client/src/main/java/client/ConferenceException.java: -------------------------------------------------------------------------------- 1 | package client; 2 | 3 | public class ConferenceException extends Exception { 4 | 5 | public ConferenceException(String message) { 6 | super(message); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /client/src/main/java/client/Session.java: -------------------------------------------------------------------------------- 1 | package client; 2 | 3 | import javax.xml.bind.annotation.XmlAttribute; 4 | import javax.xml.bind.annotation.XmlRootElement; 5 | 6 | public class Session { 7 | 8 | private String id; 9 | private String title; 10 | private String description; 11 | 12 | public Session() { 13 | super(); 14 | } 15 | 16 | public Session(String id, String title, String description) { 17 | this.id = id; 18 | this.title = title; 19 | this.description = description; 20 | } 21 | 22 | public String getId() { 23 | return id; 24 | } 25 | 26 | public void setId(String id) { 27 | this.id = id; 28 | } 29 | 30 | public String getTitle() { 31 | return title; 32 | } 33 | 34 | public void setTitle(String title) { 35 | this.title = title; 36 | } 37 | 38 | public String getDescription() { 39 | return description; 40 | } 41 | 42 | public void setDescription(String description) { 43 | this.description = description; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /client/src/main/java/client/Speaker.java: -------------------------------------------------------------------------------- 1 | package client; 2 | 3 | import javax.xml.bind.annotation.XmlAttribute; 4 | import javax.xml.bind.annotation.XmlRootElement; 5 | import java.io.Serializable; 6 | 7 | public class Speaker implements Serializable { 8 | 9 | private Long id; 10 | private String name; 11 | private String title; 12 | private String photoUri; 13 | private String twitter; 14 | private String bio; 15 | private String company; 16 | 17 | public Speaker() { 18 | super(); 19 | } 20 | 21 | public Speaker(Long id, String name, String title, String photoUri, String twitter, String bio, String company) { 22 | this.id = id; 23 | this.name = name; 24 | this.title = title; 25 | this.photoUri = photoUri; 26 | this.twitter = twitter; 27 | this.bio = bio; 28 | this.company = company; 29 | } 30 | 31 | public Long getId() { 32 | return id; 33 | } 34 | 35 | public void setId(Long id) { 36 | this.id = id; 37 | } 38 | 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | public void setName(String name) { 44 | this.name = name; 45 | } 46 | 47 | public String getTitle() { 48 | return title; 49 | } 50 | 51 | public void setTitle(String title) { 52 | this.title = title; 53 | } 54 | 55 | public String getPhotoUri() { 56 | return photoUri; 57 | } 58 | 59 | public void setPhotoUri(String photoUri) { 60 | this.photoUri = photoUri; 61 | } 62 | 63 | public String getTwitter() { 64 | return twitter; 65 | } 66 | 67 | public void setTwitter(String twitter) { 68 | this.twitter = twitter; 69 | } 70 | 71 | public String getBio() { 72 | return bio; 73 | } 74 | 75 | public void setBio(String bio) { 76 | this.bio = bio; 77 | } 78 | 79 | public String getCompany() { 80 | return company; 81 | } 82 | 83 | public void setCompany(String company) { 84 | this.company = company; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /client/src/main/java/client/config/ServletInitializer.java: -------------------------------------------------------------------------------- 1 | package client.config; 2 | 3 | import javax.servlet.ServletContext; 4 | import javax.servlet.ServletException; 5 | 6 | import org.springframework.web.context.WebApplicationContext; 7 | import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; 8 | import org.springframework.web.filter.DelegatingFilterProxy; 9 | import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer; 10 | 11 | public class ServletInitializer extends AbstractDispatcherServletInitializer { 12 | 13 | @Override 14 | protected WebApplicationContext createServletApplicationContext() { 15 | AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); 16 | context.register(WebMvcConfig.class); 17 | return context; 18 | } 19 | 20 | @Override 21 | protected String[] getServletMappings() { 22 | return new String[] { "/" }; 23 | } 24 | 25 | @Override 26 | protected WebApplicationContext createRootApplicationContext() { 27 | return null; 28 | } 29 | 30 | @Override 31 | public void onStartup(ServletContext servletContext) throws ServletException { 32 | super.onStartup(servletContext); 33 | registerProxyFilter(servletContext, "oauth2ClientContextFilter"); 34 | } 35 | 36 | private void registerProxyFilter(ServletContext servletContext, String name) { 37 | DelegatingFilterProxy filter = new DelegatingFilterProxy(name); 38 | filter.setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher"); 39 | servletContext.addFilter(name, filter).addMappingForUrlPatterns(null, false, "/*"); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /client/src/main/java/client/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package client.config; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import javax.annotation.Resource; 8 | 9 | import client.mvc.ConferenceController; 10 | import org.springframework.beans.factory.annotation.Qualifier; 11 | import org.springframework.beans.factory.annotation.Value; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.context.annotation.PropertySource; 15 | import org.springframework.context.annotation.Scope; 16 | import org.springframework.context.annotation.ScopedProxyMode; 17 | import org.springframework.context.support.ConversionServiceFactoryBean; 18 | import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; 19 | import org.springframework.http.MediaType; 20 | import org.springframework.http.converter.BufferedImageHttpMessageConverter; 21 | import org.springframework.http.converter.HttpMessageConverter; 22 | import client.converter.AccessTokenRequestConverter; 23 | import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; 24 | import org.springframework.security.oauth2.client.OAuth2RestTemplate; 25 | import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; 26 | import org.springframework.security.oauth2.client.token.AccessTokenRequest; 27 | import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; 28 | import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; 29 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; 30 | import org.springframework.web.accept.ContentNegotiationManagerFactoryBean; 31 | import org.springframework.web.client.RestOperations; 32 | import org.springframework.web.client.RestTemplate; 33 | import org.springframework.web.servlet.View; 34 | import org.springframework.web.servlet.ViewResolver; 35 | import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; 36 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 37 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 38 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 39 | import org.springframework.web.servlet.view.ContentNegotiatingViewResolver; 40 | import org.springframework.web.servlet.view.InternalResourceViewResolver; 41 | import org.springframework.web.servlet.view.json.MappingJacksonJsonView; 42 | 43 | @Configuration 44 | @EnableWebMvc 45 | @PropertySource("classpath:client.properties") 46 | public class WebMvcConfig extends WebMvcConfigurerAdapter { 47 | 48 | @Bean 49 | public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { 50 | return new PropertySourcesPlaceholderConfigurer(); 51 | } 52 | 53 | @Bean 54 | public ContentNegotiatingViewResolver contentViewResolver() throws Exception { 55 | ContentNegotiatingViewResolver contentViewResolver = new ContentNegotiatingViewResolver(); 56 | ContentNegotiationManagerFactoryBean contentNegotiationManager = new ContentNegotiationManagerFactoryBean(); 57 | contentNegotiationManager.addMediaType("json", MediaType.APPLICATION_JSON); 58 | contentViewResolver.setContentNegotiationManager(contentNegotiationManager.getObject()); 59 | contentViewResolver.setDefaultViews(Arrays. asList(new MappingJacksonJsonView())); 60 | return contentViewResolver; 61 | } 62 | 63 | @Bean 64 | public ViewResolver viewResolver() { 65 | InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); 66 | viewResolver.setPrefix("/WEB-INF/jsp/"); 67 | viewResolver.setSuffix(".jsp"); 68 | return viewResolver; 69 | } 70 | 71 | @Override 72 | public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 73 | configurer.enable(); 74 | } 75 | 76 | @Bean 77 | public ConferenceController conferenceController(@Qualifier("conferenceRestTemplate") 78 | RestOperations restTemplate, 79 | @Qualifier("trustedClientRestTemplate") 80 | RestOperations trustedClientRestTemplate, 81 | @Qualifier("unprotectedRestTemplate") 82 | RestOperations unprotectedRestTemplate) { 83 | ConferenceController controller = new ConferenceController(); 84 | controller.setRestTemplate(restTemplate); 85 | controller.setTrustedClientRestTemplate(trustedClientRestTemplate); 86 | controller.setUnprotectedRestTemplate(unprotectedRestTemplate); 87 | return controller; 88 | } 89 | 90 | @Bean 91 | public ConversionServiceFactoryBean conversionService() { 92 | ConversionServiceFactoryBean conversionService = new ConversionServiceFactoryBean(); 93 | conversionService.setConverters(Collections.singleton(new AccessTokenRequestConverter())); 94 | return conversionService; 95 | } 96 | 97 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 98 | registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); 99 | } 100 | 101 | @Override 102 | public void configureMessageConverters(List> converters) { 103 | converters.add(new BufferedImageHttpMessageConverter()); 104 | } 105 | 106 | @Bean 107 | public RestTemplate unprotectedRestTemplate() { 108 | return new RestTemplate(); 109 | } 110 | 111 | @Configuration 112 | @EnableOAuth2Client 113 | protected static class ResourceConfiguration { 114 | 115 | @Value("${accessTokenUri}") 116 | private String accessTokenUri; 117 | 118 | @Value("${userAuthorizationUri}") 119 | private String userAuthorizationUri; 120 | 121 | @Resource 122 | @Qualifier("accessTokenRequest") 123 | private AccessTokenRequest accessTokenRequest; 124 | 125 | @Bean 126 | public OAuth2ProtectedResourceDetails conference() { 127 | AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails(); 128 | details.setId("conference/client"); 129 | details.setClientId("client"); 130 | details.setClientSecret("secret"); 131 | details.setAccessTokenUri(accessTokenUri); 132 | details.setUserAuthorizationUri(userAuthorizationUri); 133 | details.setScope(Arrays.asList("read", "write")); 134 | return details; 135 | } 136 | 137 | @Bean 138 | @Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES) 139 | public OAuth2RestTemplate conferenceRestTemplate() { 140 | return new OAuth2RestTemplate(conference(), new DefaultOAuth2ClientContext(accessTokenRequest)); 141 | } 142 | 143 | 144 | @Bean 145 | public OAuth2ProtectedResourceDetails conferenceRedirect() { 146 | AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails(); 147 | details.setId("conference/client-redirect"); 148 | details.setClientId("client-with-redirect"); 149 | details.setClientSecret("secret"); 150 | details.setAccessTokenUri(accessTokenUri); 151 | details.setUserAuthorizationUri(userAuthorizationUri); 152 | details.setScope(Arrays.asList("read", "write")); 153 | details.setUseCurrentUri(false); 154 | return details; 155 | } 156 | 157 | @Bean 158 | @Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES) 159 | public OAuth2RestTemplate conferenceRedirectRestTemplate() { 160 | return new OAuth2RestTemplate(conferenceRedirect(), new DefaultOAuth2ClientContext(accessTokenRequest)); 161 | } 162 | 163 | @Bean 164 | public OAuth2ProtectedResourceDetails trusted() { 165 | ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails(); 166 | details.setId("conference/trusted"); 167 | details.setClientId("my-client-with-registered-redirect"); 168 | details.setAccessTokenUri(accessTokenUri); 169 | details.setScope(Arrays.asList("trust")); 170 | return details; 171 | } 172 | 173 | @Bean 174 | public OAuth2RestTemplate trustedClientRestTemplate() { 175 | return new OAuth2RestTemplate(trusted(), new DefaultOAuth2ClientContext()); 176 | } 177 | 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /client/src/main/java/client/converter/AccessTokenRequestConverter.java: -------------------------------------------------------------------------------- 1 | package client.converter; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | import org.springframework.core.convert.TypeDescriptor; 8 | import org.springframework.core.convert.converter.GenericConverter; 9 | import org.springframework.security.oauth2.client.token.AccessTokenRequest; 10 | 11 | public class AccessTokenRequestConverter implements GenericConverter { 12 | 13 | private Set convertibleTypes = new HashSet( 14 | Arrays.asList(new ConvertiblePair(AccessTokenRequest.class, AccessTokenRequest.class))); 15 | 16 | public Set getConvertibleTypes() { 17 | return convertibleTypes; 18 | } 19 | 20 | public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { 21 | return source; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /client/src/main/java/client/mvc/ConferenceController.java: -------------------------------------------------------------------------------- 1 | package client.mvc; 2 | 3 | import client.Session; 4 | import client.Speaker; 5 | import org.springframework.http.HttpHeaders; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.http.MediaType; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.http.converter.HttpMessageNotReadableException; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.ui.Model; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.client.RestOperations; 15 | import org.xml.sax.Attributes; 16 | import org.xml.sax.SAXException; 17 | import org.xml.sax.helpers.DefaultHandler; 18 | 19 | import javax.imageio.ImageIO; 20 | import javax.imageio.ImageReadParam; 21 | import javax.imageio.ImageReader; 22 | import javax.imageio.stream.MemoryCacheImageInputStream; 23 | import javax.servlet.UnavailableException; 24 | import javax.xml.parsers.ParserConfigurationException; 25 | import javax.xml.parsers.SAXParser; 26 | import javax.xml.parsers.SAXParserFactory; 27 | import java.awt.image.BufferedImage; 28 | import java.io.ByteArrayInputStream; 29 | import java.io.IOException; 30 | import java.io.InputStream; 31 | import java.net.URI; 32 | import java.util.ArrayList; 33 | import java.util.Iterator; 34 | import java.util.List; 35 | 36 | @Controller 37 | public class ConferenceController { 38 | 39 | private String speakersListURL = "http://localhost:8080/conference/rest/speakers"; 40 | private String sessionsListURL = "http://localhost:8080/conference/rest/sessions"; 41 | private String trustedMessageURL = "http://localhost:8080/conference/rest/trusted/message"; 42 | private String testMessageURL = "http://localhost:8080/conference/rest/test"; 43 | 44 | private RestOperations restTemplate; 45 | private RestOperations trustedClientRestTemplate; 46 | private RestOperations unprotectedRestTemplate; 47 | 48 | @RequestMapping("/conference/speakers") 49 | public String getSpeakers(Model model) throws Exception { 50 | try { 51 | InputStream photosXML = new ByteArrayInputStream(restTemplate.getForObject( 52 | URI.create(speakersListURL), byte[].class)); 53 | 54 | List speakers = new ArrayList(); 55 | SAXParserFactory parserFactory = SAXParserFactory.newInstance(); 56 | SAXParser parser = parserFactory.newSAXParser(); 57 | parser.parse(photosXML, parserSpeaker(speakers)); 58 | 59 | model.addAttribute("speakers", speakers); 60 | 61 | return "speakers"; 62 | } catch (Exception e) { 63 | throw new IllegalStateException(e); 64 | } 65 | } 66 | 67 | @RequestMapping("/conference/sessions") 68 | public String getSessions(Model model) throws Exception { 69 | try { 70 | InputStream photosXML = new ByteArrayInputStream(unprotectedRestTemplate.getForObject( 71 | URI.create(sessionsListURL), byte[].class)); 72 | 73 | List sessions = new ArrayList(); 74 | SAXParserFactory parserFactory = SAXParserFactory.newInstance(); 75 | SAXParser parser = parserFactory.newSAXParser(); 76 | parser.parse(photosXML, parserSessions(sessions)); 77 | 78 | model.addAttribute("sessions", sessions); 79 | 80 | return "sessions"; 81 | } catch (Exception e) { 82 | throw new IllegalStateException(e); 83 | } 84 | } 85 | 86 | 87 | @RequestMapping("/conference/trusted/message") 88 | public String trusted(Model model) throws Exception { 89 | String message = trustedClientRestTemplate.getForObject(URI.create(trustedMessageURL), String.class); 90 | model.addAttribute("message", message); 91 | return "message"; 92 | } 93 | 94 | @RequestMapping("/conference/test") 95 | public String test(Model model) throws Exception { 96 | String message = restTemplate.getForObject(URI.create(testMessageURL), String.class); 97 | model.addAttribute("message", message); 98 | return "message"; 99 | } 100 | 101 | public void setRestTemplate(RestOperations restTemplate) { 102 | this.restTemplate = restTemplate; 103 | } 104 | 105 | public void setTrustedClientRestTemplate(RestOperations trustedClientRestTemplate) { 106 | this.trustedClientRestTemplate = trustedClientRestTemplate; 107 | } 108 | 109 | public void setUnprotectedRestTemplate(RestOperations unprotectedRestTemplate) { 110 | this.unprotectedRestTemplate = unprotectedRestTemplate; 111 | } 112 | 113 | private DefaultHandler parserSpeaker(final List speakers) throws Exception { 114 | return new DefaultHandler() { 115 | @Override 116 | public void startElement(String uri, String localName, String qName, Attributes attributes) 117 | throws SAXException { 118 | if ("speaker".equals(qName)) { 119 | Speaker speaker = new Speaker(); 120 | speaker.setId(new Long(attributes.getValue("id"))); 121 | speaker.setName(attributes.getValue("name")); 122 | speaker.setCompany(attributes.getValue("company")); 123 | speaker.setPhotoUri(attributes.getValue("photoUri")); 124 | speaker.setTitle(attributes.getValue("title")); 125 | speaker.setTwitter(attributes.getValue("twitter")); 126 | speaker.setBio(attributes.getValue("bio")); 127 | speakers.add(speaker); 128 | } 129 | } 130 | }; 131 | } 132 | 133 | private DefaultHandler parserSessions(final List sessions) throws Exception { 134 | return new DefaultHandler() { 135 | @Override 136 | public void startElement(String uri, String localName, String qName, Attributes attributes) 137 | throws SAXException { 138 | if ("session".equals(qName)) { 139 | Session session = new Session(); 140 | session.setId(attributes.getValue("id")); 141 | session.setTitle(attributes.getValue("title")); 142 | session.setDescription(attributes.getValue("description")); 143 | sessions.add(session); 144 | } 145 | } 146 | }; 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /client/src/main/resources/client.properties: -------------------------------------------------------------------------------- 1 | speakersListURL=http://localhost:8080/conference/rest/speakers 2 | sessionListURL=http://localhost:8080/conference/rest/sessions 3 | photoListURL=http://localhost:8080/conference/photos?format=xml 4 | photoURLPattern=http://localhost:8080/conference/photos/%s 5 | trustedMessageURL=http://localhost:8080/conference/photos/trusted/message 6 | accessTokenUri=http://localhost:8080/conference/oauth/token 7 | userAuthorizationUri=http://localhost:8080/conference/oauth/authorize 8 | -------------------------------------------------------------------------------- /client/src/main/resources/commons-logging.properties: -------------------------------------------------------------------------------- 1 | org.apache.commons.logging.LogFactory=org.apache.commons.logging.impl.LogFactoryImpl 2 | org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog -------------------------------------------------------------------------------- /client/src/main/resources/simplelog.properties: -------------------------------------------------------------------------------- 1 | org.apache.commons.logging.simplelog.defaultlog=info 2 | org.apache.commons.logging.simplelog.showdatetime=true 3 | org.apache.commons.logging.simplelog.dateTimeFormat='tonr2' HH:mm:ss.SSS 4 | org.apache.commons.logging.simplelog.log.org.springframework.security=debug 5 | #org.apache.commons.logging.simplelog.log.org.springframework.security.oauth=info 6 | org.apache.commons.logging.simplelog.log.org.springframework.web=debug 7 | -------------------------------------------------------------------------------- /client/src/main/webapp/WEB-INF/jsp/message.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 12 | 14 | Conference Client 15 | 16 | 17 |
18 |

Conference Client

19 | 20 |

${message}

21 |
22 | 23 | -------------------------------------------------------------------------------- /client/src/main/webapp/WEB-INF/jsp/sessions.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="authz" 2 | uri="http://www.springframework.org/security/tags" %> 3 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 14 | 16 | Sessions 17 | 18 | 19 |
20 |

Sessions

21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
IDTitle
${session.id}${session.title}
36 |
37 |
38 | 39 | -------------------------------------------------------------------------------- /client/src/main/webapp/WEB-INF/jsp/speakers.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="authz" 2 | uri="http://www.springframework.org/security/tags"%> 3 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 14 | 16 | Speakers 17 | 18 | 19 |
20 |

Speakers

21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
PhotoNameTitleCompanyTwitter
${speaker.name}${speaker.title}${speaker.company}Twitter
42 |
43 |
44 | 45 | -------------------------------------------------------------------------------- /client/src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 12 | 14 | Conference Web Site 15 | 16 | 17 |
18 |

Conference Web Site

19 | 20 |

21 | ">View speakers
22 | ">View sessions 23 |

24 |
25 | 26 | ${message} 27 | 28 | 29 | -------------------------------------------------------------------------------- /client/src/main/webapp/js/libs/jso.js: -------------------------------------------------------------------------------- 1 | (function(exp, $) { 2 | 3 | var 4 | config = {}, 5 | default_lifetime = 3600; 6 | 7 | 8 | /* 9 | * ------ SECTION: Utilities 10 | */ 11 | 12 | /* 13 | * Returns a random string used for state 14 | */ 15 | var uuid = function() { 16 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 17 | var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 18 | return v.toString(16); 19 | }); 20 | } 21 | 22 | /* 23 | * Takes an URL as input and a params object. 24 | * Each property in the params is added to the url as query string parameters 25 | */ 26 | var encodeURL= function(url, params) { 27 | var res = url; 28 | var k, i = 0; 29 | for(k in params) { 30 | res += (i++ === 0 ? '?' : '&') + encodeURIComponent(k) + '=' + encodeURIComponent(params[k]); 31 | } 32 | return res; 33 | } 34 | 35 | /* 36 | * Returns epoch, seconds since 1970. 37 | * Used for calculation of expire times. 38 | */ 39 | var epoch = function() { 40 | return Math.round(new Date().getTime()/1000.0); 41 | } 42 | 43 | /* 44 | * Redirects the user to a specific URL 45 | */ 46 | var redirect = function(url) { 47 | window.location = url; 48 | // $("body").append('

Go here...

'); 49 | } 50 | 51 | var parseQueryString = function (qs) { 52 | var e, 53 | a = /\+/g, // Regex for replacing addition symbol with a space 54 | r = /([^&;=]+)=?([^&;]*)/g, 55 | d = function (s) { return decodeURIComponent(s.replace(a, " ")); }, 56 | q = qs, 57 | urlParams = {}; 58 | 59 | while (e = r.exec(q)) 60 | urlParams[d(e[1])] = d(e[2]); 61 | 62 | return urlParams; 63 | } 64 | /* 65 | * ------ / SECTION: Utilities 66 | */ 67 | 68 | 69 | 70 | 71 | /* 72 | * ------ SECTION: Storage for Tokens and state 73 | */ 74 | 75 | /* 76 | saveState stores an object with an Identifier. 77 | TODO: Ensure that both localstorage and JSON encoding has fallbacks for ancient browsers. 78 | In the state object, we put the request object, plus these parameters: 79 | * restoreHash 80 | * providerID 81 | * scopes 82 | 83 | */ 84 | var saveState = function(state, obj) { 85 | // console.log("SaveState (" + state+ ")"); 86 | localStorage.setItem("state-" + state, JSON.stringify(obj)); 87 | }; 88 | 89 | /* 90 | * getState returns the state object, but also removes it. 91 | */ 92 | var getState = function(state) { 93 | // console.log("getState (" + state+ ")"); 94 | var obj = JSON.parse(localStorage.getItem("state-" + state)); 95 | localStorage.removeItem("state-" + state) 96 | return obj; 97 | }; 98 | 99 | 100 | 101 | /* 102 | * Checks if a token, has includes a specific scope. 103 | * If token has no scope at all, false is returned. 104 | */ 105 | var hasScope = function(token, scope) { 106 | var i; 107 | if (!token.scopes) return false; 108 | for(i = 0; i < token.scopes.length; i++) { 109 | if (token.scopes[i] === scope) return true; 110 | } 111 | return false; 112 | }; 113 | 114 | /* 115 | * Takes an array of tokens, and removes the ones that 116 | * are expired, and the ones that do not meet a scopes requirement. 117 | */ 118 | var filterTokens = function(tokens, scopes) { 119 | var i, j, 120 | result = [], 121 | now = epoch(), 122 | usethis; 123 | 124 | if (!scopes) scopes = []; 125 | 126 | for(i = 0; i < tokens.length; i++) { 127 | usethis = true; 128 | 129 | // Filter out expired tokens. Tokens that is expired in 1 second from now. 130 | if (tokens[i].expires && tokens[i].expires < (now+1)) usethis = false; 131 | 132 | // Filter out this token if not all scope requirements are met 133 | for(j = 0; j < scopes.length; j++) { 134 | if (!hasScope(tokens[i], scopes[j])) usethis = false; 135 | } 136 | 137 | if (usethis) result.push(tokens[i]); 138 | } 139 | return result; 140 | }; 141 | 142 | /* 143 | * saveTokens() stores a list of tokens for a provider. 144 | 145 | Usually the tokens stored are a plain Access token plus: 146 | * expires : time that the token expires 147 | * providerID: the provider of the access token? 148 | * scopes: an array with the scopes (not string) 149 | */ 150 | var saveTokens = function(provider, tokens) { 151 | // console.log("Save Tokens (" + provider+ ")"); 152 | localStorage.setItem("tokens-" + provider, JSON.stringify(tokens)); 153 | }; 154 | 155 | var getTokens = function(provider) { 156 | // console.log("Get Tokens (" + provider+ ")"); 157 | var tokens = JSON.parse(localStorage.getItem("tokens-" + provider)); 158 | if (!tokens) tokens = []; 159 | 160 | // console.log(tokens) 161 | return tokens; 162 | }; 163 | var wipeTokens = function(provider) { 164 | localStorage.removeItem("tokens-" + provider); 165 | }; 166 | /* 167 | * Save a single token for a provider. 168 | * This also cleans up expired tokens for the same provider. 169 | */ 170 | var saveToken = function(provider, token) { 171 | var tokens = getTokens(provider); 172 | tokens = filterTokens(tokens); 173 | tokens.push(token); 174 | saveTokens(provider, tokens); 175 | }; 176 | 177 | /* 178 | * Get a token if exists for a provider with a set of scopes. 179 | * The scopes parameter is OPTIONAL. 180 | */ 181 | var getToken = function(provider, scopes) { 182 | var tokens = getTokens(provider); 183 | tokens = filterTokens(tokens, scopes); 184 | if (tokens.length < 1) return null; 185 | return tokens[0]; 186 | }; 187 | /* 188 | * ------ /SECTION: Storage for Tokens and state 189 | */ 190 | 191 | 192 | 193 | 194 | 195 | 196 | /** 197 | * Check if the hash contains an access token. 198 | * And if it do, extract the state, compare with 199 | * config, and store the access token for later use. 200 | */ 201 | var jso_checkfortoken = function() { 202 | var 203 | atoken, 204 | h = window.location.hash, 205 | now = epoch(), 206 | state, 207 | co; 208 | 209 | /* 210 | * Start with checking if there is a token in the hash 211 | */ 212 | if (h.length < 2) return; 213 | if (h.indexOf("access_token") === -1) return; 214 | h = h.substring(1); 215 | var atoken = parseQueryString(h); 216 | 217 | if (!atoken.state) return; 218 | 219 | state = getState(atoken.state); 220 | if (!state) throw "Could not retrieve state"; 221 | if (!state.providerID) throw "Could not get providerid from state"; 222 | if (!config[state.providerID]) throw "Could not retrieve config for this provider."; 223 | co = config[state.providerID]; 224 | 225 | /* 226 | * Decide when this token should expire. 227 | * Priority fallback: 228 | * 1. Access token expires_in 229 | * 2. Life time in config (may be false = permanent...) 230 | * 3. Specific permanent scope. 231 | * 4. Default library lifetime: 232 | */ 233 | if (atoken["expires_in"]) { 234 | atoken["expires"] = now + atoken["expires_in"]; 235 | } else if (co["default_lifetime"] === false) { 236 | // Token is permanent. 237 | } else if (co["default_lifetime"]) { 238 | atoken["expires"] = now + co["default_lifetime"]; 239 | } else if (co["permanent_scope"]) { 240 | if (!hasScope(atoken, co["permanent_scope"])) { 241 | atoken["expires"] = now + default_lifetime; 242 | } 243 | } else { 244 | atoken["expires"] = now + default_lifetime; 245 | } 246 | 247 | /* 248 | * Handle scopes for this token 249 | */ 250 | if (atoken["scope"]) { 251 | atoken["scopes"] = atoken["scope"].split(" "); 252 | } else if (state["scopes"]) { 253 | atoken["scopes"] = state["scopes"]; 254 | } 255 | 256 | 257 | saveToken(state.providerID, atoken); 258 | 259 | if (state.restoreHash) { 260 | window.location.hash = state.restoreHash; 261 | } else { 262 | window.location.hash = ''; 263 | } 264 | 265 | // console.log(atoken); 266 | } 267 | 268 | /* 269 | * A config object contains: 270 | */ 271 | var jso_authrequest = function(providerid, scopes) { 272 | 273 | var 274 | state, 275 | request, 276 | authurl, 277 | co; 278 | 279 | if (!config[providerid]) throw "Could not find configuration for provider " + providerid; 280 | co = config[providerid]; 281 | 282 | // console.log("About to send an authorization request to [" + providerid + "]. Config:") 283 | // console.log(co); 284 | 285 | state = uuid(); 286 | request = { 287 | "response_type": "token" 288 | }; 289 | request.state = state; 290 | 291 | 292 | if (co["redirect_uri"]) { 293 | request["redirect_uri"] = co["redirect_uri"]; 294 | } 295 | if (co["client_id"]) { 296 | request["client_id"] = co["client_id"]; 297 | } 298 | if (scopes) { 299 | request["scope"] = scopes.join(" "); 300 | } 301 | 302 | authurl = encodeURL(co.authorization, request); 303 | 304 | // We'd like to cache the hash for not loosing Application state. 305 | // With the implciit grant flow, the hash will be replaced with the access 306 | // token when we return after authorization. 307 | if (window.location.hash) { 308 | request["restoreHash"] = window.location.hash; 309 | } 310 | request["providerID"] = providerid; 311 | if (scopes) { 312 | request["scopes"] = scopes; 313 | } 314 | 315 | // console.log("Saving state [" + state+ "]"); 316 | // console.log(JSON.parse(JSON.stringify(request))); 317 | saveState(state, request); 318 | redirect(authurl); 319 | 320 | }; 321 | 322 | exp.jso_ensureTokens = function (ensure) { 323 | var providerid, scopes, token; 324 | for(providerid in ensure) { 325 | scopes = undefined; 326 | if (ensure[providerid]) scopes = ensure[providerid]; 327 | token = getToken(providerid, scopes); 328 | 329 | // console.log("Ensure token for provider [" + providerid + "] "); 330 | // console.log(token); 331 | 332 | if (token === null) { 333 | jso_authrequest(providerid, scopes); 334 | } 335 | } 336 | return true; 337 | } 338 | 339 | 340 | exp.jso_configure = function(c) { 341 | config = c; 342 | try { 343 | jso_checkfortoken(); 344 | } catch(e) { 345 | console.log("Error when retrieving token from hash: " + e); 346 | window.location.hash = ""; 347 | } 348 | 349 | } 350 | 351 | exp.jso_dump = function() { 352 | var key; 353 | for(key in config) { 354 | console.log("=====> Processing provider [" + key + "]"); 355 | console.log("=] Config"); 356 | console.log(config[key]); 357 | console.log("=] Tokens") 358 | console.log(getTokens(key)); 359 | } 360 | } 361 | 362 | exp.jso_wipe = function() { 363 | var key; 364 | for(key in config) { 365 | wipeTokens(key); 366 | } 367 | } 368 | 369 | exp.jso_getToken = function(providerid, scopes) { 370 | var token = getToken(providerid, scopes); 371 | if (!token) return null; 372 | if (!token["access_token"]) return null; 373 | return token["access_token"]; 374 | } 375 | 376 | $.oajax = function(settings) { 377 | var 378 | allowia, 379 | scopes, 380 | token, 381 | providerid, 382 | co; 383 | 384 | providerid = settings.jso_provider; 385 | allowia = settings.jso_allowia || false; 386 | scopes = settings.jso_scopes; 387 | token = getToken(providerid, scopes); 388 | co = config[providerid]; 389 | 390 | if (!token) { 391 | if (allowia) { 392 | jso_authrequest(providerid, scopes); 393 | return; 394 | } else { 395 | throw "Could not perform AJAX call because no valid tokens was found."; 396 | } 397 | } 398 | 399 | if (co["presenttoken"] && co["presenttoken"] === "qs") { 400 | // settings.url += ((h.indexOf("?") === -1) ? '?' : '&') + "access_token=" + encodeURIComponent(token["access_token"]); 401 | if (!settings.data) settings.data = {}; 402 | settings.data["access_token"] = token["access_token"]; 403 | } else { 404 | if (!settings.headers) settings.headers = {}; 405 | settings.headers["Authorization"] = "Bearer " + token["access_token"]; 406 | } 407 | 408 | $.ajax(settings); 409 | 410 | }; 411 | 412 | 413 | })(window, jQuery); -------------------------------------------------------------------------------- /client/src/main/webapp/oauth_error.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core"%> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 13 | 15 | 16 | 17 |
18 | 19 | -------------------------------------------------------------------------------- /conference/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | demo 7 | oauth2 8 | 1.0.0 9 | 10 | 11 | conference 12 | war 13 | Conference 14 | 15 | 16 | 17 | 18 | 19 | org.springframework.security.oauth 20 | spring-security-oauth2 21 | 2.0.4.RELEASE 22 | 23 | 24 | 25 | 26 | org.glassfish.jersey.ext 27 | jersey-spring3 28 | 2.12 29 | 30 | 31 | 32 | org.glassfish.jersey.ext 33 | jersey-bean-validation 34 | 2.12 35 | 36 | 37 | 38 | 39 | org.springframework.security 40 | spring-security-taglibs 41 | ${spring.security.version} 42 | 43 | 44 | org.springframework 45 | spring-aop 46 | 47 | 48 | 49 | 50 | 51 | org.springframework.security 52 | spring-security-core 53 | ${spring.security.version} 54 | 55 | 56 | 57 | org.springframework.security 58 | spring-security-web 59 | ${spring.security.version} 60 | 61 | 62 | 63 | org.springframework.security 64 | spring-security-config 65 | ${spring.security.version} 66 | 67 | 68 | 69 | 70 | org.springframework 71 | spring-web 72 | ${spring.version} 73 | 74 | 75 | 76 | org.springframework 77 | spring-aop 78 | ${spring.version} 79 | 80 | 81 | 82 | org.springframework 83 | spring-jdbc 84 | ${spring.version} 85 | 86 | 87 | 88 | org.springframework 89 | spring-webmvc 90 | ${spring.version} 91 | 92 | 93 | 94 | org.springframework 95 | spring-test 96 | ${spring.version} 97 | test 98 | 99 | 100 | 101 | org.springframework 102 | spring-tx 103 | ${spring.version} 104 | 105 | 106 | 107 | org.springframework.security 108 | spring-security-taglibs 109 | ${spring.security.version} 110 | 111 | 112 | 113 | com.fasterxml.jackson.core 114 | jackson-databind 115 | 2.3.1 116 | 117 | 118 | 119 | javax.servlet 120 | javax.servlet-api 121 | 3.0.1 122 | provided 123 | 124 | 125 | 126 | javax.servlet 127 | jstl 128 | 1.2 129 | 130 | 131 | 132 | org.webjars 133 | bootstrap 134 | 3.0.3 135 | 136 | 137 | 138 | junit 139 | junit 140 | 4.11 141 | test 142 | 143 | 144 | 145 | org.apache.httpcomponents 146 | httpclient 147 | 4.3.3 148 | test 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/Session.java: -------------------------------------------------------------------------------- 1 | package conference; 2 | 3 | import javax.xml.bind.annotation.XmlAttribute; 4 | import javax.xml.bind.annotation.XmlRootElement; 5 | 6 | @XmlRootElement 7 | public class Session { 8 | 9 | private String id; 10 | private String title; 11 | private String description; 12 | 13 | public Session() { 14 | super(); 15 | } 16 | 17 | public Session(String id, String title, 18 | String description) { 19 | this.id = id; 20 | this.title = title; 21 | this.description = description; 22 | } 23 | 24 | @XmlAttribute 25 | public String getId() { 26 | return id; 27 | } 28 | 29 | public void setId(String id) { 30 | this.id = id; 31 | } 32 | 33 | @XmlAttribute 34 | public String getTitle() { 35 | return title; 36 | } 37 | 38 | public void setTitle(String title) { 39 | this.title = title; 40 | } 41 | 42 | @XmlAttribute 43 | public String getDescription() { 44 | return description; 45 | } 46 | 47 | public void setDescription(String description) { 48 | this.description = description; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/Speaker.java: -------------------------------------------------------------------------------- 1 | package conference; 2 | 3 | import javax.xml.bind.annotation.XmlAttribute; 4 | import javax.xml.bind.annotation.XmlRootElement; 5 | import java.io.Serializable; 6 | 7 | @XmlRootElement 8 | public class Speaker implements Serializable { 9 | 10 | private Long id; 11 | private String name; 12 | private String title; 13 | private String photoUri; 14 | private String twitter; 15 | private String bio; 16 | private String company; 17 | 18 | public Speaker() { 19 | super(); 20 | } 21 | 22 | public Speaker(Long id, String name, String title, 23 | String photoUri, String twitter, 24 | String bio, String company) { 25 | this.id = id; 26 | this.name = name; 27 | this.title = title; 28 | this.photoUri = photoUri; 29 | this.twitter = twitter; 30 | this.bio = bio; 31 | this.company = company; 32 | } 33 | 34 | @XmlAttribute 35 | public Long getId() { 36 | return id; 37 | } 38 | 39 | public void setId(Long id) { 40 | this.id = id; 41 | } 42 | 43 | @XmlAttribute 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | public void setName(String name) { 49 | this.name = name; 50 | } 51 | 52 | @XmlAttribute 53 | public String getTitle() { 54 | return title; 55 | } 56 | 57 | public void setTitle(String title) { 58 | this.title = title; 59 | } 60 | 61 | @XmlAttribute 62 | public String getPhotoUri() { 63 | return photoUri; 64 | } 65 | 66 | public void setPhotoUri(String photoUri) { 67 | this.photoUri = photoUri; 68 | } 69 | 70 | @XmlAttribute 71 | public String getTwitter() { 72 | return twitter; 73 | } 74 | 75 | public void setTwitter(String twitter) { 76 | this.twitter = twitter; 77 | } 78 | 79 | @XmlAttribute 80 | public String getBio() { 81 | return bio; 82 | } 83 | 84 | public void setBio(String bio) { 85 | this.bio = bio; 86 | } 87 | 88 | @XmlAttribute 89 | public String getCompany() { 90 | return company; 91 | } 92 | 93 | public void setCompany(String company) { 94 | this.company = company; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/config/AuthorizationServerConfig.java: -------------------------------------------------------------------------------- 1 | package conference.config; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.beans.factory.annotation.Qualifier; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.security.authentication.AuthenticationManager; 9 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 10 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 11 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 12 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 13 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; 14 | import org.springframework.security.oauth2.provider.approval.UserApprovalHandler; 15 | import org.springframework.security.oauth2.provider.token.TokenStore; 16 | import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; 17 | 18 | @Configuration 19 | @EnableAuthorizationServer 20 | public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { 21 | 22 | private static final String RESOURCE_ID = "conference"; 23 | 24 | @Autowired 25 | private TokenStore tokenStore; 26 | 27 | @Autowired 28 | private UserApprovalHandler userApprovalHandler; 29 | 30 | @Autowired 31 | @Qualifier("authenticationManagerBean") 32 | private AuthenticationManager authenticationManager; 33 | 34 | @Value("${redirect:http://localhost:8080/client/conference/redirect}") 35 | private String redirectUri; 36 | 37 | @Override 38 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 39 | clients.inMemory().withClient("client") 40 | .resourceIds(RESOURCE_ID) 41 | .authorizedGrantTypes("authorization_code", "implicit") 42 | .authorities("ROLE_CLIENT") 43 | .scopes("read", "write") 44 | .secret("secret") 45 | .and() 46 | .withClient("client-with-redirect") 47 | .resourceIds(RESOURCE_ID) 48 | .authorizedGrantTypes("authorization_code", "implicit") 49 | .authorities("ROLE_CLIENT") 50 | .scopes("read", "write") 51 | .secret("secret") 52 | .redirectUris(redirectUri) 53 | .and() 54 | .withClient("my-client-with-registered-redirect") 55 | .resourceIds(RESOURCE_ID) 56 | .authorizedGrantTypes("authorization_code", "client_credentials") 57 | .authorities("ROLE_CLIENT") 58 | .scopes("read", "trust") 59 | .redirectUris("http://anywhere?key=value"); 60 | } 61 | 62 | @Bean 63 | public TokenStore tokenStore() { 64 | return new InMemoryTokenStore(); 65 | } 66 | 67 | @Override 68 | public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 69 | endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler) 70 | .authenticationManager(authenticationManager); 71 | } 72 | 73 | @Override 74 | public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { 75 | oauthServer.realm("conference/client"); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/config/MethodSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package conference.config; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; 6 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 7 | import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; 8 | import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler; 9 | 10 | @Configuration 11 | @EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true) 12 | public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { 13 | 14 | @Autowired 15 | private SecurityConfiguration securityConfig; 16 | 17 | @Override 18 | protected MethodSecurityExpressionHandler createExpressionHandler() { 19 | return new OAuth2MethodSecurityExpressionHandler(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/config/OAuth2ServerConfig.java: -------------------------------------------------------------------------------- 1 | package conference.config; 2 | 3 | import conference.oauth.UserApprovalHandler; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.annotation.*; 6 | import org.springframework.core.annotation.Order; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 9 | import org.springframework.security.oauth2.provider.ClientDetailsService; 10 | import org.springframework.security.oauth2.provider.approval.ApprovalStore; 11 | import org.springframework.security.oauth2.provider.approval.TokenApprovalStore; 12 | import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory; 13 | import org.springframework.security.oauth2.provider.token.TokenStore; 14 | 15 | @Configuration 16 | public class OAuth2ServerConfig { 17 | 18 | @Autowired 19 | private ClientDetailsService clientDetailsService; 20 | 21 | @Autowired 22 | private TokenStore tokenStore; 23 | 24 | 25 | @Configuration 26 | @Order(10) 27 | protected static class UiResourceConfiguration 28 | extends WebSecurityConfigurerAdapter { 29 | @Override 30 | protected void configure(HttpSecurity http) throws Exception { 31 | http.requestMatchers().antMatchers("/rest/**") 32 | .and().authorizeRequests() 33 | .antMatchers("/rest/speakers").access("hasRole('ROLE_USER')") 34 | .antMatchers("/rest/test").access("hasRole('ROLE_USER')") 35 | .antMatchers("/rest/trusted/**").access("hasRole('ROLE_USER')"); 36 | } 37 | } 38 | 39 | 40 | @Bean 41 | public ApprovalStore approvalStore() throws Exception { 42 | TokenApprovalStore store = new TokenApprovalStore(); 43 | store.setTokenStore(tokenStore); 44 | return store; 45 | } 46 | 47 | @Bean 48 | @Lazy 49 | @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 50 | public UserApprovalHandler userApprovalHandler() throws Exception { 51 | UserApprovalHandler handler = new UserApprovalHandler(); 52 | handler.setApprovalStore(approvalStore()); 53 | handler.setRequestFactory( 54 | new DefaultOAuth2RequestFactory(clientDetailsService)); 55 | handler.setClientDetailsService(clientDetailsService); 56 | handler.setUseApprovalStore(true); 57 | return handler; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/config/ResourceServerConfig.java: -------------------------------------------------------------------------------- 1 | package conference.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.http.HttpMethod; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 7 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; 8 | import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; 9 | 10 | @Configuration 11 | @EnableResourceServer 12 | public class ResourceServerConfig extends ResourceServerConfigurerAdapter { 13 | 14 | private static final String RESOURCE_ID = "conference"; 15 | 16 | @Override 17 | public void configure(ResourceServerSecurityConfigurer resources) { 18 | resources.resourceId(RESOURCE_ID); 19 | } 20 | 21 | @Override 22 | public void configure(HttpSecurity http) throws Exception { 23 | http.requestMatchers().antMatchers("/rest/**", "/oauth/users/**", 24 | "/oauth/clients/**") 25 | .and().authorizeRequests() 26 | .antMatchers("/rest/speakers").access("#oauth2.hasScope('read')") 27 | .antMatchers("/rest/test").access("#oauth2.hasScope('read')") 28 | .antMatchers("/rest/trusted/**").access("#oauth2.hasScope('trust')") 29 | .regexMatchers(HttpMethod.DELETE, "/oauth/users/([^/].*?)/tokens/.*") 30 | .access("#oauth2.clientHasRole('ROLE_CLIENT') " + 31 | "and (hasRole('ROLE_USER') " + 32 | "or #oauth2.isClient()) and #oauth2.hasScope('write')") 33 | .regexMatchers(HttpMethod.GET, "/oauth/clients/.*") 34 | .access("#oauth2.clientHasRole('ROLE_CLIENT') " + 35 | "and #oauth2.isClient() " + 36 | "and #oauth2.hasScope('read')"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/config/SecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package conference.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.authentication.AuthenticationManager; 6 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.builders.WebSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 11 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 12 | 13 | @Configuration 14 | @EnableWebSecurity 15 | public class SecurityConfiguration extends WebSecurityConfigurerAdapter { 16 | 17 | @Override 18 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 19 | auth 20 | .inMemoryAuthentication() 21 | .withUser("duke").password("java").roles("USER") 22 | .and() 23 | .withUser("rodrigo").password("123456").roles("USER"); 24 | } 25 | 26 | @Override 27 | public void configure(WebSecurity web) throws Exception { 28 | web.ignoring().antMatchers("/webjars/**", "/images/**", 29 | "/oauth/uncache_approvals", "/oauth/cache_approvals"); 30 | } 31 | 32 | @Override 33 | @Bean 34 | public AuthenticationManager authenticationManagerBean() throws Exception { 35 | return super.authenticationManagerBean(); 36 | } 37 | 38 | @Override 39 | protected void configure(HttpSecurity http) throws Exception { 40 | http.authorizeRequests().antMatchers("/login.jsp").permitAll().and() 41 | .authorizeRequests() 42 | .anyRequest().hasRole("USER") 43 | .and() 44 | .exceptionHandling() 45 | .accessDeniedPage("/login.jsp?authorization_error=true") 46 | .and() 47 | .csrf() 48 | .requireCsrfProtectionMatcher( 49 | new AntPathRequestMatcher("/oauth/authorize")).disable() 50 | .logout() 51 | .logoutSuccessUrl("/index.jsp") 52 | .logoutUrl("/logout.do") 53 | .and() 54 | .formLogin() 55 | .usernameParameter("j_username") 56 | .passwordParameter("j_password") 57 | .failureUrl("/login.jsp?authentication_error=true") 58 | .loginPage("/login.jsp") 59 | .loginProcessingUrl("/login.do"); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/config/ServletInitializer.java: -------------------------------------------------------------------------------- 1 | package conference.config; 2 | 3 | import conference.rest.Application; 4 | import org.glassfish.jersey.servlet.ServletContainer; 5 | import org.springframework.util.ClassUtils; 6 | import org.springframework.web.context.WebApplicationContext; 7 | import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; 8 | import org.springframework.web.filter.DelegatingFilterProxy; 9 | import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer; 10 | 11 | import javax.servlet.ServletContext; 12 | import javax.servlet.ServletException; 13 | import javax.servlet.ServletRegistration; 14 | 15 | public class ServletInitializer extends AbstractDispatcherServletInitializer { 16 | 17 | @Override 18 | protected WebApplicationContext createServletApplicationContext() { 19 | AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); 20 | context.scan(ClassUtils.getPackageName(getClass())); 21 | return context; 22 | } 23 | 24 | @Override 25 | protected String[] getServletMappings() { 26 | return new String[]{"/"}; 27 | } 28 | 29 | @Override 30 | protected WebApplicationContext createRootApplicationContext() { 31 | return null; 32 | } 33 | 34 | @Override 35 | public void onStartup(ServletContext servletContext) throws ServletException { 36 | super.onStartup(servletContext); 37 | 38 | // Register Jersey 2.0 servlet 39 | ServletRegistration.Dynamic servletRegistration = servletContext.addServlet("Jersey", ServletContainer.class.getName()); 40 | servletRegistration.addMapping("/rest/*"); 41 | servletRegistration.setLoadOnStartup(1); 42 | servletRegistration.setInitParameter("javax.ws.rs.Application", Application.class.getName()); 43 | 44 | DelegatingFilterProxy filter = new DelegatingFilterProxy("springSecurityFilterChain"); 45 | filter.setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher"); 46 | servletContext.addFilter("springSecurityFilterChain", filter).addMappingForUrlPatterns(null, false, "/*"); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package conference.config; 2 | 3 | import conference.oauth.AccessConfirmationController; 4 | import conference.oauth.AdminController; 5 | import conference.oauth.UserApprovalHandler; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.security.oauth2.provider.ClientDetailsService; 11 | import org.springframework.security.oauth2.provider.approval.ApprovalStore; 12 | import org.springframework.security.oauth2.provider.token.ConsumerTokenServices; 13 | import org.springframework.security.oauth2.provider.token.TokenStore; 14 | import org.springframework.web.accept.ContentNegotiationManagerFactoryBean; 15 | import org.springframework.web.servlet.View; 16 | import org.springframework.web.servlet.ViewResolver; 17 | import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; 18 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 19 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 20 | import org.springframework.web.servlet.view.ContentNegotiatingViewResolver; 21 | import org.springframework.web.servlet.view.InternalResourceViewResolver; 22 | import org.springframework.web.servlet.view.json.MappingJackson2JsonView; 23 | 24 | import java.util.Arrays; 25 | 26 | @Configuration 27 | @EnableWebMvc 28 | public class WebMvcConfig extends WebMvcConfigurerAdapter { 29 | 30 | @Bean 31 | public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { 32 | return new PropertySourcesPlaceholderConfigurer(); 33 | } 34 | 35 | @Bean 36 | public ContentNegotiatingViewResolver contentViewResolver() throws Exception { 37 | ContentNegotiationManagerFactoryBean contentNegotiationManager = new ContentNegotiationManagerFactoryBean(); 38 | contentNegotiationManager.addMediaType("json", MediaType.APPLICATION_JSON); 39 | 40 | InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); 41 | viewResolver.setPrefix("/WEB-INF/jsp/"); 42 | viewResolver.setSuffix(".jsp"); 43 | 44 | MappingJackson2JsonView defaultView = new MappingJackson2JsonView(); 45 | defaultView.setExtractValueFromSingleKeyModel(true); 46 | 47 | ContentNegotiatingViewResolver contentViewResolver = new ContentNegotiatingViewResolver(); 48 | contentViewResolver.setContentNegotiationManager(contentNegotiationManager.getObject()); 49 | contentViewResolver.setViewResolvers(Arrays.asList(viewResolver)); 50 | contentViewResolver.setDefaultViews(Arrays.asList(defaultView)); 51 | return contentViewResolver; 52 | } 53 | 54 | @Bean 55 | public AccessConfirmationController accessConfirmationController(ClientDetailsService clientDetailsService, ApprovalStore approvalStore) { 56 | AccessConfirmationController accessConfirmationController = new AccessConfirmationController(); 57 | accessConfirmationController.setClientDetailsService(clientDetailsService); 58 | accessConfirmationController.setApprovalStore(approvalStore); 59 | return accessConfirmationController; 60 | } 61 | 62 | 63 | @Bean 64 | public AdminController adminController(TokenStore tokenStore, ConsumerTokenServices consumerTokenServices, 65 | UserApprovalHandler userApprovalHandler) { 66 | AdminController adminController = new AdminController(); 67 | adminController.setTokenStore(tokenStore); 68 | adminController.setTokenServices(consumerTokenServices); 69 | adminController.setUserApprovalHandler(userApprovalHandler); 70 | return adminController; 71 | } 72 | 73 | @Override 74 | public void configureDefaultServletHandling( 75 | DefaultServletHandlerConfigurer configurer) { 76 | configurer.enable(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/oauth/AccessConfirmationController.java: -------------------------------------------------------------------------------- 1 | package conference.oauth; 2 | 3 | import java.security.Principal; 4 | import java.util.LinkedHashMap; 5 | import java.util.Map; 6 | 7 | import org.springframework.security.oauth2.common.util.OAuth2Utils; 8 | import org.springframework.security.oauth2.provider.AuthorizationRequest; 9 | import org.springframework.security.oauth2.provider.ClientDetails; 10 | import org.springframework.security.oauth2.provider.ClientDetailsService; 11 | import org.springframework.security.oauth2.provider.approval.Approval; 12 | import org.springframework.security.oauth2.provider.approval.Approval.ApprovalStatus; 13 | import org.springframework.security.oauth2.provider.approval.ApprovalStore; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.SessionAttributes; 17 | import org.springframework.web.servlet.ModelAndView; 18 | 19 | @Controller 20 | @SessionAttributes("authorizationRequest") 21 | public class AccessConfirmationController { 22 | 23 | private ClientDetailsService clientDetailsService; 24 | 25 | private ApprovalStore approvalStore; 26 | 27 | @RequestMapping("/oauth/confirm_access") 28 | public ModelAndView getAccessConfirmation(Map model, Principal principal) throws Exception { 29 | AuthorizationRequest clientAuth = (AuthorizationRequest) model.remove("authorizationRequest"); 30 | ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId()); 31 | model.put("auth_request", clientAuth); 32 | model.put("client", client); 33 | Map scopes = new LinkedHashMap(); 34 | for (String scope : clientAuth.getScope()) { 35 | scopes.put(OAuth2Utils.SCOPE_PREFIX + scope, "false"); 36 | } 37 | for (Approval approval : approvalStore.getApprovals(principal.getName(), client.getClientId())) { 38 | if (clientAuth.getScope().contains(approval.getScope())) { 39 | scopes.put(OAuth2Utils.SCOPE_PREFIX + approval.getScope(), 40 | approval.getStatus() == ApprovalStatus.APPROVED ? "true" : "false"); 41 | } 42 | } 43 | model.put("scopes", scopes); 44 | return new ModelAndView("access_confirmation", model); 45 | } 46 | 47 | @RequestMapping("/oauth/error") 48 | public String handleError(Map model) throws Exception { 49 | // We can add more stuff to the model here for JSP rendering. If the client was a machine then 50 | // the JSON will already have been rendered. 51 | model.put("message", "There was a problem with the OAuth2 protocol"); 52 | return "oauth_error"; 53 | } 54 | 55 | public void setClientDetailsService(ClientDetailsService clientDetailsService) { 56 | this.clientDetailsService = clientDetailsService; 57 | } 58 | 59 | public void setApprovalStore(ApprovalStore approvalStore) { 60 | this.approvalStore = approvalStore; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/oauth/AdminController.java: -------------------------------------------------------------------------------- 1 | package conference.oauth; 2 | 3 | import java.security.Principal; 4 | import java.util.ArrayList; 5 | import java.util.Collection; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import conference.oauth.UserApprovalHandler; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.security.access.AccessDeniedException; 13 | import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; 14 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 15 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 16 | import org.springframework.security.oauth2.provider.token.ConsumerTokenServices; 17 | import org.springframework.security.oauth2.provider.token.TokenStore; 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.web.bind.annotation.PathVariable; 20 | import org.springframework.web.bind.annotation.RequestMapping; 21 | import org.springframework.web.bind.annotation.RequestMethod; 22 | import org.springframework.web.bind.annotation.ResponseBody; 23 | import org.springframework.web.bind.annotation.ResponseStatus; 24 | 25 | @Controller 26 | public class AdminController { 27 | 28 | private ConsumerTokenServices tokenServices; 29 | 30 | private TokenStore tokenStore; 31 | 32 | private UserApprovalHandler userApprovalHandler; 33 | 34 | @RequestMapping("/oauth/cache_approvals") 35 | @ResponseStatus(HttpStatus.NO_CONTENT) 36 | public void startCaching() throws Exception { 37 | userApprovalHandler.setUseApprovalStore(true); 38 | } 39 | 40 | @RequestMapping("/oauth/uncache_approvals") 41 | @ResponseStatus(HttpStatus.NO_CONTENT) 42 | public void stopCaching() throws Exception { 43 | userApprovalHandler.setUseApprovalStore(false); 44 | } 45 | 46 | @RequestMapping("/oauth/clients/{client}/users/{user}/tokens") 47 | @ResponseBody 48 | public Collection listTokensForUser(@PathVariable String client, @PathVariable String user, 49 | Principal principal) throws Exception { 50 | checkResourceOwner(user, principal); 51 | return enhance(tokenStore.findTokensByClientIdAndUserName(client, user)); 52 | } 53 | 54 | @RequestMapping(value = "/oauth/users/{user}/tokens/{token}", method = RequestMethod.DELETE) 55 | public ResponseEntity revokeToken(@PathVariable String user, @PathVariable String token, Principal principal) 56 | throws Exception { 57 | checkResourceOwner(user, principal); 58 | if (tokenServices.revokeToken(token)) { 59 | return new ResponseEntity(HttpStatus.NO_CONTENT); 60 | } 61 | else { 62 | return new ResponseEntity(HttpStatus.NOT_FOUND); 63 | } 64 | } 65 | 66 | @RequestMapping("/oauth/clients/{client}/tokens") 67 | @ResponseBody 68 | public Collection listTokensForClient(@PathVariable String client) throws Exception { 69 | return enhance(tokenStore.findTokensByClientId(client)); 70 | } 71 | 72 | private Collection enhance(Collection tokens) { 73 | Collection result = new ArrayList(); 74 | for (OAuth2AccessToken prototype : tokens) { 75 | DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(prototype); 76 | OAuth2Authentication authentication = tokenStore.readAuthentication(token); 77 | if (authentication == null) { 78 | continue; 79 | } 80 | String clientId = authentication.getOAuth2Request().getClientId(); 81 | if (clientId != null) { 82 | Map map = new HashMap(token.getAdditionalInformation()); 83 | map.put("client_id", clientId); 84 | token.setAdditionalInformation(map); 85 | result.add(token); 86 | } 87 | } 88 | return result; 89 | } 90 | 91 | private void checkResourceOwner(String user, Principal principal) { 92 | if (principal instanceof OAuth2Authentication) { 93 | OAuth2Authentication authentication = (OAuth2Authentication) principal; 94 | if (!authentication.isClientOnly() && !user.equals(principal.getName())) { 95 | throw new AccessDeniedException(String.format("User '%s' cannot obtain tokens for user '%s'", 96 | principal.getName(), user)); 97 | } 98 | } 99 | } 100 | 101 | /** 102 | * @param userApprovalHandler the userApprovalHandler to set 103 | */ 104 | public void setUserApprovalHandler(UserApprovalHandler userApprovalHandler) { 105 | this.userApprovalHandler = userApprovalHandler; 106 | } 107 | 108 | /** 109 | * @param tokenServices the consumerTokenServices to set 110 | */ 111 | public void setTokenServices(ConsumerTokenServices tokenServices) { 112 | this.tokenServices = tokenServices; 113 | } 114 | 115 | /** 116 | * @param tokenStore the tokenStore to set 117 | */ 118 | public void setTokenStore(TokenStore tokenStore) { 119 | this.tokenStore = tokenStore; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/oauth/UserApprovalHandler.java: -------------------------------------------------------------------------------- 1 | package conference.oauth; 2 | 3 | import java.util.Collection; 4 | 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.oauth2.provider.AuthorizationRequest; 7 | import org.springframework.security.oauth2.provider.ClientDetails; 8 | import org.springframework.security.oauth2.provider.ClientDetailsService; 9 | import org.springframework.security.oauth2.provider.ClientRegistrationException; 10 | import org.springframework.security.oauth2.provider.approval.ApprovalStoreUserApprovalHandler; 11 | 12 | public class UserApprovalHandler extends ApprovalStoreUserApprovalHandler { 13 | 14 | private boolean useApprovalStore = true; 15 | 16 | private ClientDetailsService clientDetailsService; 17 | 18 | /** 19 | * Service to load client details (optional) for auto approval checks. 20 | * 21 | * @param clientDetailsService a client details service 22 | */ 23 | public void setClientDetailsService(ClientDetailsService clientDetailsService) { 24 | this.clientDetailsService = clientDetailsService; 25 | super.setClientDetailsService(clientDetailsService); 26 | } 27 | 28 | /** 29 | * @param useApprovalStore the useTokenServices to set 30 | */ 31 | public void setUseApprovalStore(boolean useApprovalStore) { 32 | this.useApprovalStore = useApprovalStore; 33 | } 34 | 35 | /** 36 | * Allows automatic approval for a white list of clients in the implicit grant case. 37 | * 38 | * @param authorizationRequest The authorization request. 39 | * @param userAuthentication the current user authentication 40 | * 41 | * @return An updated request if it has already been approved by the current user. 42 | */ 43 | @Override 44 | public AuthorizationRequest checkForPreApproval(AuthorizationRequest authorizationRequest, 45 | Authentication userAuthentication) { 46 | 47 | boolean approved = false; 48 | // If we are allowed to check existing approvals this will short circuit the decision 49 | if (useApprovalStore) { 50 | authorizationRequest = super.checkForPreApproval(authorizationRequest, userAuthentication); 51 | approved = authorizationRequest.isApproved(); 52 | } 53 | else { 54 | if (clientDetailsService != null) { 55 | Collection requestedScopes = authorizationRequest.getScope(); 56 | try { 57 | ClientDetails client = clientDetailsService 58 | .loadClientByClientId(authorizationRequest.getClientId()); 59 | for (String scope : requestedScopes) { 60 | if (client.isAutoApprove(scope) || client.isAutoApprove("all")) { 61 | approved = true; 62 | break; 63 | } 64 | } 65 | } 66 | catch (ClientRegistrationException e) { 67 | } 68 | } 69 | } 70 | authorizationRequest.setApproved(approved); 71 | 72 | return authorizationRequest; 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/rest/Application.java: -------------------------------------------------------------------------------- 1 | package conference.rest; 2 | 3 | import org.glassfish.jersey.server.ResourceConfig; 4 | 5 | public class Application extends ResourceConfig { 6 | 7 | public Application() { 8 | register(JerseyResource.class); 9 | register(ConferenceService.class); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/rest/ConferenceService.java: -------------------------------------------------------------------------------- 1 | package conference.rest; 2 | 3 | import conference.Session; 4 | import conference.Speaker; 5 | import org.springframework.security.access.prepost.PreAuthorize; 6 | import org.springframework.stereotype.Service; 7 | 8 | import javax.annotation.PostConstruct; 9 | import javax.ws.rs.GET; 10 | import javax.ws.rs.Path; 11 | import javax.ws.rs.PathParam; 12 | import javax.ws.rs.Produces; 13 | import javax.ws.rs.core.MediaType; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | @Service 18 | @Path("/") 19 | public class ConferenceService { 20 | 21 | //list of conference speakers available 22 | private List speakers; 23 | //list of conference sessions available 24 | private List sessions; 25 | 26 | @GET 27 | @Path("/test") 28 | @Produces(MediaType.TEXT_PLAIN) 29 | public String test() { 30 | return "test"; 31 | } 32 | 33 | @GET 34 | @Path("/speakers") 35 | @Produces(MediaType.APPLICATION_XML) 36 | public List getSpeakers() { 37 | return speakers; 38 | } 39 | 40 | @GET 41 | @Path("/speakers/{id}") 42 | @Produces(MediaType.APPLICATION_JSON) 43 | public Speaker getSpeaker(@PathParam("id") Long id) { 44 | for (Speaker s : speakers) { 45 | if (s.getId().equals(id)) { 46 | return s; 47 | } 48 | } 49 | return null; 50 | } 51 | 52 | @GET 53 | @Path("/sessions") 54 | @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) 55 | public List getSessions() { 56 | return sessions; 57 | } 58 | 59 | @GET 60 | @Path("/sessions/{id}") 61 | @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) 62 | public Session getSession(String id) { 63 | for (Session s : sessions) { 64 | if (s.getId().equals(id)) { 65 | return s; 66 | } 67 | } 68 | return null; 69 | } 70 | 71 | @GET 72 | @Path("/trusted/message") 73 | @Produces(MediaType.TEXT_PLAIN) 74 | @PreAuthorize("#oauth2.clientHasRole('ROLE_CLIENT')") 75 | public String getTrustedClientMessage() { 76 | return "Hello, Trusted Client"; 77 | } 78 | 79 | @PostConstruct 80 | public void init() { 81 | loadSpeakers(); 82 | loadSessions(); 83 | } 84 | 85 | public void loadSpeakers() { 86 | if (speakers == null) { 87 | //mock all the conference speakers data 88 | speakers = new ArrayList(); 89 | speakers.add(new Speaker(1l, "Reza Rahman", "Java Evangelist", 90 | "http://confoo.ca/images/speakers/2015/reza-rahman.jpg", 91 | "http://twitter.com/reza_rahman", null, "Oracle")); 92 | speakers.add(new Speaker(2l, "Hanneli Tavante", "Senior Software Developer", 93 | "http://confoo.ca/images/speakers/2015/hanneli-tavante.jpg", 94 | "http://twitter.com/hannelita", null, "CodeMiner 42")); 95 | speakers.add(new Speaker(3l, "Rodrigo Cândido da Silva", "Software Architect", 96 | "http://confoo.ca/images/speakers/2015/rodrigo-candido-da-silva.jpg", 97 | "http://twitter.com/rcandidosilva", null, "Integritas")); 98 | speakers.add(new Speaker(4l, "Eduardo Shiota", "Senior Front-end Developer", 99 | "http://confoo.ca/images/speakers/2015/eduardo-shiota-yasuda.jpg", 100 | "http://twitter.com/shiota", null, "Booking.com")); 101 | } 102 | } 103 | 104 | public void loadSessions() { 105 | if (sessions == null) { 106 | //mock all the conference sessions data 107 | sessions = new ArrayList(); 108 | sessions.add(new Session("CONFOO01", "JMS.Next(): JMS 2 and Beyond", null)); 109 | sessions.add(new Session("CONFOO02", "Java EE 8 - Future, Wishes and Predictions", null)); 110 | sessions.add(new Session("CONFOO03", "Supporting Multi-Tenancy Applications with Java EE", null)); 111 | sessions.add(new Session("CONFOO04", "Modular JavaScript Heaven with AMD and Events", null)); 112 | 113 | } 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /conference/src/main/java/conference/rest/JerseyResource.java: -------------------------------------------------------------------------------- 1 | package conference.rest; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.Produces; 6 | import javax.ws.rs.core.MediaType; 7 | 8 | @Path("hello") 9 | public class JerseyResource { 10 | 11 | @GET 12 | @Produces(MediaType.TEXT_PLAIN) 13 | public String getHello() { 14 | return "hello Jersey"; 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /conference/src/main/resources/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /conference/src/main/resources/commons-logging.properties: -------------------------------------------------------------------------------- 1 | org.apache.commons.logging.LogFactory=org.apache.commons.logging.impl.LogFactoryImpl 2 | org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog -------------------------------------------------------------------------------- /conference/src/main/resources/sample-oauth.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 98 | 99 | 100 | 102 | 103 | 104 | 105 | 107 | 108 | 109 | 110 | 111 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 130 | 131 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /conference/src/main/resources/simplelog.properties: -------------------------------------------------------------------------------- 1 | org.apache.commons.logging.simplelog.defaultlog=info 2 | org.apache.commons.logging.simplelog.showdatetime=true 3 | org.apache.commons.logging.simplelog.dateTimeFormat='conference' HH:mm:ss.SSS 4 | #org.apache.commons.logging.simplelog.log.org.springframework.web=debug 5 | org.apache.commons.logging.simplelog.log.org.springframework.security=debug 6 | org.apache.commons.logging.simplelog.log.org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource=info 7 | -------------------------------------------------------------------------------- /conference/src/main/webapp/WEB-INF/jsp/access_confirmation.jsp: -------------------------------------------------------------------------------- 1 | <%@ page import="org.springframework.security.core.AuthenticationException" %> 2 | <%@ page import="org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter" %> 3 | <%@ page import="org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException" %> 4 | <%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %> 5 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 6 | 7 | 8 | 9 | 10 | 11 | 12 | Conference Server 13 | 15 | 17 | 19 | 20 | 21 | 22 | 23 |
24 |

Conference Server

25 | <% 26 | if (session.getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY) != null 27 | && !(session 28 | .getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY) instanceof UnapprovedClientAuthenticationException)) { 29 | %> 30 |
31 |

Woops!

32 | 33 |

34 | Access could not be granted. (<%=((AuthenticationException) session 35 | .getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY)) 36 | .getMessage()%>) 37 |

38 |
39 | <% 40 | } 41 | %> 42 | 43 | 44 | 45 |

46 | You hereby authorize "" to access your protected resources. 47 |

48 | 49 |
51 | 52 |
    53 | 54 | 55 | checked 56 | 57 | 58 | checked 59 | 60 |
  • 61 |
    62 | ${scope.key}: Approve Deny 66 |
    67 |
  • 68 |
    69 |
70 | 71 |
72 |
73 | 74 |
75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /conference/src/main/webapp/WEB-INF/jsp/oauth_error.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 2 | 3 | 4 | 5 | 6 | 7 | Conference Server 8 | 10 | 11 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |

Conference Server OAuth2 Error

20 | 21 |

22 | 23 | ( 24 | 25 | ) 26 |

27 |

Please go back to your client application and try again, or 28 | contact the owner and ask for support

29 | 30 |
31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /conference/src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="authz" 2 | uri="http://www.springframework.org/security/tags"%> 3 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 4 | 5 | 6 | 7 | 8 | 9 | 10 | Conference 11 | 13 | 14 | 16 | 17 | 18 | 27 | 28 | 29 | 30 | 31 |
32 | 33 |

Conference Server

34 | 35 | 36 |
37 |
" role="form"> 38 | 39 |
40 |
41 |
42 | 43 |
44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /conference/src/main/webapp/login.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 2 | 3 | 4 | 5 | 6 | 7 | 8 | Conference 9 | 11 | 12 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Conference Server

21 | 22 | 23 |

Woops!

24 | 25 |

Your login attempt was not successful.

26 |
27 | 28 |

Woops!

29 | 30 |

You are not permitted to access that resource.

31 |
32 | 33 |
34 |
35 |
36 | 37 |
" method="post" role="form"> 38 |
39 | 43 |
44 |
45 | 48 |
49 | 50 | 52 |
53 |
54 |
55 |
56 | 57 |
58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | org.springframework.security.oauth 6 | spring-security-oauth-parent 7 | 2.0.4.RELEASE 8 | 9 | 10 | demo 11 | oauth2 12 | OAuth2 Demo 13 | 1.0.0 14 | pom 15 | 16 | 17 | conference 18 | client 19 | 20 | 21 | http://static.springframework.org/spring-security/oauth/samples 22 | 23 | 24 | 25 | 26 | 27 | maven-deploy-plugin 28 | 2.6 29 | 30 | true 31 | 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-war-plugin 36 | 37 | false 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | --------------------------------------------------------------------------------