From b2e2a518b27be8287968e9ad12031687929913e1 Mon Sep 17 00:00:00 2001 From: liquidjoo Date: Fri, 17 May 2019 19:12:03 +0900 Subject: [PATCH] oauth2 token database access --- .../config/OAuth2SsoServerConfig.java | 22 ++++- .../config/WebMvcConfig.java | 27 ++++++ .../config/WebSecurityConfig.java | 12 +-- .../src/main/resources/application.properties | 2 + .../main/resources/templates/authorize.ftl | 27 ++++++ .../src/main/resources/templates/login.ftl | 20 ++++ .../gradle/wrapper/gradle-wrapper.properties | 3 +- .../gatewayzuul/GatewayZuulApplication.java | 30 ++++++ .../gatewayzuul/config/SecurityConfig.java | 96 +++++++++++++++++-- .../DynamicOauth2ClientContextFilter.java | 45 +++++++++ .../src/main/resources/application.properties | 32 +++++-- test-service/build.gradle | 1 - .../testservice/TestServiceApplication.java | 33 +++++++ .../src/main/resources/application.properties | 4 + 14 files changed, 327 insertions(+), 27 deletions(-) create mode 100644 authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebMvcConfig.java create mode 100644 authorization-server/src/main/resources/templates/authorize.ftl create mode 100644 authorization-server/src/main/resources/templates/login.ftl create mode 100644 gateway-zuul/src/main/java/io/bluemoon/gatewayzuul/filter/DynamicOauth2ClientContextFilter.java diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/OAuth2SsoServerConfig.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/OAuth2SsoServerConfig.java index c8982a3..766fa84 100644 --- a/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/OAuth2SsoServerConfig.java +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/OAuth2SsoServerConfig.java @@ -5,6 +5,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; +import org.springframework.core.io.ClassPathResource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; @@ -19,8 +20,11 @@ import org.springframework.security.oauth2.provider.code.AuthorizationCodeServic import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices; 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.KeyStoreKeyFactory; import javax.sql.DataSource; +import java.security.KeyPair; @Configuration @EnableAuthorizationServer @@ -54,9 +58,9 @@ public class OAuth2SsoServerConfig extends AuthorizationServerConfigurerAdapter @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { // auth server에 대한 설정 -// security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()"); + security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()"); // properties 로 해결 가능 - super.configure(security); +// super.configure(security); } @Override @@ -73,12 +77,17 @@ public class OAuth2SsoServerConfig extends AuthorizationServerConfigurerAdapter .approvalStore(approvalStore) // code service .authorizationCodeServices(authorizationCodeServices); + +// .accessTokenConverter(jwtAccessTokenConverter()); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // client 에 대한 설정 clients.withClientDetails(clientDetailsService); +// clients.inMemory().withClient("system1").secret("1234") +// .authorizedGrantTypes("authorization_code", "refresh_token","password") +// .scopes("read"); } @Bean @@ -101,4 +110,13 @@ public class OAuth2SsoServerConfig extends AuthorizationServerConfigurerAdapter public ApprovalStore jdbcApprovalStore(DataSource dataSource) { return new JdbcApprovalStore(dataSource); } + +// @Bean +// public JwtAccessTokenConverter jwtAccessTokenConverter() { +// JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); +// converter.setSigningKey("abc"); +//// KeyPair keyPair = new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "foobar".toCharArray()).getKeyPair("test"); +//// converter.setKeyPair(keyPair); +// return converter; +// } } diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebMvcConfig.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebMvcConfig.java new file mode 100644 index 0000000..0f858ea --- /dev/null +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebMvcConfig.java @@ -0,0 +1,27 @@ +package io.bluemoon.authorizationserver.config; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.core.Ordered; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.web.filter.ForwardedHeaderFilter; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@EnableWebSecurity +public class WebMvcConfig implements WebMvcConfigurer { + + @Bean + FilterRegistrationBean forwardedHeaderFilter() { + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); + filterRegistrationBean.setFilter(new ForwardedHeaderFilter()); + filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); + return filterRegistrationBean; + } + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/login").setViewName("login"); + registry.addViewController("/oauth/confirm_access").setViewName("authorize"); + } +} diff --git a/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebSecurityConfig.java b/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebSecurityConfig.java index d419262..de120d7 100644 --- a/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebSecurityConfig.java +++ b/authorization-server/src/main/java/io/bluemoon/authorizationserver/config/WebSecurityConfig.java @@ -41,20 +41,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { - http.authorizeRequests().antMatchers("/**").permitAll() + http.formLogin().loginPage("/login").permitAll() .and() - .logout() - .permitAll(); + .authorizeRequests().anyRequest().authenticated(); } - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - // - auth - .inMemoryAuthentication() - .withUser("user1").password("1234").roles("USER"); - } /** * authentication Object managing diff --git a/authorization-server/src/main/resources/application.properties b/authorization-server/src/main/resources/application.properties index 9784617..dcf1858 100644 --- a/authorization-server/src/main/resources/application.properties +++ b/authorization-server/src/main/resources/application.properties @@ -1,4 +1,6 @@ server.port=8081 +server.servlet.context-path=/mk-auth +server.use-forward-headers=false security.oauth2.authorization.check-token-access=isAuthenticated() diff --git a/authorization-server/src/main/resources/templates/authorize.ftl b/authorization-server/src/main/resources/templates/authorize.ftl new file mode 100644 index 0000000..90373b7 --- /dev/null +++ b/authorization-server/src/main/resources/templates/authorize.ftl @@ -0,0 +1,27 @@ + + + + +
+

Please Confirm

+ +

+ Do you authorize "${authorizationRequest.clientId}" at "${authorizationRequest.redirectUri}" to access your + protected resources + with scope ${authorizationRequest.scope?join(", ")}. +

+
+ + + +
+
+ + + +
+
+ + diff --git a/authorization-server/src/main/resources/templates/login.ftl b/authorization-server/src/main/resources/templates/login.ftl new file mode 100644 index 0000000..dc39c82 --- /dev/null +++ b/authorization-server/src/main/resources/templates/login.ftl @@ -0,0 +1,20 @@ + + + + +
+
+
+ + +
+
+ + +
+ + +
+
+ + diff --git a/gateway-zuul/gradle/wrapper/gradle-wrapper.properties b/gateway-zuul/gradle/wrapper/gradle-wrapper.properties index f4d7b2b..d3c18cc 100644 --- a/gateway-zuul/gradle/wrapper/gradle-wrapper.properties +++ b/gateway-zuul/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Fri May 17 17:34:15 KST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/gateway-zuul/src/main/java/io/bluemoon/gatewayzuul/GatewayZuulApplication.java b/gateway-zuul/src/main/java/io/bluemoon/gatewayzuul/GatewayZuulApplication.java index 6cabcc5..de2b181 100644 --- a/gateway-zuul/src/main/java/io/bluemoon/gatewayzuul/GatewayZuulApplication.java +++ b/gateway-zuul/src/main/java/io/bluemoon/gatewayzuul/GatewayZuulApplication.java @@ -2,7 +2,23 @@ package io.bluemoon.gatewayzuul; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoRestTemplateCustomizer; +import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor; +import org.springframework.cloud.netflix.zuul.EnableZuulProxy; +import org.springframework.context.annotation.Bean; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.security.oauth2.client.token.AccessTokenProviderChain; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@EnableZuulProxy @SpringBootApplication public class GatewayZuulApplication { @@ -10,4 +26,18 @@ public class GatewayZuulApplication { SpringApplication.run(GatewayZuulApplication.class, args); } +// @Bean +// UserInfoRestTemplateCustomizer userInfoRestTemplateCustomizer(LoadBalancerInterceptor loadBalancerInterceptor) { +// return template -> { +// List interceptors = new ArrayList<>(); +// interceptors.add(loadBalancerInterceptor); +// AccessTokenProviderChain accessTokenProviderChain = Stream +// .of(new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), +// new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider()) +// .peek(tp -> tp.setInterceptors(interceptors)) +// .collect(Collectors.collectingAndThen(Collectors.toList(), AccessTokenProviderChain::new)); +// template.setAccessTokenProvider(accessTokenProviderChain); +// }; +// } + } diff --git a/gateway-zuul/src/main/java/io/bluemoon/gatewayzuul/config/SecurityConfig.java b/gateway-zuul/src/main/java/io/bluemoon/gatewayzuul/config/SecurityConfig.java index f2f708e..d270dfb 100644 --- a/gateway-zuul/src/main/java/io/bluemoon/gatewayzuul/config/SecurityConfig.java +++ b/gateway-zuul/src/main/java/io/bluemoon/gatewayzuul/config/SecurityConfig.java @@ -1,28 +1,112 @@ package io.bluemoon.gatewayzuul.config; +import io.bluemoon.gatewayzuul.filter.DynamicOauth2ClientContextFilter; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.annotation.Order; +import org.springframework.security.authentication.AuthenticationManager; 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.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; +import org.springframework.security.oauth2.provider.ClientDetailsService; +import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager; +import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import org.springframework.security.web.csrf.CsrfFilter; +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.regex.Pattern; @Configuration @EnableOAuth2Sso @EnableResourceServer +@Order(value = 0) public class SecurityConfig extends WebSecurityConfigurerAdapter { - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication().withUser("user1").password("1234").roles("USER"); - } +// @Bean +// @Primary +// public OAuth2ClientContextFilter dynamicOauth2ClientContextFilter() { +// return new DynamicOauth2ClientContextFilter(); +// } @Override - protected void configure(HttpSecurity http) throws Exception { + public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() - .antMatchers("/**", "/login").permitAll() + .antMatchers("/mk-auth/**", "/login").permitAll().anyRequest().authenticated() .and() + .csrf().requireCsrfProtectionMatcher(csrfRequestMatcher()).csrfTokenRepository(csrfTokenRepository()) + .and() + .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class) .logout().permitAll() .logoutSuccessUrl("/"); } + + private RequestMatcher csrfRequestMatcher() { + return new RequestMatcher() { + + private final Pattern allowedMethods = Pattern.compile("^(GET|HEAD|OPTIONS|TRACE)$"); + + // Disable CSFR protection on the following urls: + private final AntPathRequestMatcher[] requestMatchers = { new AntPathRequestMatcher("/mk-auth/**") }; + + @Override + public boolean matches(HttpServletRequest request) { + if (allowedMethods.matcher(request.getMethod()).matches()) { + return false; + } + + for (AntPathRequestMatcher matcher : requestMatchers) { + if (matcher.matches(request)) { + return false; + } + } + return true; + } + }; + } + + private static Filter csrfHeaderFilter() { + + return new OncePerRequestFilter() { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); + if (csrf != null) { + Cookie cookie = new Cookie("XSRF-TOKEN", csrf.getToken()); + cookie.setPath("/"); + cookie.setSecure(true); + response.addCookie(cookie); + } + filterChain.doFilter(request, response); + } + }; + } + + private static CsrfTokenRepository csrfTokenRepository() { + HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); + repository.setHeaderName("X-XSRF-TOKEN"); + return repository; + } + + + } diff --git a/gateway-zuul/src/main/java/io/bluemoon/gatewayzuul/filter/DynamicOauth2ClientContextFilter.java b/gateway-zuul/src/main/java/io/bluemoon/gatewayzuul/filter/DynamicOauth2ClientContextFilter.java new file mode 100644 index 0000000..f0c40ee --- /dev/null +++ b/gateway-zuul/src/main/java/io/bluemoon/gatewayzuul/filter/DynamicOauth2ClientContextFilter.java @@ -0,0 +1,45 @@ +package io.bluemoon.gatewayzuul.filter; + +import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter; +import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException; +import org.springframework.security.web.DefaultRedirectStrategy; +import org.springframework.security.web.RedirectStrategy; +import org.springframework.web.util.UriComponentsBuilder; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Map; + +public class DynamicOauth2ClientContextFilter extends OAuth2ClientContextFilter { + private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + + @Override + protected void redirectUser(UserRedirectRequiredException e, HttpServletRequest request, HttpServletResponse response) throws IOException { + String redirectUri = e.getRedirectUri(); + UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(redirectUri); + Map requestParams = e.getRequestParams(); + for (Map.Entry param : requestParams.entrySet()) { + builder.queryParam(param.getKey(), param.getValue()); + } + + if (e.getStateKey() != null) { + builder.queryParam("state", e.getStateKey()); + } + + String url = getBaseUrl(request) + builder.build().encode().toUriString(); + this.redirectStrategy.sendRedirect(request, response, url); + + + } + + private String getBaseUrl(HttpServletRequest request) { + StringBuffer url = request.getRequestURL(); + return url.substring(0, url.length() - request.getRequestURI().length() + request.getContextPath().length()); + } + + @Override + public void setRedirectStrategy(RedirectStrategy redirectStrategy) { + this.redirectStrategy = redirectStrategy; + } +} diff --git a/gateway-zuul/src/main/resources/application.properties b/gateway-zuul/src/main/resources/application.properties index 5396dd8..20eb236 100644 --- a/gateway-zuul/src/main/resources/application.properties +++ b/gateway-zuul/src/main/resources/application.properties @@ -1,19 +1,37 @@ server.port=8765 +zuul.routes.mk2-service.path=/service/** +zuul.routes.mk2-service.url=http://127.0.0.1:8082 + + +zuul.routes.mk2-oauth.path=/mk-auth/** zuul.routes.mk2-oauth.url=http://127.0.0.1:8081 +zuul.routes.mk2-oauth.sensitive-headers=Authorization #zuul.routes.mk2-oauth.path=/mk2auth/** -zuul.routes.mk2-oauth.path=/** + zuul.routes.mk2-oauth.strip-prefix=false +zuul.add-proxy-headers=true -security.oauth2.sso.login-path=/mk2/login +security.oauth2.sso.login-path=/login -security.oauth2.client.access-token-uri=http://mk2-oauth/oauth/token -security.oauth2.client.user-authorization-uri=/oauth/authorize + + +security.oauth2.client.access-token-uri=http://127.0.0.1:8081/mk-auth/oauth/token +security.oauth2.client.user-authorization-uri=http://127.0.0.1:8081/mk-auth/oauth/authorize +security.oauth2.resource.token-info-uri=http://127.0.0.1:8081/mk-auth/oauth/check_token security.oauth2.client.client-id=system1 security.oauth2.client.client-secret=1234 -spring.security.user.name=user1 -spring.security.user.password=1234 -security.oauth2.resource.token-info-uri=http://mk2-oauth/check_token \ No newline at end of file + +#security.oauth2.resource.jwt.key-value="abc" +#security.oauth2.resource.id=read +#security.oauth2.resource.service-id=${PREFIX:}resource + + +management.endpoints.web.exposure.include=routes, health, filter +management.endpoint.routes.enabled=true +management.endpoint.filters.enabled=true + + diff --git a/test-service/build.gradle b/test-service/build.gradle index 4bcbf90..f5aaa5c 100644 --- a/test-service/build.gradle +++ b/test-service/build.gradle @@ -26,7 +26,6 @@ ext { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.cloud:spring-cloud-starter-oauth2' - implementation 'org.springframework.cloud:spring-cloud-starter-security' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/test-service/src/main/java/io/bluemoon/testservice/TestServiceApplication.java b/test-service/src/main/java/io/bluemoon/testservice/TestServiceApplication.java index 8664952..d11f97f 100644 --- a/test-service/src/main/java/io/bluemoon/testservice/TestServiceApplication.java +++ b/test-service/src/main/java/io/bluemoon/testservice/TestServiceApplication.java @@ -2,7 +2,16 @@ package io.bluemoon.testservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import java.security.Principal; + +@EnableResourceServer @SpringBootApplication public class TestServiceApplication { @@ -10,4 +19,28 @@ public class TestServiceApplication { SpringApplication.run(TestServiceApplication.class, args); } + @Controller + @RequestMapping("/") + public static class TestController{ + + @RequestMapping(method = RequestMethod.GET) + @ResponseBody + public String helloMk2(Principal principal) { + return principal == null ? "hello anonymous" : "heelo" + principal.getName(); + } + + @PreAuthorize("#oauth2.hasScope('read') and hasRole('ROLE_USER')") + @RequestMapping(value = "secret", method = RequestMethod.GET) + @ResponseBody + public String helloMk2Secret(Principal principal) { + return principal == null ? "hello anonymous" : "heelo" + principal.getName(); + } + + @RequestMapping(method = RequestMethod.GET, value = "test") + @ResponseBody + public String test() { + return "test"; + } + } + } diff --git a/test-service/src/main/resources/application.properties b/test-service/src/main/resources/application.properties index 8b13789..447e2a4 100644 --- a/test-service/src/main/resources/application.properties +++ b/test-service/src/main/resources/application.properties @@ -1 +1,5 @@ +server.port=8082 + + +security.oauth2.resource.token-info-uri=http://127.0.0.1:8081/mk-auth/oauth/check_token \ No newline at end of file