From fd2f3f8a87808d0c5d370a47acf0998e91a9d040 Mon Sep 17 00:00:00 2001 From: DOHA Date: Thu, 12 Feb 2015 14:13:21 +0200 Subject: [PATCH] update spring oauth --- .../org/baeldung/web/RedditController.java | 28 -- .../src/main/resources/persistence.properties | 2 +- .../.classpath | 0 .../.project | 2 +- .../.springBeans | 0 .../pom.xml | 6 +- .../config/MyAccessTokenProviderChain.java | 134 ++++++++ ...yAuthorizationCodeAccessTokenProvider.java | 295 ++++++++++++++++++ .../baeldung/config/PersistenceJPAConfig.java | 76 +++++ .../org/baeldung/config/SecurityConfig.java | 53 ++++ .../baeldung/config/ServletInitializer.java | 3 +- .../java/org/baeldung/config/WebConfig.java | 11 +- .../persistence/dao/UserRepository.java | 8 + .../org/baeldung/persistence/model/User.java | 44 +++ .../security/MyUserDetailsService.java | 46 +++ .../org/baeldung/web/RedditController.java | 47 +++ .../src/main/resources/log4j.xml | 0 .../src/main/resources/persistence.properties | 10 + .../src/main/resources/reddit.properties | 0 .../src/main/webapp/WEB-INF/jsp/reddit.jsp | 2 +- .../src/main/webapp/WEB-INF/jsp/test.jsp | 12 + .../src/main/webapp/index.jsp | 6 +- .../src/test/resources/log4j.xml | 0 23 files changed, 745 insertions(+), 40 deletions(-) delete mode 100644 spring-oauth/src/main/java/org/baeldung/web/RedditController.java rename {spring-oauth => spring-security-oauth}/.classpath (100%) rename {spring-oauth => spring-security-oauth}/.project (97%) rename {spring-oauth => spring-security-oauth}/.springBeans (100%) rename {spring-oauth => spring-security-oauth}/pom.xml (99%) create mode 100644 spring-security-oauth/src/main/java/org/baeldung/config/MyAccessTokenProviderChain.java create mode 100644 spring-security-oauth/src/main/java/org/baeldung/config/MyAuthorizationCodeAccessTokenProvider.java create mode 100644 spring-security-oauth/src/main/java/org/baeldung/config/PersistenceJPAConfig.java create mode 100644 spring-security-oauth/src/main/java/org/baeldung/config/SecurityConfig.java rename {spring-oauth => spring-security-oauth}/src/main/java/org/baeldung/config/ServletInitializer.java (89%) rename {spring-oauth => spring-security-oauth}/src/main/java/org/baeldung/config/WebConfig.java (80%) create mode 100644 spring-security-oauth/src/main/java/org/baeldung/persistence/dao/UserRepository.java create mode 100644 spring-security-oauth/src/main/java/org/baeldung/persistence/model/User.java create mode 100644 spring-security-oauth/src/main/java/org/baeldung/security/MyUserDetailsService.java create mode 100644 spring-security-oauth/src/main/java/org/baeldung/web/RedditController.java rename {spring-oauth => spring-security-oauth}/src/main/resources/log4j.xml (100%) create mode 100644 spring-security-oauth/src/main/resources/persistence.properties rename {spring-oauth => spring-security-oauth}/src/main/resources/reddit.properties (100%) rename {spring-oauth => spring-security-oauth}/src/main/webapp/WEB-INF/jsp/reddit.jsp (82%) create mode 100644 spring-security-oauth/src/main/webapp/WEB-INF/jsp/test.jsp rename {spring-oauth => spring-security-oauth}/src/main/webapp/index.jsp (62%) rename {spring-oauth => spring-security-oauth}/src/test/resources/log4j.xml (100%) diff --git a/spring-oauth/src/main/java/org/baeldung/web/RedditController.java b/spring-oauth/src/main/java/org/baeldung/web/RedditController.java deleted file mode 100644 index 31a1e9a920..0000000000 --- a/spring-oauth/src/main/java/org/baeldung/web/RedditController.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.baeldung.web; - -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.client.RestOperations; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -@Controller -public class RedditController { - - private RestOperations redditRestTemplate; - - @RequestMapping("/reddit/info") - public String getInfo(Model model) throws Exception { - String result = redditRestTemplate.getForObject("https://oauth.reddit.com/api/v1/me", String.class); - JsonNode node = new ObjectMapper().readTree(result); - model.addAttribute("info", node.get("name").asText()); - return "reddit"; - } - - public void setRedditRestTemplate(RestOperations redditRestTemplate) { - this.redditRestTemplate = redditRestTemplate; - } - -} \ No newline at end of file diff --git a/spring-security-login-and-registration/src/main/resources/persistence.properties b/spring-security-login-and-registration/src/main/resources/persistence.properties index 06b2528f64..cd282b147b 100644 --- a/spring-security-login-and-registration/src/main/resources/persistence.properties +++ b/spring-security-login-and-registration/src/main/resources/persistence.properties @@ -1,6 +1,6 @@ ################### DataSource Configuration ########################## jdbc.driverClassName=com.mysql.jdbc.Driver -jdbc.url=jdbc:mysql://localhost:3306/registration_02?createDatabaseIfNotExist=true +jdbc.url=jdbc:mysql://localhost:3606/registration_02?createDatabaseIfNotExist=true jdbc.user=tutorialuser jdbc.pass=tutorialmy5ql init-db=false diff --git a/spring-oauth/.classpath b/spring-security-oauth/.classpath similarity index 100% rename from spring-oauth/.classpath rename to spring-security-oauth/.classpath diff --git a/spring-oauth/.project b/spring-security-oauth/.project similarity index 97% rename from spring-oauth/.project rename to spring-security-oauth/.project index 031740156c..b1fe5e0f42 100644 --- a/spring-oauth/.project +++ b/spring-security-oauth/.project @@ -1,6 +1,6 @@ - spring-oauth + spring-security-oauth diff --git a/spring-oauth/.springBeans b/spring-security-oauth/.springBeans similarity index 100% rename from spring-oauth/.springBeans rename to spring-security-oauth/.springBeans diff --git a/spring-oauth/pom.xml b/spring-security-oauth/pom.xml similarity index 99% rename from spring-oauth/pom.xml rename to spring-security-oauth/pom.xml index 457b8f9c3f..382f9ecf47 100644 --- a/spring-oauth/pom.xml +++ b/spring-security-oauth/pom.xml @@ -1,10 +1,10 @@ 4.0.0 org.baeldung - spring-oauth + spring-security-oauth 0.1-SNAPSHOT - spring-oauth + spring-security-oauth war @@ -251,7 +251,7 @@ - spring-oauth + spring-security-oauth src/main/resources diff --git a/spring-security-oauth/src/main/java/org/baeldung/config/MyAccessTokenProviderChain.java b/spring-security-oauth/src/main/java/org/baeldung/config/MyAccessTokenProviderChain.java new file mode 100644 index 0000000000..4d7ecee1e4 --- /dev/null +++ b/spring-security-oauth/src/main/java/org/baeldung/config/MyAccessTokenProviderChain.java @@ -0,0 +1,134 @@ +package org.baeldung.config; + +import java.util.Collections; +import java.util.List; + +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.InsufficientAuthenticationException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException; +import org.springframework.security.oauth2.client.token.AccessTokenProvider; +import org.springframework.security.oauth2.client.token.AccessTokenRequest; +import org.springframework.security.oauth2.client.token.ClientTokenServices; +import org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.OAuth2RefreshToken; +import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; + +public class MyAccessTokenProviderChain extends OAuth2AccessTokenSupport implements AccessTokenProvider { + + private final List chain; + + private ClientTokenServices clientTokenServices; + + public MyAccessTokenProviderChain(List chain) { + this.chain = chain == null ? Collections. emptyList() : Collections.unmodifiableList(chain); + } + + public void setClientTokenServices(ClientTokenServices clientTokenServices) { + this.clientTokenServices = clientTokenServices; + } + + public boolean supportsResource(OAuth2ProtectedResourceDetails resource) { + for (AccessTokenProvider tokenProvider : chain) { + if (tokenProvider.supportsResource(resource)) { + return true; + } + } + return false; + } + + public boolean supportsRefresh(OAuth2ProtectedResourceDetails resource) { + for (AccessTokenProvider tokenProvider : chain) { + if (tokenProvider.supportsRefresh(resource)) { + return true; + } + } + return false; + } + + public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails resource, AccessTokenRequest request) throws UserRedirectRequiredException, AccessDeniedException { + System.out.println("Obtain new token====="); + OAuth2AccessToken accessToken = null; + OAuth2AccessToken existingToken = null; + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + System.out.println("The authentication is ---- " + auth); + if (auth instanceof AnonymousAuthenticationToken) { + if (!resource.isClientOnly()) { + throw new InsufficientAuthenticationException("Authentication is required to obtain an access token (anonymous not allowed)"); + } + } + + if (resource.isClientOnly() || (auth != null && auth.isAuthenticated())) { + existingToken = request.getExistingToken(); + System.out.println("checking existing token ====="); + if (existingToken == null && clientTokenServices != null) { + System.out.println("get existing token from clientTokenServices ==== "); + existingToken = clientTokenServices.getAccessToken(resource, auth); + } + + if (existingToken != null) { + if (existingToken.isExpired()) { + System.out.println("expired token"); + if (clientTokenServices != null) { + clientTokenServices.removeAccessToken(resource, auth); + } + OAuth2RefreshToken refreshToken = existingToken.getRefreshToken(); + if (refreshToken != null) { + System.out.println("let's refresh it"); + accessToken = refreshAccessToken(resource, refreshToken, request); + } + } else { + System.out.println("use existing because not expired yet"); + accessToken = existingToken; + } + } + } + + if (accessToken == null) { + System.out.println("no token so let get it"); + accessToken = obtainNewAccessTokenInternal(resource, request); + + if (accessToken == null) { + throw new IllegalStateException("An OAuth 2 access token must be obtained or an exception thrown."); + } + } + + if (clientTokenServices != null && auth != null && auth.isAuthenticated()) { + clientTokenServices.saveAccessToken(resource, auth, accessToken); + } + + return accessToken; + } + + protected OAuth2AccessToken obtainNewAccessTokenInternal(OAuth2ProtectedResourceDetails details, AccessTokenRequest request) throws UserRedirectRequiredException, AccessDeniedException { + + if (request.isError()) { + throw OAuth2Exception.valueOf(request.toSingleValueMap()); + } + + for (AccessTokenProvider tokenProvider : chain) { + if (tokenProvider.supportsResource(details)) { + System.out.println("we will use this provider to get it => " + tokenProvider.getClass().getName()); + return tokenProvider.obtainAccessToken(details, request); + } + } + + throw new OAuth2AccessDeniedException("Unable to obtain a new access token for resource '" + details.getId() + "'. The provider manager is not configured to support it.", details); + } + + public OAuth2AccessToken refreshAccessToken(OAuth2ProtectedResourceDetails resource, OAuth2RefreshToken refreshToken, AccessTokenRequest request) throws UserRedirectRequiredException { + for (AccessTokenProvider tokenProvider : chain) { + if (tokenProvider.supportsRefresh(resource)) { + System.out.println("we will use this provider to refresh it => " + tokenProvider.getClass().getName()); + return tokenProvider.refreshAccessToken(resource, refreshToken, request); + } + } + throw new OAuth2AccessDeniedException("Unable to obtain a new access token for resource '" + resource.getId() + "'. The provider manager is not configured to support it.", resource); + } + +} \ No newline at end of file diff --git a/spring-security-oauth/src/main/java/org/baeldung/config/MyAuthorizationCodeAccessTokenProvider.java b/spring-security-oauth/src/main/java/org/baeldung/config/MyAuthorizationCodeAccessTokenProvider.java new file mode 100644 index 0000000000..bf454a9d7e --- /dev/null +++ b/spring-security-oauth/src/main/java/org/baeldung/config/MyAuthorizationCodeAccessTokenProvider.java @@ -0,0 +1,295 @@ +package org.baeldung.config; + +import java.io.IOException; +import java.net.URI; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.oauth2.client.filter.state.DefaultStateKeyGenerator; +import org.springframework.security.oauth2.client.filter.state.StateKeyGenerator; +import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.resource.UserApprovalRequiredException; +import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException; +import org.springframework.security.oauth2.client.token.AccessTokenProvider; +import org.springframework.security.oauth2.client.token.AccessTokenRequest; +import org.springframework.security.oauth2.client.token.DefaultRequestEnhancer; +import org.springframework.security.oauth2.client.token.RequestEnhancer; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.OAuth2RefreshToken; +import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; +import org.springframework.security.oauth2.common.util.OAuth2Utils; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.ResponseExtractor; + +public class MyAuthorizationCodeAccessTokenProvider extends AuthorizationCodeAccessTokenProvider implements AccessTokenProvider { + + private StateKeyGenerator stateKeyGenerator = new DefaultStateKeyGenerator(); + + private String scopePrefix = OAuth2Utils.SCOPE_PREFIX; + + private RequestEnhancer authorizationRequestEnhancer = new DefaultRequestEnhancer(); + + @Override + public void setAuthorizationRequestEnhancer(RequestEnhancer authorizationRequestEnhancer) { + this.authorizationRequestEnhancer = authorizationRequestEnhancer; + } + + @Override + public void setScopePrefix(String scopePrefix) { + this.scopePrefix = scopePrefix; + } + + @Override + public void setStateKeyGenerator(StateKeyGenerator stateKeyGenerator) { + this.stateKeyGenerator = stateKeyGenerator; + } + + @Override + public boolean supportsResource(OAuth2ProtectedResourceDetails resource) { + return resource instanceof AuthorizationCodeResourceDetails && "authorization_code".equals(resource.getGrantType()); + } + + @Override + public boolean supportsRefresh(OAuth2ProtectedResourceDetails resource) { + return supportsResource(resource); + } + + @Override + public String obtainAuthorizationCode(OAuth2ProtectedResourceDetails details, AccessTokenRequest request) throws UserRedirectRequiredException, UserApprovalRequiredException, AccessDeniedException, OAuth2AccessDeniedException { + + AuthorizationCodeResourceDetails resource = (AuthorizationCodeResourceDetails) details; + + HttpHeaders headers = getHeadersForAuthorizationRequest(request); + MultiValueMap form = new LinkedMultiValueMap(); + if (request.containsKey(OAuth2Utils.USER_OAUTH_APPROVAL)) { + form.set(OAuth2Utils.USER_OAUTH_APPROVAL, request.getFirst(OAuth2Utils.USER_OAUTH_APPROVAL)); + for (String scope : details.getScope()) { + form.set(scopePrefix + scope, request.getFirst(OAuth2Utils.USER_OAUTH_APPROVAL)); + } + } else { + form.putAll(getParametersForAuthorizeRequest(resource, request)); + } + form.set("duration", "permanent"); + authorizationRequestEnhancer.enhance(request, resource, form, headers); + final AccessTokenRequest copy = request; + + final ResponseExtractor> delegate = getAuthorizationResponseExtractor(); + ResponseExtractor> extractor = new ResponseExtractor>() { + @Override + public ResponseEntity extractData(ClientHttpResponse response) throws IOException { + if (response.getHeaders().containsKey("Set-Cookie")) { + copy.setCookie(response.getHeaders().getFirst("Set-Cookie")); + } + return delegate.extractData(response); + } + }; + + ResponseEntity response = getRestTemplate().execute(resource.getUserAuthorizationUri(), HttpMethod.POST, getRequestCallback(resource, form, headers), extractor, form.toSingleValueMap()); + + if (response.getStatusCode() == HttpStatus.OK) { + throw getUserApprovalSignal(resource, request); + } + + URI location = response.getHeaders().getLocation(); + String query = location.getQuery(); + Map map = OAuth2Utils.extractMap(query); + if (map.containsKey("state")) { + request.setStateKey(map.get("state")); + if (request.getPreservedState() == null) { + String redirectUri = resource.getRedirectUri(request); + if (redirectUri != null) { + request.setPreservedState(redirectUri); + } else { + request.setPreservedState(new Object()); + } + } + } + + String code = map.get("code"); + if (code == null) { + throw new UserRedirectRequiredException(location.toString(), form.toSingleValueMap()); + } + request.set("code", code); + return code; + + } + + @Override + protected ResponseExtractor> getAuthorizationResponseExtractor() { + return new ResponseExtractor>() { + public ResponseEntity extractData(ClientHttpResponse response) throws IOException { + return new ResponseEntity(response.getHeaders(), response.getStatusCode()); + } + }; + } + + @Override + public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request) throws UserRedirectRequiredException, UserApprovalRequiredException, AccessDeniedException, OAuth2AccessDeniedException { + + AuthorizationCodeResourceDetails resource = (AuthorizationCodeResourceDetails) details; + + if (request.getAuthorizationCode() == null) { + if (request.getStateKey() == null) { + throw getRedirectForAuthorization(resource, request); + } + obtainAuthorizationCode(resource, request); + } + return retrieveToken(request, resource, getParametersForTokenRequest(resource, request), getHeadersForTokenRequest(request)); + + } + + @Override + public OAuth2AccessToken refreshAccessToken(OAuth2ProtectedResourceDetails resource, OAuth2RefreshToken refreshToken, AccessTokenRequest request) throws UserRedirectRequiredException, OAuth2AccessDeniedException { + MultiValueMap form = new LinkedMultiValueMap(); + form.add("grant_type", "refresh_token"); + form.add("refresh_token", refreshToken.getValue()); + try { + return retrieveToken(request, resource, form, getHeadersForTokenRequest(request)); + } catch (OAuth2AccessDeniedException e) { + throw getRedirectForAuthorization((AuthorizationCodeResourceDetails) resource, request); + } + } + + private HttpHeaders getHeadersForTokenRequest(AccessTokenRequest request) { + HttpHeaders headers = new HttpHeaders(); + // No cookie for token request + return headers; + } + + private HttpHeaders getHeadersForAuthorizationRequest(AccessTokenRequest request) { + HttpHeaders headers = new HttpHeaders(); + headers.putAll(request.getHeaders()); + if (request.getCookie() != null) { + headers.set("Cookie", request.getCookie()); + } + return headers; + } + + private MultiValueMap getParametersForTokenRequest(AuthorizationCodeResourceDetails resource, AccessTokenRequest request) { + + MultiValueMap form = new LinkedMultiValueMap(); + form.set("grant_type", "authorization_code"); + form.set("code", request.getAuthorizationCode()); + + Object preservedState = request.getPreservedState(); + if (request.getStateKey() != null) { + if (preservedState == null) { + throw new InvalidRequestException("Possible CSRF detected - state parameter was present but no state could be found"); + } + } + + String redirectUri = null; + if (preservedState instanceof String) { + redirectUri = String.valueOf(preservedState); + } else { + redirectUri = resource.getRedirectUri(request); + } + + if (redirectUri != null && !"NONE".equals(redirectUri)) { + form.set("redirect_uri", redirectUri); + } + + return form; + + } + + private MultiValueMap getParametersForAuthorizeRequest(AuthorizationCodeResourceDetails resource, AccessTokenRequest request) { + + MultiValueMap form = new LinkedMultiValueMap(); + form.set("response_type", "code"); + form.set("client_id", resource.getClientId()); + + if (request.get("scope") != null) { + form.set("scope", request.getFirst("scope")); + } else { + form.set("scope", OAuth2Utils.formatParameterList(resource.getScope())); + } + + String redirectUri = resource.getPreEstablishedRedirectUri(); + + Object preservedState = request.getPreservedState(); + if (redirectUri == null && preservedState != null) { + redirectUri = String.valueOf(preservedState); + } else { + redirectUri = request.getCurrentUri(); + } + + String stateKey = request.getStateKey(); + if (stateKey != null) { + form.set("state", stateKey); + if (preservedState == null) { + throw new InvalidRequestException("Possible CSRF detected - state parameter was present but no state could be found"); + } + } + + if (redirectUri != null) { + form.set("redirect_uri", redirectUri); + } + + return form; + + } + + private UserRedirectRequiredException getRedirectForAuthorization(AuthorizationCodeResourceDetails resource, AccessTokenRequest request) { + + // we don't have an authorization code yet. So first get that. + TreeMap requestParameters = new TreeMap(); + requestParameters.put("response_type", "code"); // oauth2 spec, section 3 + requestParameters.put("client_id", resource.getClientId()); + requestParameters.put("duration", "permanent"); + // Client secret is not required in the initial authorization request + + String redirectUri = resource.getRedirectUri(request); + if (redirectUri != null) { + requestParameters.put("redirect_uri", redirectUri); + } + + if (resource.isScoped()) { + + StringBuilder builder = new StringBuilder(); + List scope = resource.getScope(); + + if (scope != null) { + Iterator scopeIt = scope.iterator(); + while (scopeIt.hasNext()) { + builder.append(scopeIt.next()); + if (scopeIt.hasNext()) { + builder.append(' '); + } + } + } + + requestParameters.put("scope", builder.toString()); + } + + UserRedirectRequiredException redirectException = new UserRedirectRequiredException(resource.getUserAuthorizationUri(), requestParameters); + + String stateKey = stateKeyGenerator.generateKey(resource); + redirectException.setStateKey(stateKey); + request.setStateKey(stateKey); + redirectException.setStateToPreserve(redirectUri); + request.setPreservedState(redirectUri); + + return redirectException; + + } + + protected UserApprovalRequiredException getUserApprovalSignal(AuthorizationCodeResourceDetails resource, AccessTokenRequest request) { + String message = String.format("Do you approve the client '%s' to access your resources with scope=%s", resource.getClientId(), resource.getScope()); + return new UserApprovalRequiredException(resource.getUserAuthorizationUri(), Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, message), resource.getClientId(), resource.getScope()); + } + +} diff --git a/spring-security-oauth/src/main/java/org/baeldung/config/PersistenceJPAConfig.java b/spring-security-oauth/src/main/java/org/baeldung/config/PersistenceJPAConfig.java new file mode 100644 index 0000000000..aa952db6f3 --- /dev/null +++ b/spring-security-oauth/src/main/java/org/baeldung/config/PersistenceJPAConfig.java @@ -0,0 +1,76 @@ +package org.baeldung.config; + +import java.util.Properties; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@Configuration +@EnableTransactionManagement +@PropertySource({ "classpath:persistence.properties" }) +@ComponentScan({ "org.baeldung.persistence" }) +@EnableJpaRepositories(basePackages = "org.baeldung.persistence.dao") +public class PersistenceJPAConfig { + + @Autowired + private Environment env; + + public PersistenceJPAConfig() { + super(); + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan(new String[] { "org.baeldung.persistence.model" }); + final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); + em.setJpaVendorAdapter(vendorAdapter); + em.setJpaProperties(additionalProperties()); + return em; + } + + @Bean + public DataSource dataSource() { + final DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); + dataSource.setUrl(env.getProperty("jdbc.url")); + dataSource.setUsername(env.getProperty("jdbc.user")); + dataSource.setPassword(env.getProperty("jdbc.pass")); + return dataSource; + } + + @Bean + public JpaTransactionManager transactionManager() { + final JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); + return transactionManager; + } + + @Bean + public PersistenceExceptionTranslationPostProcessor exceptionTranslation() { + return new PersistenceExceptionTranslationPostProcessor(); + } + + final Properties additionalProperties() { + final Properties hibernateProperties = new Properties(); + hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); + hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); + return hibernateProperties; + } + +} + diff --git a/spring-security-oauth/src/main/java/org/baeldung/config/SecurityConfig.java b/spring-security-oauth/src/main/java/org/baeldung/config/SecurityConfig.java new file mode 100644 index 0000000000..9024734fd8 --- /dev/null +++ b/spring-security-oauth/src/main/java/org/baeldung/config/SecurityConfig.java @@ -0,0 +1,53 @@ +package org.baeldung.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; + +@Configuration +@EnableWebSecurity +@ComponentScan(basePackages = { "org.baeldung.security" }) +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private UserDetailsService userDetailsService; + + @Bean + public DaoAuthenticationProvider authProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService); + return authProvider; + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationProvider(authProvider()); + } + + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring().antMatchers("/resources/**"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http.anonymous().disable().authorizeRequests() + .antMatchers("/**").permitAll() + .antMatchers("/reddit","/reddit/**").hasRole("User"); +// .and() +// .formLogin() +// .loginPage("/login") +// .permitAll(); + // @formatter:on + } + +} \ No newline at end of file diff --git a/spring-oauth/src/main/java/org/baeldung/config/ServletInitializer.java b/spring-security-oauth/src/main/java/org/baeldung/config/ServletInitializer.java similarity index 89% rename from spring-oauth/src/main/java/org/baeldung/config/ServletInitializer.java rename to spring-security-oauth/src/main/java/org/baeldung/config/ServletInitializer.java index a5b8708ffa..c2a7f3d4ce 100644 --- a/spring-oauth/src/main/java/org/baeldung/config/ServletInitializer.java +++ b/spring-security-oauth/src/main/java/org/baeldung/config/ServletInitializer.java @@ -13,7 +13,7 @@ public class ServletInitializer extends AbstractDispatcherServletInitializer { @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); - context.register(WebConfig.class); + context.register(PersistenceJPAConfig.class, SecurityConfig.class, WebConfig.class); return context; } @@ -31,6 +31,7 @@ public class ServletInitializer extends AbstractDispatcherServletInitializer { public void onStartup(ServletContext servletContext) throws ServletException { super.onStartup(servletContext); registerProxyFilter(servletContext, "oauth2ClientContextFilter"); + // registerProxyFilter(servletContext, "springSecurityFilterChain"); } private void registerProxyFilter(ServletContext servletContext, String name) { diff --git a/spring-oauth/src/main/java/org/baeldung/config/WebConfig.java b/spring-security-oauth/src/main/java/org/baeldung/config/WebConfig.java similarity index 80% rename from spring-oauth/src/main/java/org/baeldung/config/WebConfig.java rename to spring-security-oauth/src/main/java/org/baeldung/config/WebConfig.java index 2bd2fcad43..755ec494a9 100644 --- a/spring-oauth/src/main/java/org/baeldung/config/WebConfig.java +++ b/spring-security-oauth/src/main/java/org/baeldung/config/WebConfig.java @@ -12,9 +12,12 @@ import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.security.oauth2.client.OAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.token.AccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; +import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; -import org.springframework.web.client.RestOperations; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @@ -45,7 +48,7 @@ public class WebConfig extends WebMvcConfigurerAdapter { } @Bean - public RedditController redditController(@Qualifier("redditRestTemplate") RestOperations redditRestTemplate) { + public RedditController redditController(@Qualifier("redditRestTemplate") OAuth2RestTemplate redditRestTemplate) { RedditController controller = new RedditController(); controller.setRedditRestTemplate(redditRestTemplate); return controller; @@ -82,12 +85,16 @@ public class WebConfig extends WebMvcConfigurerAdapter { details.setUserAuthorizationUri(userAuthorizationUri); details.setTokenName("oauth_token"); details.setScope(Arrays.asList("identity")); + details.setGrantType("authorization_code"); return details; } @Bean public OAuth2RestTemplate redditRestTemplate(OAuth2ClientContext clientContext) { OAuth2RestTemplate template = new OAuth2RestTemplate(reddit(), clientContext); + AccessTokenProvider accessTokenProvider = new MyAccessTokenProviderChain(Arrays. asList(new MyAuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), new ResourceOwnerPasswordAccessTokenProvider(), + new ClientCredentialsAccessTokenProvider())); + template.setAccessTokenProvider(accessTokenProvider); return template; } } diff --git a/spring-security-oauth/src/main/java/org/baeldung/persistence/dao/UserRepository.java b/spring-security-oauth/src/main/java/org/baeldung/persistence/dao/UserRepository.java new file mode 100644 index 0000000000..3ba055b027 --- /dev/null +++ b/spring-security-oauth/src/main/java/org/baeldung/persistence/dao/UserRepository.java @@ -0,0 +1,8 @@ +package org.baeldung.persistence.dao; + +import org.baeldung.persistence.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { + public User findByUsername(String name); +} diff --git a/spring-security-oauth/src/main/java/org/baeldung/persistence/model/User.java b/spring-security-oauth/src/main/java/org/baeldung/persistence/model/User.java new file mode 100644 index 0000000000..b712b829ac --- /dev/null +++ b/spring-security-oauth/src/main/java/org/baeldung/persistence/model/User.java @@ -0,0 +1,44 @@ +package org.baeldung.persistence.model; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class User { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String username; + + private String password; + + public User() { + super(); + } + + public User(String username, String password) { + super(); + this.username = username; + this.password = password; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + +} \ No newline at end of file diff --git a/spring-security-oauth/src/main/java/org/baeldung/security/MyUserDetailsService.java b/spring-security-oauth/src/main/java/org/baeldung/security/MyUserDetailsService.java new file mode 100644 index 0000000000..9dea7c117d --- /dev/null +++ b/spring-security-oauth/src/main/java/org/baeldung/security/MyUserDetailsService.java @@ -0,0 +1,46 @@ +package org.baeldung.security; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import org.baeldung.persistence.dao.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service("userDetailsService") +@Transactional +public class MyUserDetailsService implements UserDetailsService { + + @Autowired + private UserRepository userRepository; + + public MyUserDetailsService() { + super(); + } + + @Override + public UserDetails loadUserByUsername(final String name) { + org.baeldung.persistence.model.User user = userRepository.findByUsername(name); + if (user == null) { + user = new org.baeldung.persistence.model.User(name, UUID.randomUUID().toString()); + user = userRepository.save(user); + } + return new User(user.getUsername(), user.getPassword(), getGrantedAuthorities(Arrays.asList("ROLE_USER"))); + } + + private static List getGrantedAuthorities(List roles) { + List authorities = new ArrayList(); + for (String role : roles) { + authorities.add(new SimpleGrantedAuthority(role)); + } + return authorities; + } +} diff --git a/spring-security-oauth/src/main/java/org/baeldung/web/RedditController.java b/spring-security-oauth/src/main/java/org/baeldung/web/RedditController.java new file mode 100644 index 0000000000..61a81ce177 --- /dev/null +++ b/spring-security-oauth/src/main/java/org/baeldung/web/RedditController.java @@ -0,0 +1,47 @@ +package org.baeldung.web; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Controller +public class RedditController { + + @Autowired + private UserDetailsService userDetailsService; + + private OAuth2RestTemplate redditRestTemplate; + + @RequestMapping("/info") + public String getInfo(Model model) throws Exception { + String result = redditRestTemplate.getForObject("https://oauth.reddit.com/api/v1/me", String.class); + JsonNode node = new ObjectMapper().readTree(result); + String name = node.get("name").asText(); + model.addAttribute("info", name); + + UserDetails userDetails = userDetailsService.loadUserByUsername(name); + Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + return "reddit"; + } + + @RequestMapping("/reddit/test") + public String test(Model model) { + return "test"; + } + + public void setRedditRestTemplate(OAuth2RestTemplate redditRestTemplate) { + this.redditRestTemplate = redditRestTemplate; + } + +} \ No newline at end of file diff --git a/spring-oauth/src/main/resources/log4j.xml b/spring-security-oauth/src/main/resources/log4j.xml similarity index 100% rename from spring-oauth/src/main/resources/log4j.xml rename to spring-security-oauth/src/main/resources/log4j.xml diff --git a/spring-security-oauth/src/main/resources/persistence.properties b/spring-security-oauth/src/main/resources/persistence.properties new file mode 100644 index 0000000000..cc4ac2df61 --- /dev/null +++ b/spring-security-oauth/src/main/resources/persistence.properties @@ -0,0 +1,10 @@ +################### DataSource Configuration ########################## +jdbc.driverClassName=com.mysql.jdbc.Driver +jdbc.url=jdbc:mysql://localhost:3606/oauth?createDatabaseIfNotExist=true +jdbc.user=tutorialuser +jdbc.pass=tutorialmy5ql +init-db=false +################### Hibernate Configuration ########################## +hibernate.dialect=org.hibernate.dialect.MySQLDialect +hibernate.show_sql=false +hibernate.hbm2ddl.auto=create-drop diff --git a/spring-oauth/src/main/resources/reddit.properties b/spring-security-oauth/src/main/resources/reddit.properties similarity index 100% rename from spring-oauth/src/main/resources/reddit.properties rename to spring-security-oauth/src/main/resources/reddit.properties diff --git a/spring-oauth/src/main/webapp/WEB-INF/jsp/reddit.jsp b/spring-security-oauth/src/main/webapp/WEB-INF/jsp/reddit.jsp similarity index 82% rename from spring-oauth/src/main/webapp/WEB-INF/jsp/reddit.jsp rename to spring-security-oauth/src/main/webapp/WEB-INF/jsp/reddit.jsp index 5e17c3904c..ac16612972 100755 --- a/spring-oauth/src/main/webapp/WEB-INF/jsp/reddit.jsp +++ b/spring-security-oauth/src/main/webapp/WEB-INF/jsp/reddit.jsp @@ -3,7 +3,7 @@ -Spring OAuth +Spring Security OAuth

