From b93cdaa537ad09f36ab9f9334714191fcafbf67c Mon Sep 17 00:00:00 2001 From: Franck Andriano Date: Thu, 19 Jan 2017 16:34:38 +0100 Subject: [PATCH 1/3] Back DefaultTokenServices and explore CompositeTokenGranter... --- auth-service/pom.xml | 7 +- .../com/jservlet/AuthServiceApplication.java | 320 +++++++++++++----- .../src/main/resources/bootstrap.properties | 29 +- auth-service/src/main/resources/schema.sql | 62 +++- 4 files changed, 301 insertions(+), 117 deletions(-) diff --git a/auth-service/pom.xml b/auth-service/pom.xml index 08c8295..31d368c 100644 --- a/auth-service/pom.xml +++ b/auth-service/pom.xml @@ -73,7 +73,7 @@ com.h2database h2 - runtime + org.springframework.boot @@ -91,6 +91,11 @@ springloaded 1.2.6.RELEASE + + org.springframework.integration + spring-integration-core + 4.3.5.RELEASE + diff --git a/auth-service/src/main/java/com/jservlet/AuthServiceApplication.java b/auth-service/src/main/java/com/jservlet/AuthServiceApplication.java index b76f26d..df01dc2 100644 --- a/auth-service/src/main/java/com/jservlet/AuthServiceApplication.java +++ b/auth-service/src/main/java/com/jservlet/AuthServiceApplication.java @@ -2,19 +2,22 @@ import org.apache.log4j.Logger; import org.hibernate.validator.constraints.NotEmpty; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.*; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.*; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -26,18 +29,36 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; -import org.springframework.security.oauth2.provider.ClientDetailsService; -import org.springframework.security.oauth2.provider.ClientRegistrationException; +import org.springframework.security.oauth2.provider.*; +import org.springframework.security.oauth2.provider.approval.ApprovalStore; +import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore; import org.springframework.security.oauth2.provider.client.BaseClientDetails; +import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter; +import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices; +import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter; +import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices; +import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator; +import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator; +import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter; +import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter; +import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter; +import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.security.oauth2.provider.token.DefaultTokenServices; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.stereotype.Component; +import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import javax.persistence.*; @@ -47,6 +68,7 @@ import javax.servlet.http.HttpServletResponse; import javax.sql.DataSource; import java.io.IOException; +import java.io.Serializable; import java.security.Principal; import java.util.*; import java.util.stream.Collectors; @@ -55,14 +77,9 @@ /** * A Minimal Security OAuth2 Server * - * Active OAuth2 with Openid JWT (Jason Web Token) SHA256 with RSA private/public keys in config! - * - * curl http://localhost:9191/uaa/oauth/token_key - * {"alg":"SHA256withRSA", - * "value":"-----BEGIN PUBLIC KEY----- - * MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNQZKqTlO/+2b4ZdhqGJzGBDltb5PZmBz1ALN2YLvt341pH6i5mO1V9cX5Ty1LM70fKfnIoYUP4KCE - * 33dPnC7LkUwE/myh1zM6m8cbL5cYFPyP099thbVxzJkjHWqywvQih/qOOjliomKbM9pxG8Z1dB26hL9dSAZuA8xExjlPmQIDAQAB - * -----END PUBLIC KEY-----"} + * curl -v -s --user acme:$2a$10$z/8fQRJlWmEB2jU3kC2rueX0gtVi340X2/bri6U5Yxw4tdHG/vZJS http://localhost:9191/uaa/oauth/token -d grant_type=password -d client_id=acme -d scope=read -d username=franck -d password=spring + * curl -v -s --user acme:$2a$10$z/8fQRJlWmEB2jU3kC2rueX0gtVi340X2/bri6U5Yxw4tdHG/vZJS http://localhost:9191/uaa/oauth/token -d grant_type=client_credentials -d scope=read + * curl -v -s --user acme:$2a$10$z/8fQRJlWmEB2jU3kC2rueX0gtVi340X2/bri6U5Yxw4tdHG/vZJS http://localhost:9191/uaa/oauth/token -d grant_type=authorization_code -d client_id=acme -d redirect_uri=http://example.com -d code=LCd7zR * * ************* * Code grant @@ -110,6 +127,8 @@ *

* Use this TOKEN as a parameter of http request... access_token=9bf70492-6dad-40f4-9d6a-0237e5c1dec4 * + * See h2-console on Eureka Service http://localhost:8761/h2-console/ + * * @author Franck Andriano 2016 */ @EnableDiscoveryClient @@ -131,8 +150,8 @@ interface AuthoritiesRepository extends JpaRepository { List findByUsername(String username); } -interface ClientRepository extends JpaRepository { - Optional findByClientId(String clientId); +interface ClientRepository extends JpaRepository { + Optional findByClientId(String clientId); } @Component @@ -145,7 +164,8 @@ class OAuth2InitConfig implements CommandLineRunner { private final ClientRepository clientRepository; @Autowired - public OAuth2InitConfig(UsersRepository usersRepository, AuthoritiesRepository authoritiesRepository, + public OAuth2InitConfig(UsersRepository usersRepository, + AuthoritiesRepository authoritiesRepository, ClientRepository clientRepository) { this.usersRepository = usersRepository; this.authoritiesRepository = authoritiesRepository; @@ -180,7 +200,7 @@ public void run(String... strings) throws Exception { // Clients if (clientRepository.findAll().isEmpty()) { Stream.of("acme,acmesecret", "mobile,secret", "html5,secret").map(x -> x.split(",")) - .forEach(tpl -> clientRepository.save(new Client(tpl[0], new BCryptPasswordEncoder(10).encode(tpl[1])))); + .forEach(tpl -> clientRepository.save(new Clients(tpl[0], new BCryptPasswordEncoder(10).encode(tpl[1])))); logger.warn("ClientRepository creation:"); } else logger.warn("ClientRepository injection:"); @@ -198,7 +218,8 @@ class OAuth2ServerConfig { private final UsersRepository usersRepository; @Autowired - public OAuth2ServerConfig(UsersRepository usersRepository, AuthoritiesRepository authoritiesRepository, + public OAuth2ServerConfig(UsersRepository usersRepository, + AuthoritiesRepository authoritiesRepository, ClientRepository clientRepository) { this.usersRepository = usersRepository; this.authoritiesRepository = authoritiesRepository; @@ -211,16 +232,16 @@ ClientDetailsService clientDetailsService() { .map(client -> { BaseClientDetails details = new BaseClientDetails( client.getClientId(), - null, - client.getScopes(), + client.getResourceIds(), + client.getScope(), client.getAuthorizedGrantTypes(), client.getAuthorities(), - client.getRegisteredRedirectUri() + client.getWebServerRedirectUri() ); - details.setClientSecret(client.getSecret()); - details.setAutoApproveScopes(Arrays.asList(client.getAutoApproveScopes().split(","))); - details.setAccessTokenValiditySeconds(1800); // 30mn Token < 1h RefreshToken! - details.setRefreshTokenValiditySeconds(3600); + details.setClientSecret(client.getClientSecret()); + details.setAutoApproveScopes(Arrays.asList(client.getAutoapprove().split(","))); + details.setAccessTokenValiditySeconds(client.getAccessTokenValidity()); // 30mn Token < 1h RefreshToken! + details.setRefreshTokenValiditySeconds(client.getRefreshTokenValidity()); return details; }) .orElseThrow(() -> new ClientRegistrationException(String.format("no client %s registered", clientId))); @@ -308,7 +329,7 @@ public void update(@RequestParam(value = "username") String username, Optional optional = usersRepository.findByUsername(username); Users user = optional.get(); user.setPassword(new BCryptPasswordEncoder(10).encode(password)); - usersRepository.saveAndFlush(user); + usersRepository.save(user); logger.warn("UsersRepository update user: " + optional.get().getUsername()); } } else response.sendError(HttpServletResponse.SC_UNAUTHORIZED); @@ -330,8 +351,7 @@ public void delete(@RequestParam(value = "username") String username, @GetMapping("/raw") public Object[] raw(HttpServletRequest request) { // Return a raw list! - if (request.isUserInRole("ROLE_SUPERVISOR")) return usersRepository.findAll().toArray(); - else return null; + return request.isUserInRole("ROLE_SUPERVISOR") ? usersRepository.findAll().toArray() : null; } } @@ -353,9 +373,11 @@ public WebSecurityConfig(UserDetailsService userDetailsService) { protected void configure(HttpSecurity http) throws Exception { // @formatter:off http - .authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/oauth/token", "/oauth/token_key").permitAll() + .requestMatchers().antMatchers(HttpMethod.OPTIONS, "/oauth/token") .and() - .csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + .csrf().disable().authorizeRequests().anyRequest().permitAll() + .and() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // @formatter:on } @@ -375,7 +397,7 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; response.setHeader("Access-Control-Allow-Origin", "*"); - response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); + response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE"); response.setHeader("Access-Control-Max-Age", "1800"); response.setHeader("Access-Control-Allow-Headers", "origin,accept,x-requested-with,content-type,access-control-request-method,access-control-request-headers,authorization"); if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { @@ -398,33 +420,23 @@ class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { private Logger logger = Logger.getLogger(getClass()); - @Value("${config.oauth2.private-key}") - private String privateKey; + @Autowired + DataSource dataSource; + + @Autowired + AuthorizationCodeServices authorizationCodeServices; - @Value("${config.oauth2.public-key}") - private String publicKey; + @Autowired + ApprovalStore approvalStore; private final AuthenticationManager authenticationManager; private final ClientDetailsService clientDetailsService; private final UserDetailsService userDetailsService; - @Bean - public JwtAccessTokenConverter tokenEnhancer() { - logger.warn("Initializing JWT with public key:\n" + publicKey); - JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); - converter.setSigningKey(privateKey); - converter.setVerifierKey(publicKey); - return converter; - } - - @Bean - public JwtTokenStore tokenStore() { // Handle OAuth2 refresh JwtToken! - return new JwtTokenStore(tokenEnhancer()); - } - @Autowired public AuthorizationServerConfig(AuthenticationManager authenticationManager, - ClientDetailsService clientDetailsService, UserDetailsService userDetailsService) { + ClientDetailsService clientDetailsService, + UserDetailsService userDetailsService) { this.authenticationManager = authenticationManager; this.clientDetailsService = clientDetailsService; this.userDetailsService = userDetailsService; @@ -437,21 +449,88 @@ public void configure(ClientDetailsServiceConfigurer clients) throws Exception { @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { + + OAuth2RequestFactory oAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetailsService); + + AuthorizationServerTokenServices tokenServices = tokenServices(); + + // Custom your TokenGranter! + ResourceOwnerPasswordTokenGranter resourceOwnerPasswordTokenGranter = new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetailsService, oAuth2RequestFactory); + RefreshTokenGranter refreshTokenGranter = new RefreshTokenGranter(tokenServices, clientDetailsService, oAuth2RequestFactory); + ImplicitTokenGranter implicitTokenGranter = new ImplicitTokenGranter(tokenServices, clientDetailsService, oAuth2RequestFactory); + ClientCredentialsTokenGranter clientCredentialsTokenGranter = new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, oAuth2RequestFactory); + AuthorizationCodeTokenGranter authorizationCodeTokenGranter = new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices(), clientDetailsService, oAuth2RequestFactory); + + List tokenGranters = new ArrayList<>(); + tokenGranters.add(resourceOwnerPasswordTokenGranter); + tokenGranters.add(refreshTokenGranter); + tokenGranters.add(implicitTokenGranter); + tokenGranters.add(clientCredentialsTokenGranter); + tokenGranters.add(authorizationCodeTokenGranter); + + CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(tokenGranters); + // @formatter:off endpoints .tokenStore(tokenStore()) - .accessTokenConverter(tokenEnhancer()) - .authenticationManager(authenticationManager).userDetailsService(userDetailsService); + .tokenServices(tokenServices) + .approvalStore(approvalStore) + .requestFactory(oAuth2RequestFactory) + .authorizationCodeServices(authorizationCodeServices) + .tokenGranter(compositeTokenGranter) + .authenticationManager(authenticationManager) + .userDetailsService(userDetailsService) + .setClientDetailsService(clientDetailsService); + + endpoints.exceptionTranslator(loggingExceptionTranslator()); // @formatter:on } - @Override - public void configure(AuthorizationServerSecurityConfigurer server) throws Exception { - server.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')") - .checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')"); + @Bean + @Primary + public DefaultTokenServices tokenServices() { + DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); + defaultTokenServices.setTokenStore(tokenStore()); + defaultTokenServices.setSupportRefreshToken(true); + return defaultTokenServices; + } + + @Bean + public TokenStore tokenStore() { + return new JdbcTokenStore(dataSource); + } + + @Bean + protected AuthorizationCodeServices authorizationCodeServices() { + return new JdbcAuthorizationCodeServices(dataSource); + } + + @Bean + protected ApprovalStore approvalStore() { + return new JdbcApprovalStore(dataSource); + } + + @Bean + public WebResponseExceptionTranslator loggingExceptionTranslator() { + return new DefaultWebResponseExceptionTranslator() { + @Override + public ResponseEntity translate(Exception e) throws Exception { + // This is the line that prints the stack trace to the log. + // You can customise this to format the trace etc if you like + e.printStackTrace(); + + // Carry on handling the exception + ResponseEntity responseEntity = super.translate(e); + HttpHeaders headers = new HttpHeaders(); + headers.setAll(responseEntity.getHeaders().toSingleValueMap()); + OAuth2Exception excBody = responseEntity.getBody(); + return new ResponseEntity<>(excBody, headers, responseEntity.getStatusCode()); + } + }; } } + // See bootstrap.properties h2 database server config! // DROP TABLE IF EXISTS USERS; // create table users(ID BIGINT auto_increment PRIMARY KEY, username varchar_ignorecase(50) not null unique, password varchar_ignorecase(255) not null, enabled boolean not null); @@ -546,73 +625,130 @@ public String toString() { } } -// Customized oauth_client_details table -// DROP TABLE IF EXISTS CLIENT; -// CREATE TABLE CLIENT(ID BIGINT auto_increment PRIMARY KEY,CLIENT_ID VARCHAR(255),SECRET VARCHAR(255),SCOPES VARCHAR(255), -// AUTHORIZED_GRANT_TYPES VARCHAR(255),AUTHORITIES VARCHAR(255),AUTO_APPROVE_SCOPES VARCHAR(255), REGISTERED_REDIRECT_URI VARCHAR(1024)); +// See ../resources/schema.sql @Entity -class Client { +@Table(name = "oauth_client_details") +class Clients implements Serializable { @Id - @GeneratedValue - private Long id; - - @NotEmpty - private String clientId, secret; - private String scopes = from("read", "write"); + private String clientId; + private String clientSecret; + private String resourceIds = null; + private String scope = from("read", "write"); private String authorizedGrantTypes = from("client_credentials", "implicit", "authorization_code", "refresh_token", "password"); + private String webServerRedirectUri = from(""); private String authorities = from("ROLE_USER", "ROLE_ADMIN"); - private String autoApproveScopes = from("true"); - private String registeredRedirectUri = from(); + private Integer accessTokenValidity = 1800; // 30mn Token < 1h RefreshToken! + private Integer refreshTokenValidity = 3600; + private String additionalInformation = "SSO RestWeb"; + private String autoapprove = from("true"); - public String getScopes() { - return scopes; + Clients() { // JPA why !? + } + + public Clients(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + private static String from(String... arr) { + return Arrays.stream(arr).collect(Collectors.joining(",")); + } + + public String getClientId() { + return this.clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { + return this.clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public String getResourceIds() { + return this.resourceIds; + } + + public void setResourceIds(String resourceIds) { + this.resourceIds = resourceIds; + } + + public String getScope() { + return this.scope; + } + + public void setScope(String scope) { + this.scope = scope; } public String getAuthorizedGrantTypes() { - return authorizedGrantTypes; + return this.authorizedGrantTypes; + } + + public void setAuthorizedGrantTypes(String authorizedGrantTypes) { + this.authorizedGrantTypes = authorizedGrantTypes; + } + + public String getWebServerRedirectUri() { + return this.webServerRedirectUri; + } + + public void setWebServerRedirectUri(String webServerRedirectUri) { + this.webServerRedirectUri = webServerRedirectUri; } public String getAuthorities() { - return authorities; + return this.authorities; } - public String getAutoApproveScopes() { - return autoApproveScopes; + public void setAuthorities(String authorities) { + this.authorities = authorities; } - public String getRegisteredRedirectUri() { - return registeredRedirectUri; + public Integer getAccessTokenValidity() { + return this.accessTokenValidity; } - private static String from(String... arr) { - return Arrays.stream(arr).collect(Collectors.joining(",")); + public void setAccessTokenValidity(Integer accessTokenValidity) { + this.accessTokenValidity = accessTokenValidity; } - public Client(String clientId, String clientSecret) { - this.clientId = clientId; - this.secret = clientSecret; + public Integer getRefreshTokenValidity() { + return this.refreshTokenValidity; } - Client() { // JPA why !? + public void setRefreshTokenValidity(Integer refreshTokenValidity) { + this.refreshTokenValidity = refreshTokenValidity; } - public Long getId() { - return id; + public String getAdditionalInformation() { + return this.additionalInformation; } - public String getClientId() { - return clientId; + public void setAdditionalInformation(String additionalInformation) { + this.additionalInformation = additionalInformation; } - public String getSecret() { - return secret; + public String getAutoapprove() { + return this.autoapprove; + } + + public void setAutoapprove(String autoapprove) { + this.autoapprove = autoapprove; } @Override public String toString() { - return "Client { clientId: " + clientId + ", secret: " + secret + ", scopes: [" + scopes + + return "Client { clientId: " + clientId + ", clientSecret: " + clientSecret + ", scope: [" + scope + "], authorizedGrantTypes: [" + authorizedGrantTypes + "], authorities: [" + authorities + - "] autoApproveScopes: [" + autoApproveScopes + "], registeredRedirectUri: [" + registeredRedirectUri + "] }"; + "], accessTokenValidity: [" + accessTokenValidity + "], refreshTokenValidity: [" + refreshTokenValidity + + "] autoapprove: [" + autoapprove + "], webServerRedirectUri: [" + webServerRedirectUri + "] }"; } -} \ No newline at end of file + +} diff --git a/auth-service/src/main/resources/bootstrap.properties b/auth-service/src/main/resources/bootstrap.properties index 2e58c95..b09c30a 100644 --- a/auth-service/src/main/resources/bootstrap.properties +++ b/auth-service/src/main/resources/bootstrap.properties @@ -23,29 +23,12 @@ spring.jpa.show-sql=true # openssl genrsa -out jwt.pem 2048 # openssl rsa -in jwt.pem -config.oauth2.private-key=\ - -----BEGIN RSA PRIVATE KEY----- \ - MIICXQIBAAKBgQDNQZKqTlO/+2b4ZdhqGJzGBDltb5PZmBz1ALN2YLvt341pH6i5\ - mO1V9cX5Ty1LM70fKfnIoYUP4KCE33dPnC7LkUwE/myh1zM6m8cbL5cYFPyP099t\ - hbVxzJkjHWqywvQih/qOOjliomKbM9pxG8Z1dB26hL9dSAZuA8xExjlPmQIDAQAB\ - AoGAImnYGU3ApPOVtBf/TOqLfne+2SZX96eVU06myDY3zA4rO3DfbR7CzCLE6qPn\ - yDAIiW0UQBs0oBDdWOnOqz5YaePZu/yrLyj6KM6Q2e9ywRDtDh3ywrSfGpjdSvvo\ - aeL1WesBWsgWv1vFKKvES7ILFLUxKwyCRC2Lgh7aI9GGZfECQQD84m98Yrehhin3\ - fZuRaBNIu348Ci7ZFZmrvyxAIxrV4jBjpACW0RM2BvF5oYM2gOJqIfBOVjmPwUro\ - bYEFcHRvAkEAz8jsfmxsZVwh3Y/Y47BzhKIC5FLaads541jNjVWfrPirljyCy1n4\ - sg3WQH2IEyap3WTP84+csCtsfNfyK7fQdwJBAJNRyobY74cupJYkW5OK4OkXKQQL\ - Hp2iosJV/Y5jpQeC3JO/gARcSmfIBbbI66q9zKjtmpPYUXI4tc3PtUEY8QsCQQCc\ - xySyC0sKe6bNzyC+Q8AVvkxiTKWiI5idEr8duhJd589H72Zc2wkMB+a2CEGo+Y5H\ - jy5cvuph/pG/7Qw7sljnAkAy/feClt1mUEiAcWrHRwcQ71AoA0+21yC9VkqPNrn3\ - w7OEg8gBqPjRlXBNb00QieNeGGSkXOoU6gFschR22Dzy\ - -----END RSA PRIVATE KEY-----\ +#config.oauth2.private-key=\ +# -----BEGIN RSA PRIVATE KEY----- \ +# -----END RSA PRIVATE KEY-----\ # openssl rsa -in jwt.pem -pubout -config.oauth2.public-key=\ - -----BEGIN PUBLIC KEY-----\ - MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNQZKqTlO/+2b4ZdhqGJzGBDlt\ - b5PZmBz1ALN2YLvt341pH6i5mO1V9cX5Ty1LM70fKfnIoYUP4KCE33dPnC7LkUwE\ - /myh1zM6m8cbL5cYFPyP099thbVxzJkjHWqywvQih/qOOjliomKbM9pxG8Z1dB26\ - hL9dSAZuA8xExjlPmQIDAQAB\ - -----END PUBLIC KEY-----\ +#config.oauth2.public-key=\ +# -----BEGIN PUBLIC KEY-----\ +# -----END PUBLIC KEY-----\ diff --git a/auth-service/src/main/resources/schema.sql b/auth-service/src/main/resources/schema.sql index 41b8b65..9a6c5f4 100644 --- a/auth-service/src/main/resources/schema.sql +++ b/auth-service/src/main/resources/schema.sql @@ -12,6 +12,66 @@ create table if not exists client( registered_redirect_uri varchar(1024) ); +-- drop table if exists oauth_client_details; +create table if not exists oauth_client_details ( + client_id VARCHAR(256) PRIMARY KEY, + resource_ids VARCHAR(256), + client_secret VARCHAR(256), + scope VARCHAR(256), + authorized_grant_types VARCHAR(256), + web_server_redirect_uri VARCHAR(256), + authorities VARCHAR(256), + access_token_validity INTEGER, + refresh_token_validity INTEGER, + additional_information VARCHAR(4096), + autoapprove VARCHAR(256) +); + +-- INSERT INTO `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) +-- VALUES ('acme', '', '$2a$10$z/8fQRJlWmEB2jU3kC2rueX0gtVi340X2/bri6U5Yxw4tdHG/vZJS', 'read,write', 'refresh_token,password', NULL, 'ROLE_USER,ROLE_ADMIN', 0, 0, 'SSO', 'true'); + +-- drop table if exists oauth_client_token; +create table if not exists oauth_client_token ( + token_id VARCHAR(256), + token LONGVARBINARY, + authentication_id VARCHAR(256) PRIMARY KEY, + user_name VARCHAR(256), + client_id VARCHAR(256) +); + +-- drop table if exists oauth_access_token; +create table if not exists oauth_access_token ( + token_id VARCHAR(256), + token LONGVARBINARY, + authentication_id VARCHAR(256) PRIMARY KEY, + user_name VARCHAR(256), + client_id VARCHAR(256), + authentication LONGVARBINARY, + refresh_token VARCHAR(256) +); + +-- drop table if exists oauth_refresh_token; +create table if not exists oauth_refresh_token ( + token_id VARCHAR(256), + token LONGVARBINARY, + authentication LONGVARBINARY +); + +-- drop table if exists oauth_code; +create table if not exists oauth_code ( + code VARCHAR(256), authentication LONGVARBINARY +); + +-- drop table if exists oauth_approvals; +create table if not exists oauth_approvals ( + userId VARCHAR(256), + clientId VARCHAR(256), + scope VARCHAR(256), + status VARCHAR(10), + expiresAt TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', + lastModifiedAt TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' +); + -- users table -- drop table if exists users; create table if not exists users( @@ -29,4 +89,4 @@ create table if not exists authorities( authority varchar_ignorecase(50) not null, constraint fk_authorities_users foreign key(username) references users(username) ); -create unique index if not exists ix_auth_username on authorities (username, authority); \ No newline at end of file +create unique index if not exists ix_auth_username on authorities (username, authority); From 362e7d140b4618f8cc089125b6882a7dd98d55e9 Mon Sep 17 00:00:00 2001 From: Franck Andriano Date: Thu, 19 Jan 2017 16:37:49 +0100 Subject: [PATCH 2/3] Back DefaultTokenServices and explore CompositeTokenGranter... --- .../java/com/jservlet/TcloudClientApplication.java | 8 ++++---- .../src/main/resources/templates/jssoclient.ftl | 10 +++++++--- .../src/main/resources/templates/jssoclient.html | 10 +++++++--- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/tcloud-client/src/main/java/com/jservlet/TcloudClientApplication.java b/tcloud-client/src/main/java/com/jservlet/TcloudClientApplication.java index dcaaaae..5ec1ecb 100644 --- a/tcloud-client/src/main/java/com/jservlet/TcloudClientApplication.java +++ b/tcloud-client/src/main/java/com/jservlet/TcloudClientApplication.java @@ -209,7 +209,7 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; response.setHeader("Access-Control-Allow-Origin", "*"); - response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); + response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE"); response.setHeader("Access-Control-Max-Age", "1800"); response.setHeader("Access-Control-Allow-Headers", "origin,accept,x-requested-with,content-type,access-control-request-method,access-control-request-headers,authorization"); if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { @@ -277,7 +277,7 @@ public Docket tcloudApi() { .securitySchemes(newArrayList(apiKey())) ; } - // Put Authorization bearer +' '+JWT (Jason Web Token) + // Put Authorization bearer +' '+Token @Bean SecurityScheme apiKey() { return new ApiKey("Authorization", "api_key", "header"); @@ -287,7 +287,7 @@ private ApiInfo apiInfo() { ApiInfo apiInfo = new ApiInfo( "Tcloud API Manual", "This online API manual for the development of client-side reference :\n" + - "- Put Authorization header : Bearer+' '+JWT(Jason Web Token)\n", + "- Put Authorization header : Bearer+' '+Token\n", "0.0.1", "Terms of service", new Contact("Swagger Tcloud API Team", "https://github.com/javaguru/tcloud-microservices", "support@jservlet.com"), "GPL-3.0 license", "https://github.com/javaguru/tcloud-microservices/blob/master/LICENSE"); @@ -303,7 +303,6 @@ class TcloudApiGateway { private final TcloudReader tcloudReader; - /* private final TcloudWriter tcloudWriter;*/ @Autowired @@ -396,6 +395,7 @@ public void update(@RequestParam(value = "id") long id, @RequestBody Tcloud tclo @ApiResponses(value = { @ApiResponse(code = 200, message = "Success"), @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 404, message = "Not Found"), @ApiResponse(code = 500, message = "Failure")}) @DeleteMapping(path="/delete") public void delete(@RequestParam(value = "id") Long id) { diff --git a/tcloud-client/src/main/resources/templates/jssoclient.ftl b/tcloud-client/src/main/resources/templates/jssoclient.ftl index 822ab27..045a30e 100644 --- a/tcloud-client/src/main/resources/templates/jssoclient.ftl +++ b/tcloud-client/src/main/resources/templates/jssoclient.ftl @@ -133,7 +133,7 @@ function loginToken(){ // Form input values username & password var $form = $(form_auth); - if ($form.find('input[name="username"]').length>0 && $form.find('input[name="password"]').length>0) { + if ($form.find('input[name="username"]').length>0 && $form.find('input[name="password"]').length>0) { OAuth2Client.username = $form.find('input[name="username"]').val(); OAuth2Client.password = $form.find('input[name="password"]').val(); } @@ -278,8 +278,12 @@ function loginSSOError(xhr) { var jsonResp = JSON.parse(xhr.responseText); - $("#error").html(jsonResp.status+" "+jsonResp.error+" "+jsonResp.message+ - (jsonResp.error_description != undefined ? jsonResp.error_description : "")).css("color","red"); + if (jsonResp.status != undefined && jsonResp.message != undefined && jsonResp.error != undefined) { + $("#error").html(jsonResp.status + " " +jsonResp.error+" "+ jsonResp.message).css("color", "red"); + } + else if (jsonResp.error != undefined && jsonResp.error_description != undefined){ + $("#error").html(jsonResp.error+" "+jsonResp.error_description).css("color","red"); + } } // Ajax Client Services, attach deferred.promise(jqXHR) diff --git a/tcloud-client/src/main/resources/templates/jssoclient.html b/tcloud-client/src/main/resources/templates/jssoclient.html index 27700d3..4b01b84 100644 --- a/tcloud-client/src/main/resources/templates/jssoclient.html +++ b/tcloud-client/src/main/resources/templates/jssoclient.html @@ -133,7 +133,7 @@ function loginToken(){ // Form input values username & password var $form = $(form_auth); - if ($form.find('input[name="username"]').length>0 && $form.find('input[name="password"]').length>0) { + if ($form.find('input[name="username"]').length>0 && $form.find('input[name="password"]').length>0) { OAuth2Client.username = $form.find('input[name="username"]').val(); OAuth2Client.password = $form.find('input[name="password"]').val(); } @@ -287,8 +287,12 @@ function loginSSOError(xhr) { var jsonResp = JSON.parse(xhr.responseText); - $("#error").html(jsonResp.status+" "+jsonResp.error+" "+jsonResp.message+ - (jsonResp.error_description != undefined ? jsonResp.error_description : "")).css("color","red"); + if (jsonResp.status != undefined && jsonResp.message != undefined && jsonResp.error != undefined) { + $("#error").html(jsonResp.status + " " +jsonResp.error+" "+ jsonResp.message).css("color", "red"); + } + else if (jsonResp.error != undefined && jsonResp.error_description != undefined){ + $("#error").html(jsonResp.error+" "+jsonResp.error_description).css("color","red"); + } } // Ajax Client Services, attach deferred.promise(jqXHR) From 7601c0640693ec60f6e1e439b93a9caf19121933 Mon Sep 17 00:00:00 2001 From: Franck Andriano Date: Thu, 19 Jan 2017 18:17:42 +0100 Subject: [PATCH 3/3] Back DefaultTokenServices and explore CompositeTokenGranter... --- microservices-config/tcloud-client.properties | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/microservices-config/tcloud-client.properties b/microservices-config/tcloud-client.properties index efafb01..480b12e 100644 --- a/microservices-config/tcloud-client.properties +++ b/microservices-config/tcloud-client.properties @@ -6,17 +6,26 @@ info.component=Tcloud Client with OAuth2 Sso spring.cloud.stream.bindings.output.destination=tcloud # Oauth2 Resource user endpoint -#security.oauth2.resource.user-info-uri=http://localhost:9191/uaa/user +security.oauth2.resource.user-info-uri=http://localhost:9191/uaa/user # Oauth2 Resource jwt token endpoint -security.oauth2.resource.jwt.key-uri=http://localhost:9191/uaa/oauth/token_key +#security.oauth2.resource.jwt.key-uri=http://localhost:9191/uaa/oauth/token_key +#security.oauth2.resource.id=tcloud # Mandatory at false for OAuth2FeignConfig! security.oauth2.resource.loadBalanced=false # Active resource loadBalanced! spring.oauth2.resource.loadBalanced=true -#security.oauth2.resource.prefer-token-info=false + + +# Oauth2 Resource Client +#security.oauth2.client.client-id=acme +#security.oauth2.client.client-secret=acmesecret +#security.oauth2.client.access-token-uri=http://localhost:9191/uaa/oauth/token +#security.oauth2.client.user-authorization-uri=http://localhost:9191/uaa/oauth/authorize +#security.oauth2.client.client-authentication-scheme=header +#security.oauth2.client.authentication-scheme=form # Protect against CSRF attack! (Only path /login working, but forcing a HTTPS redirect URL!) #security.oauth2.client.use-current-uri=false