Your Reddit Info

diff --git a/spring-security-oauth/src/main/webapp/WEB-INF/jsp/test.jsp b/spring-security-oauth/src/main/webapp/WEB-INF/jsp/test.jsp new file mode 100644 index 0000000000..4e39547e4b --- /dev/null +++ b/spring-security-oauth/src/main/webapp/WEB-INF/jsp/test.jsp @@ -0,0 +1,12 @@ + +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> + + + +Spring Security OAuth + + +

Test

+Test + + \ No newline at end of file diff --git a/spring-oauth/src/main/webapp/index.jsp b/spring-security-oauth/src/main/webapp/index.jsp similarity index 62% rename from spring-oauth/src/main/webapp/index.jsp rename to spring-security-oauth/src/main/webapp/index.jsp index 06baaf8e3c..244927908f 100755 --- a/spring-oauth/src/main/webapp/index.jsp +++ b/spring-security-oauth/src/main/webapp/index.jsp @@ -4,12 +4,12 @@ -Spring OAuth +Spring Security OAuth -

Welcome to Spring OAuth

-Login with Reddit +

Welcome to Spring Security OAuth

+Login with Reddit \ No newline at end of file diff --git a/spring-oauth/src/test/resources/log4j.xml b/spring-security-oauth/src/test/resources/log4j.xml similarity index 100% rename from spring-oauth/src/test/resources/log4j.xml rename to spring-security-oauth/src/test/resources/log4j.xml