Compare commits

62 Commits
dev ... master

Author SHA1 Message Date
liquidjoo
b69cc45ff0 chore : clean code 2020-08-04 14:11:58 +09:00
liquidjoo
f83a3ab606 chore: 사용하지 않는 패키지 정리 2020-08-04 14:10:10 +09:00
liquidjoo
6457af3084 feat : 문서 설정 및 안쓰는 메서드 제거 2020-08-04 14:08:01 +09:00
liquidjoo
bd4a0708cf feat : 사용하지 않는 모듈 제거 2020-08-04 11:01:26 +09:00
liquidjoo
593d4b66dd security modified 2019-09-24 13:12:20 +09:00
liquidjoo
7758e1d961 authorization exception handler, logout 2019-09-19 18:36:30 +09:00
liquidjoo
bf1e576ffa logout -> token revoke 2019-09-18 18:53:21 +09:00
liquidjoo
c4d7977aa2 auth test 2019-09-17 17:21:31 +09:00
liquidjoo
0253a6340d dynamic routing 2019-09-04 11:42:26 +09:00
liquidjoo
2ec6e54a0a security order set 2019-09-02 20:44:01 +09:00
liquidjoo
8cb7884951 Merge branch 'master' of https://github.com/liquidjoo/spring-cloud-oauth2-sso-mk2 2019-08-30 14:16:11 +09:00
liquidjoo
e0988ca3f5 .. ha.. what the.. 2019-08-30 14:15:58 +09:00
liquidjoo
7563884d03 read me 2019-08-07 23:13:06 +09:00
liquidjoo
6bdf42e640 zuul write 2019-07-30 16:56:38 +09:00
liquidjoo
0611b52851 what the... 2019-07-16 18:58:08 +09:00
liquidjoo
5cb9aa8434 writing reademe 2019-07-16 16:35:47 +09:00
liquidjoo
698ebcfb0d security config modified 2019-07-16 11:52:30 +09:00
liquidjoo
f3700246c0 filter order fix 2019-07-12 17:02:35 +09:00
liquidjoo
93452cfeb1 authserver2, service database mysql to postgresql chanaging 2019-07-11 13:54:33 +09:00
liquidjoo
d2dbddb9d2 resourceServer config, websecurity config setting 2019-07-10 15:26:23 +09:00
liquidjoo
1cad6d916b password change 2019-07-09 14:35:39 +09:00
liquidjoo
2b7f23b7e7 user crud event handler to auth user 2019-07-09 14:34:22 +09:00
liquidjoo
8db3469c6f grant type password adject 2019-07-09 13:56:58 +09:00
liquidjoo
7139f1b098 User event handler to OAuth user 2019-07-09 13:56:38 +09:00
liquidjoo
a793b89656 custom api request 2019-07-09 13:55:35 +09:00
liquidjoo
bbd02d44e7 temp commit 2019-07-05 19:05:46 +09:00
liquidjoo
98d4fef9a7 social login deprecated 2019-07-05 17:31:58 +09:00
liquidjoo
4aa8d9dcb3 modified 2019-07-05 17:30:23 +09:00
liquidjoo
690ae8b69e authorization server grant_type password without sso, code 2019-07-05 17:30:07 +09:00
liquidjoo
f8f19562ea properties modified 2019-07-05 17:03:59 +09:00
liquidjoo
238bf1d77c oauth sso dev 2019-07-05 17:01:26 +09:00
liquidjoo
062269d122 queue delete 2019-07-03 10:13:03 +09:00
liquidjoo
4b56d5a3f3 readme 2019-07-02 19:01:38 +09:00
liquidjoo
9ff670ffcf zuul sso-login 빼고 oauth2 환경 테스트 중 2019-07-02 19:00:54 +09:00
liquidjoo
8077c4715d read me 2019-06-11 15:20:41 +09:00
liquidjoo
a436507e59 ref 수정 2019-06-05 11:10:40 +09:00
liquidjoo
c6d01e0653 image change 2019-06-05 11:09:28 +09:00
liquidjoo
2165b4c7c8 image change 2019-06-05 11:08:55 +09:00
liquidjoo
72097cc8ad line test 2019-06-05 11:07:36 +09:00
liquidjoo
80c51b5be9 line 2019-06-05 10:50:12 +09:00
liquidjoo
2dc0144eb0 image path 수정 2019-06-05 10:48:33 +09:00
liquidjoo
b4432151eb readme... 2019-06-05 10:47:35 +09:00
liquidjoo
9a4bd7a13e readme 쓰는중 2019-06-04 11:58:31 +09:00
liquidjoo
6688e51adc write read me 2019-06-03 17:33:54 +09:00
liquidjoo
4dbbb8bbd9 social login user resolver fix 2019-05-31 16:01:24 +09:00
liquidjoo
c1a5d7fb59 user role column -> table 2019-05-31 11:28:46 +09:00
liquidjoo
d7d166d989 auth schema 변경 2019-05-30 14:20:53 +09:00
liquidjoo
935a50318b writing doc 2019-05-29 19:03:10 +09:00
liquidjoo
056f80c5ac document init 2019-05-29 18:35:49 +09:00
liquidjoo
64da987eca test 2019-05-28 18:29:56 +09:00
liquidjoo
3bcd036b82 principal map modified 2019-05-27 17:30:19 +09:00
liquidjoo
e6d04205e6 auth params setting resolve 2019-05-27 17:13:12 +09:00
liquidjoo
7eee47237e google sso 2019-05-27 15:42:46 +09:00
liquidjoo
3ec25e9a79 google cloud jose add 2019-05-27 15:42:23 +09:00
liquidjoo
41e2175468 google login init 2019-05-27 11:52:57 +09:00
liquidjoo
2016155dd6 spring 2.x deprecated 2019-05-27 11:52:38 +09:00
liquidjoo
992796000b facebook login testing 2019-05-27 11:34:18 +09:00
liquidjoo
319ad587bb social + base login 2019-05-24 18:29:00 +09:00
liquidjoo
0fd06bc576 facebook test 2019-05-23 18:40:32 +09:00
liquidjoo
3671ae00cb user model modified 2019-05-23 11:55:21 +09:00
liquidjoo
939392c7f7 social login working.. 2019-05-22 18:39:00 +09:00
liquidjoo
a83500bd4c authorization server description 2019-05-22 15:31:50 +09:00
68 changed files with 1466 additions and 761 deletions

BIN
after_login_network.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -24,10 +24,13 @@ ext {
}
dependencies {
compile('org.springframework.security:spring-security-oauth2-client')
compile('org.springframework.security:spring-security-oauth2-jose')
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-freemarker'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-oauth2'
// implementation 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.1.5.RELEASE'
implementation 'org.springframework.cloud:spring-cloud-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
compileOnly 'org.projectlombok:lombok'

View File

@@ -2,10 +2,8 @@ package io.bluemoon.authorizationserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
@SpringBootApplication
@EnableResourceServer
public class AuthorizationServerApplication {
public static void main(String[] args) {

View File

@@ -1,11 +1,10 @@
package io.bluemoon.authorizationserver.config;
import io.bluemoon.authorizationserver.service.user.CustomUserDetailsServiceImpl;
import io.bluemoon.authorizationserver.service.user.CustomUserDetailsService;
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;
@@ -21,13 +20,21 @@ import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeSe
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;
/*
* provider configuration의 중요한 측면은 인증 코드 부여가 OAuth 클라이언트에 제공되는 방식임.
* 인증 코드는 최종 사용자를 인증 페이지로 안내하여 사용자가 자격 증명을 입력 할 수 있게하여
* OAuth 클라이언트가 가져 와서 인증 코드가 있는 OAuth 클라이언트로 다시 리디렉션되도록합니다.
*/
@Configuration
@EnableAuthorizationServer
// EnableAuthorizationServer는 OAuth 2.0 인증 서버를 구성 및 사용 설정하고 인증 서버와 상호 작용하는데
// 필요한 여러 엔드 포인트를 사용하도록 설정
public class OAuth2SsoServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
@@ -37,7 +44,7 @@ public class OAuth2SsoServerConfig extends AuthorizationServerConfigurerAdapter
private ClientDetailsService clientDetailsService;
private AuthenticationManager authenticationManager;
private DataSource dataSource;
private CustomUserDetailsServiceImpl customUserDetailsService;
private CustomUserDetailsService customUserDetailsService;
public OAuth2SsoServerConfig(
// AuthorizationCodeServices authorizationCodeServices,
@@ -45,7 +52,7 @@ public class OAuth2SsoServerConfig extends AuthorizationServerConfigurerAdapter
ClientDetailsService clientDetailsService,
AuthenticationManager authenticationManager,
DataSource dataSource,
CustomUserDetailsServiceImpl customUserDetailsService
CustomUserDetailsService customUserDetailsService
) {
// this.authorizationCodeServices = authorizationCodeServices;
// this.approvalStore = approvalStore;
@@ -58,38 +65,67 @@ public class OAuth2SsoServerConfig extends AuthorizationServerConfigurerAdapter
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// auth server에 대한 설정
// 토큰 엔드포인트의 보안 제한 조건을 정
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
// properties 로 해결 가능
// super.configure(security);
}
/**
* OAuth2 서버가 작동하기 위한 Endpoint에 대한 정보를 설정
* 권한 부여 및 토큰 엔드 포인트와 토큰 서비스를 설정.
* AuhorizationEndpoint가 지원하는 부여 유형을 정할 수 있음.
*
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// OAuth2 서버가 작동하기 위한 Endpoint에 대한 정보를 설정
endpoints
// authentication
// 비밀 번호 부여는 AuthenticationManager를 주입해야 켜짐
.authenticationManager(authenticationManager)
// jdbc token processing
.tokenStore(jdbcTokenStore(dataSource))
// refresh token
// 사용자 세부 정보가 필요할 때
.userDetailsService(customUserDetailsService)
// approval store
.approvalStore(approvalStore)
// code service
// refresh token
.reuseRefreshTokens(true)
// 인증 코드 부여에 대한 인증 코드 서비스
.authorizationCodeServices(authorizationCodeServices);
// .accessTokenConverter(jwtAccessTokenConverter());
}
/**
* 클라리언트 세부 사항 서비스의 메모리 내 or JDBC구현을 정의
* JdbcClientDetailsService를 활용해 데이터베이스 테이블로 세부 정보를 업데이트 가능
* AuthorizationServerConfigurer의 콜백
*
* @param clients
* @throws Exception
*/
@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");
}
/**
* 액세스 토큰을 만들 때 액세스 토콘을 수락하는 리소스가 나중에 참조 할 수 있도록 인증을 저장해야함
* 액세스 토콘을 생성 권한 부여에 사용 된 인증을 로드하는데 사용됨.
* 서버간에 데이터베이스를 공유 할 수 있는 경우!!
*
* @param dataSource
* @return
*/
@Bean
public TokenStore jdbcTokenStore(DataSource dataSource) {
return new JdbcTokenStore(dataSource);

View File

@@ -0,0 +1,21 @@
package io.bluemoon.authorizationserver.config;
import io.bluemoon.authorizationserver.config.handler.CustomAccessDeniedHandler;
import io.bluemoon.authorizationserver.config.handler.CustomHttp403ForbiddenEntryPoint;
import org.springframework.context.annotation.Configuration;
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.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.authenticationEntryPoint(new CustomHttp403ForbiddenEntryPoint());
resources.accessDeniedHandler(new CustomAccessDeniedHandler());
resources.resourceId("resource-id");
}
}

View File

@@ -1,16 +1,29 @@
package io.bluemoon.authorizationserver.config;
import io.bluemoon.authorizationserver.domain.social.UserArgumentResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.web.filter.ForwardedHeaderFilter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@EnableWebSecurity
import java.util.List;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private UserArgumentResolver userArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(userArgumentResolver);
}
@Bean
FilterRegistrationBean forwardedHeaderFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();

View File

@@ -1,6 +1,7 @@
package io.bluemoon.authorizationserver.config;
import io.bluemoon.authorizationserver.service.user.CustomUserDetailsServiceImpl;
import io.bluemoon.authorizationserver.config.handler.CustomAuthFailureHandler;
import io.bluemoon.authorizationserver.service.user.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
@@ -10,27 +11,41 @@ import org.springframework.security.authentication.AuthenticationManager;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
//import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
@Configuration
//@Order(SecurityProperties.BASIC_AUTH_ORDER - 6)
@Order(-1)
@EnableWebSecurity
@Order(SecurityProperties.DEFAULT_FILTER_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private CustomUserDetailsServiceImpl customUserDetailsService;
@Autowired
CustomAuthFailureHandler customAuthFailureHandler;
private CustomUserDetailsService customUserDetailsService;
public WebSecurityConfig(
CustomUserDetailsServiceImpl customUserDetailsService
CustomUserDetailsService customUserDetailsService
) {
this.customUserDetailsService = customUserDetailsService;
}
@Bean
public static PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* authentication processing
* if success -> Authentication in info object return
* els fail -> Exception
* impl 구현체 -> authentication provider 에서 구현해서 authentication object를 던져줌
*
* @return AuthenticationManager
* @throws Exception
*/
@@ -42,15 +57,23 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().loginPage("/login").permitAll()
// --------------------------------- sso test
http.formLogin().loginPage("/login").permitAll().failureHandler(customAuthFailureHandler)
.and()
.requestMatchers().antMatchers("/login", "/logout", "/oauth/authorize", "/oauth/confirm_access")
.requestMatchers().antMatchers("/login/**", "/oauth/authorize")
.and()
.authorizeRequests().anyRequest().authenticated();
.authorizeRequests().anyRequest().authenticated()
.and()
.headers().frameOptions().disable()
.and()
.logout().logoutSuccessUrl("/logout").logoutRequestMatcher(new AntPathRequestMatcher("/logout")).invalidateHttpSession(true).deleteCookies("JSESSIONID")
.and()
.oauth2Login()
.loginPage("/login").permitAll().defaultSuccessUrl("/login/success", true).failureHandler(customAuthFailureHandler);
}
/**
* authentication Object managing
*
@@ -62,6 +85,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
auth.authenticationProvider(daoAuthenticationProvider());
}
// @Bean
// @SuppressWarnings("deprecation")
// public static NoOpPasswordEncoder passwordEncoder() {
// return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
// }
/**
* user info at database for make authentication object
*
@@ -75,9 +104,44 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
return daoAuthenticationProvider;
}
@Bean
@SuppressWarnings("deprecation")
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
// social login
// @Bean
// public FilterRegistrationBean oauth2ClientFilterRegistration(
// OAuth2ClientContextFilter filter
// ) {
// FilterRegistrationBean registration = new FilterRegistrationBean();
// registration.setFilter(new ForwardedHeaderFilter());
// registration.setFilter(filter);
// registration.setOrder(-100);
// return registration;
// }
//
// private Filter oauth2Filter() {
// CompositeFilter filter = new CompositeFilter();
// List<Filter> filters = new ArrayList<>();
// filters.add(oauth2Filter(facebook(), "/login/facebook", SocialType.FACEBOOK));
//
// filter.setFilters(filters);
// return filter;
// }
//
// private Filter oauth2Filter(ClientResources client, String path, SocialType socialType) {
// OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(path);
// OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oAuth2ClientContext);
// filter.setRestTemplate(template);
// filter.setTokenServices(new UserTokenService(client, socialType));
// filter.setAuthenticationSuccessHandler((request, response, authentication) ->
// response.sendRedirect("/" + socialType.getVaule() + "/complete"));
// filter.setAuthenticationFailureHandler((request, response, exception) ->
// response.sendRedirect("/error"));
// return filter;
// }
//
// @Bean
// @ConfigurationProperties("facebook")
// public ClientResources facebook() {
// return new ClientResources();
// }
}

View File

@@ -0,0 +1,12 @@
package io.bluemoon.authorizationserver.config.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface SocialUser {
}

View File

@@ -0,0 +1,37 @@
package io.bluemoon.authorizationserver.config.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
System.out.println("---------custom access denied handler");
System.out.println(request.getRequestURI());
System.out.println(accessDeniedException.getMessage());
response.setContentType("application/json;charset=UTF-8");
Map map = new HashMap();
map.put("errorauth", "400");
map.put("message", accessDeniedException.getMessage());
map.put("path", request.getServletPath());
map.put("timestamp", LocalDateTime.now().toString());
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
try {
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), map);
} catch (Exception e) {
throw new ServletException();
}
}
}

View File

@@ -0,0 +1,29 @@
package io.bluemoon.authorizationserver.config.handler;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import java.util.Map;
public class CustomAuthEndpointsApprovalHandler implements UserApprovalHandler {
@Override
public boolean isApproved(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {
return false;
}
@Override
public AuthorizationRequest checkForPreApproval(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {
return null;
}
@Override
public AuthorizationRequest updateAfterApproval(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {
return null;
}
@Override
public Map<String, Object> getUserApprovalRequest(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {
return null;
}
}

View File

@@ -0,0 +1,21 @@
package io.bluemoon.authorizationserver.config.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
@Slf4j
public class CustomAuthFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
// logging 붙이기
System.out.println(exception.getMessage());
}
}

View File

@@ -0,0 +1,21 @@
package io.bluemoon.authorizationserver.config.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@Component
public class CustomAuthSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// logging 붙이기
}
}

View File

@@ -0,0 +1,36 @@
package io.bluemoon.authorizationserver.config.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
public class CustomHttp403ForbiddenEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
System.out.println("-------------------");
System.out.println(request.getRequestURI());
System.out.println("-------------------");
Map map = new HashMap();
map.put("errorentry", "401");
map.put("message", authException.getMessage());
map.put("path", request.getServletPath());
map.put("timestamp", LocalDateTime.now().toString());
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
try {
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), map);
} catch (Exception e) {
throw new ServletException();
}
}
}

View File

@@ -0,0 +1,11 @@
package io.bluemoon.authorizationserver.config.handler;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
@JsonSerialize(using = CustomOAuthExceptionSerializer.class)
public class CustomOAuthException extends OAuth2Exception {
public CustomOAuthException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,34 @@
package io.bluemoon.authorizationserver.config.handler;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
public class CustomOAuthExceptionSerializer extends StdSerializer<CustomOAuthException> {
public CustomOAuthExceptionSerializer() {
super(CustomOAuthException.class);
}
@Override
public void serialize(CustomOAuthException value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeNumberField("code4444", value.getHttpErrorCode());
gen.writeBooleanField("status", false);
gen.writeObjectField("data", null);
gen.writeObjectField("errors", Arrays.asList(value.getOAuth2ErrorCode(), value.getMessage()));
if (value.getAdditionalInformation() != null) {
for (Map.Entry<String, String> entry : value.getAdditionalInformation().entrySet()) {
String key = entry.getKey();
String add = entry.getValue();
gen.writeStringField(key, add);
}
}
gen.writeEndObject();
}
}

View File

@@ -0,0 +1,27 @@
package io.bluemoon.authorizationserver.config.handler;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
public class CustomResponseExceptionTranslator implements WebResponseExceptionTranslator {
@Override
public ResponseEntity translate(Exception e) throws Exception {
if (e instanceof OAuth2Exception) {
OAuth2Exception oAuth2Exception = (OAuth2Exception) e;
return ResponseEntity
.status(oAuth2Exception.getHttpErrorCode())
.body(new CustomOAuthException(oAuth2Exception.getMessage()));
} else if (e instanceof AuthenticationException) {
AuthenticationException authenticationException = (AuthenticationException) e;
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body(new CustomOAuthException(authenticationException.getMessage()));
}
return ResponseEntity
.status(HttpStatus.OK)
.body(new CustomOAuthException(e.getMessage()));
}
}

View File

@@ -1,72 +1,33 @@
package io.bluemoon.authorizationserver.controller.sso;
import io.bluemoon.authorizationserver.domain.UserResponseWrapper;
import io.bluemoon.authorizationserver.domain.oauth.accesstoken.AccessToken;
import io.bluemoon.authorizationserver.service.sso.SsoService;
import io.bluemoon.authorizationserver.config.annotation.SocialUser;
import io.bluemoon.authorizationserver.domain.user.User;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.security.Principal;
@Controller
public class SsoController {
private SsoService ssoService;
private AuthorizationServerTokenServices authorizationServerTokenServices;
private ConsumerTokenServices consumerTokenServices;
public SsoController(
SsoService ssoService
) {
this.ssoService = ssoService;
}
@RequestMapping(value = "/userInfo", method = RequestMethod.POST)
@ResponseBody
public UserResponseWrapper userResponse(
@RequestParam(name = "token") String token,
@RequestParam(name = "clientId") String clientId
) {
AccessToken accessToken = ssoService.getAccessToken(token, clientId);
UserResponseWrapper userResponseWrapper = new UserResponseWrapper();
if (accessToken == null) {
userResponseWrapper.setResult(false);
userResponseWrapper.setMessage("사용자 정보를 조회할 수 없습니다.");
} else {
userResponseWrapper.setMessage(accessToken.getUserName());
}
return userResponseWrapper;
}
@RequestMapping(value = "/userLogout", method = RequestMethod.GET)
public String userLogout(
@RequestParam(name = "clientId") String clientId,
HttpServletRequest request
) {
String userName = request.getRemoteUser();
String baseUri = ssoService.logoutAllClient(clientId, userName);
request.getSession().invalidate();
return "redirect:"+baseUri;
}
@RequestMapping(value = "/oauthCallback", method = RequestMethod.GET)
public String oauthCallback(
@RequestParam(name = "code") String code,
@RequestParam(name = "state") String state,
HttpServletRequest request, ModelMap map
) {
System.out.println(code);
System.out.println(state);
System.out.println(request);
System.out.println(map.toString());
return "aa";
public SsoController(AuthorizationServerTokenServices authorizationServerTokenServices,
ConsumerTokenServices consumerTokenServices) {
this.authorizationServerTokenServices = authorizationServerTokenServices;
this.consumerTokenServices = consumerTokenServices;
}
@RequestMapping(value = "/user")
@@ -75,4 +36,56 @@ public class SsoController {
return user;
}
@GetMapping(value = "/login/success")
public String loginComplete(@SocialUser User user) {
System.out.println(user);
// zuul login page redirect
return "redirect:https://localhost:8765/login";
// return "why not";
}
@PostMapping("/revokeToken")
public void revokeToken(HttpServletRequest request, HttpServletResponse response, Principal principal) {
OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) principal;
OAuth2AccessToken accessToken = authorizationServerTokenServices.getAccessToken(oAuth2Authentication);
consumerTokenServices.revokeToken(accessToken.getValue());
HttpSession httpSession = request.getSession();
httpSession.invalidate();
}
@RequestMapping("/rending")
public String rending(HttpServletRequest request, HttpServletResponse response) {
HttpSession httpSession = request.getSession();
httpSession.invalidate();
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
cookie.setPath("/");
cookie.setSecure(true);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
}
return "redirect:/login";
}
// @GetMapping(value = "/{facebook|google|kakao}/complete")
// public String loginComplete(HttpSession session) {
// OAuth2Authentication authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
// Map<String, String> map = (HashMap<String, String>) authentication.getUserAuthentication().getDetails();
// session.setAttribute("user", User.builder()
// .username(map.get("username"))
// .email(map.get("email"))
// .principal(map.get("id"))
// .socialType(SocialType.FACEBOOK)
// .createdAt(LocalDateTime.now())
// .updatedAt(LocalDateTime.now())
// .build()
// );
// return "redirect:/";
// }
}

View File

@@ -1,7 +1,6 @@
package io.bluemoon.authorizationserver.domain.oauth.accesstoken;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;

View File

@@ -0,0 +1,23 @@
//package io.bluemoon.authorizationserver.domain.social;
//
//import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
//import org.springframework.boot.context.properties.NestedConfigurationProperty;
//import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
//
//public class ClientResources {
//
// @NestedConfigurationProperty
// private AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails();
//
// @NestedConfigurationProperty
// private ResourceServerProperties resource = new ResourceServerProperties();
//
// public AuthorizationCodeResourceDetails getClient() {
// return client;
// }
//
// public ResourceServerProperties getResource() {
// return resource;
// }
//}

View File

@@ -0,0 +1,27 @@
package io.bluemoon.authorizationserver.domain.social;
public enum SocialType {
FACEBOOK("facebook"),
GOOGLE("google"),
UNEEDCOMMS("uneedcomms");
private final String ROLE_PREFIX = "ROLE_";
private String name;
SocialType(String name) {
this.name = name;
}
public String getRoleType() {
return ROLE_PREFIX + name.toUpperCase();
}
public String getVaule() {
return name;
}
public boolean isEquals(String authority) {
return this.getRoleType().equals(authority);
}
}

View File

@@ -0,0 +1,203 @@
package io.bluemoon.authorizationserver.domain.social;
import io.bluemoon.authorizationserver.config.annotation.SocialUser;
import io.bluemoon.authorizationserver.domain.user.User;
import io.bluemoon.authorizationserver.domain.user.UserRepository;
import io.bluemoon.authorizationserver.domain.user.UserRole;
import io.bluemoon.authorizationserver.domain.user.UserRoleRepository;
import org.springframework.core.MethodParameter;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpSession;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
private UserRepository userRepository;
private UserRoleRepository userRoleRepository;
public UserArgumentResolver(
UserRepository userRepository,
UserRoleRepository userRoleRepository
) {
this.userRepository = userRepository;
this.userRoleRepository = userRoleRepository;
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(SocialUser.class) != null &&
parameter.getParameterType().equals(User.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpSession session = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getSession();
User user = (User) session.getAttribute("user");
return getUser(user, session);
}
/**
* 인증된 User 객체를 만드는 메인 메서드
*
* @param user
* @param session
* @return
*/
private User getUser(User user, HttpSession session) {
// 세션에서 가져온 유저가 널일 경우에만
System.out.println("-------------------------------------");
System.out.println(user);
if (user == null) {
try {
// OAuth2Authentication authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
// Map<String, String> map = (HashMap<String, String>) authentication.getUserAuthentication().getDetails();
// User convertUser = convertUser(String.valueOf(authentication.getAuthorities().toArray()[0]), map);
OAuth2AuthenticationToken authentication = (OAuth2AuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
// SecurityContext 사용자의 보호 및 인증된 세션
Map<String, Object> map = authentication.getPrincipal().getAttributes();
System.out.println(map.toString());
User convertUser = convertUser(authentication.getAuthorizedClientRegistrationId(), map);
System.out.println(convertUser.toString());
user = userRepository.findByEmail(convertUser.getEmail());
if (user == null) {
user = userRepository.save(convertUser);
UserRole userRole = UserRole.builder()
.role("USER")
.user(user)
.build();
UserRole userRoles = userRoleRepository.save(userRole);
}
List<UserRole> userRoles = userRoleRepository.findByUser(user);
// role 부여
setRoleIfNotSame(user, authentication, map, userRoles);
session.setAttribute("user", user);
} catch (ClassCastException e) {
return user;
}
}
System.out.println(user);
return user;
}
/**
* 사용자의 인증된 소셜 미디어 타입에 따라 빌더를 사용하여 User 객체를 만들어 주는 가교 역할
*
* @param authority
* @param map
* @return
*/
private User convertUser(String authority, Map<String, Object> map) {
if (SocialType.FACEBOOK.getVaule().equals(authority)) return getModernUser(SocialType.FACEBOOK, map);
else if (SocialType.GOOGLE.getVaule().equals(authority)) return getModernUser(SocialType.GOOGLE, map);
return null;
}
/**
* 페이스북이나 구글 같이 공통되는 명명규칙을 가진 그룹을 맵핑
*
* @param socialType
* @param map
* @return
*/
private User getModernUser(SocialType socialType, Map<String, Object> map) {
if (socialType.getVaule().equals("facebook")) {
return User.builder()
.name(String.valueOf(map.get("name")))
.email(String.valueOf(map.get("email")))
.principal(String.valueOf(map.get("id")))
.socialType(socialType)
.createdAt(LocalDateTime.now())
.updatedAt(LocalDateTime.now())
.build();
} else if (socialType.getVaule().equals("google")) {
return User.builder()
.name(String.valueOf(map.get("name")))
.email(String.valueOf(map.get("email")))
.principal(String.valueOf(map.get("sub")))
.socialType(socialType)
.createdAt(LocalDateTime.now())
.updatedAt(LocalDateTime.now())
.build();
} else {
return User.builder()
.name(String.valueOf(map.get("name")))
.email(String.valueOf(map.get("email")))
.principal(String.valueOf(map.get("id")))
.socialType(socialType)
.createdAt(LocalDateTime.now())
.updatedAt(LocalDateTime.now())
.build();
}
}
/**
* 인증된 authentication이 권한을 갖고 있는지 체크하는 용도
* 만약 저장된 User 권한이 없으면 SecurityContextHolder를 사용하여 해당 소셜미디어 타입으로 권한을 저장
*
* @param user
* @param authentication
* @param map
*/
private void setRoleIfNotSame(User user, OAuth2AuthenticationToken authentication, Map<String, Object> map, List<UserRole> userRoles) {
Map<String, Object> principalMap = new HashMap<>();
if (user.getSocialType().getVaule().equals("google")) {
principalMap.put("id", map.get("sub"));
principalMap.put("name", map.get("name"));
principalMap.put("email", map.get("email"));
} else {
principalMap = map;
}
// spring security authentiaction params setting
// 후.. 찾기 힘들었다..
// if (!authentication.getAuthorities().contains(
// new SimpleGrantedAuthority(user.getSocialType().getRoleType()))) {
// SecurityContextHolder.getContext().setAuthentication(
// new UsernamePasswordAuthenticationToken(principalMap, "N/A", AuthorityUtils.createAuthorityList(user.getSocialType().getRoleType()))
// );
// }
// social default user role
if (userRoles != null) {
List<GrantedAuthority> authoritiesRole = new ArrayList<>(userRoles.size());
for (UserRole ur : userRoles) {
authoritiesRole.add(new SimpleGrantedAuthority(ur.getRole()));
}
if (!authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_USER"))) {
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(principalMap, "N/A", authoritiesRole)
);
}
} else {
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(principalMap, "N/A", AuthorityUtils.createAuthorityList("ROLE_NONE")));
}
}
}

View File

@@ -0,0 +1,32 @@
//package io.bluemoon.authorizationserver.domain.social;
//
//import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor;
//import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
//import org.springframework.security.core.GrantedAuthority;
//import org.springframework.security.core.authority.AuthorityUtils;
//
//import java.util.List;
//import java.util.Map;
//
//public class UserTokenService extends UserInfoTokenServices {
// public UserTokenService(ClientResources resources, SocialType socialType) {
// super(resources.getResource().getUserInfoUri(), resources.getClient().getClientId());
//
// setAuthoritiesExtractor(new OAuth2AuthoritiesExtractor(socialType));
//
// }
//
// public static class OAuth2AuthoritiesExtractor implements AuthoritiesExtractor {
// private String socialType;
//
// public OAuth2AuthoritiesExtractor(SocialType socialType) {
// this.socialType = socialType.getRoleType();
// }
//
// @Override
// public List<GrantedAuthority> extractAuthorities(Map<String, Object> map) {
// return AuthorityUtils.createAuthorityList(this.socialType);
// }
// }
//}

View File

@@ -10,26 +10,25 @@ import java.util.Collection;
import java.util.List;
@Data
public class UserDetail implements UserDetails {
public class CustomUserDetails implements UserDetails {
private static final long serialVersionUID = 6396079419309274853L;
private Long id;
private String username;
private String password;
private String userType;
private List<String> roles;
private List<String> userRole;
public UserDetail(User user) {
public CustomUserDetails(User user, List<String> userRole) {
this.id = user.getId();
this.username = user.getUserName();
this.username = user.getUsername();
this.password = user.getPassword();
this.userType = user.getUserType();
this.userRole = userRole;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
for (String role: roles) {
for (String role : userRole) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;

View File

@@ -1,28 +1,68 @@
package io.bluemoon.authorizationserver.domain.user;
import lombok.Data;
import io.bluemoon.authorizationserver.domain.social.SocialType;
import lombok.*;
import javax.persistence.*;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.Collection;
@Entity
@Data
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 20, nullable = false, unique = true)
private String userName;
@Column
private String username;
@Column(length = 100, nullable = false)
@Column
private String name;
@Column
private String password;
//1:수퍼관리자, 2:관리자, 3:사용자
@Column(length = 1, nullable = false)
private String userType;
@Column
private String email;
@Column(nullable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date regDate = new Date();
@Column
private String principal;
@Column
@Enumerated(EnumType.STRING)
private SocialType socialType;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "user", fetch = FetchType.EAGER)
private Collection<UserRole> userRole;
@Column
private LocalDateTime createdAt;
@Column
private LocalDateTime updatedAt;
// //1:수퍼관리자, 2:관리자, 3:사용자
// @Column
// private String userType;
// @Column
// @Temporal(TemporalType.TIMESTAMP)
// private Date regDate = new Date();
@Builder
public User(String username, String name, String password, String email, String principal, LocalDateTime createdAt, LocalDateTime updatedAt, SocialType socialType) {
this.username = username;
this.name = name;
this.password = password;
this.email = email;
this.principal = principal;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.socialType = socialType;
}
}

View File

@@ -1,8 +1,9 @@
package io.bluemoon.authorizationserver.domain.user;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
public interface UserRepository extends JpaRepository<User, Long> {
User findByUserName(String userName);
User findByUsername(String username);
User findByEmail(String email);
}

View File

@@ -0,0 +1,30 @@
package io.bluemoon.authorizationserver.domain.user;
import lombok.*;
import javax.persistence.*;
@Getter
@Setter
@Entity
@ToString(exclude = "user")
@NoArgsConstructor
@AllArgsConstructor
public class UserRole {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String role;
@ManyToOne(optional = false)
@JoinColumn(name = "userId")
private User user;
@Builder
public UserRole(User user, String role) {
this.user = user;
this.role = role;
}
}

View File

@@ -0,0 +1,10 @@
package io.bluemoon.authorizationserver.domain.user;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface UserRoleRepository extends JpaRepository<UserRole, Integer> {
List<UserRole> findByUser(User user);
}

View File

@@ -7,8 +7,8 @@ import io.bluemoon.authorizationserver.domain.oauth.client.ClientRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
@@ -17,11 +17,11 @@ import java.util.Map;
import java.util.Optional;
@Service
public class SsoServiceImpl implements SsoService{
public class DefaultSsoService implements SsoService {
private AccessTokenRepository accessTokenRepository;
private ClientRepository clientRepository;
public SsoServiceImpl(
public DefaultSsoService(
AccessTokenRepository accessTokenRepository,
ClientRepository clientRepository
) {
@@ -44,13 +44,11 @@ public class SsoServiceImpl implements SsoService{
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] bytes = digest.digest(value.getBytes("UTF-8"));
byte[] bytes = digest.digest(value.getBytes(StandardCharsets.UTF_8));
return String.format("%032x", new BigInteger(1, bytes));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("MD5 algorithm not avilable. Fatal (should be in the JDK).");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 encoding not available. Fatal (should be in the JDK).");
}
}

View File

@@ -0,0 +1,65 @@
package io.bluemoon.authorizationserver.service.user;
import io.bluemoon.authorizationserver.domain.user.*;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class CustomUserDetailsService implements UserDetailsService {
// User Info
private UserRepository userRepository;
private UserRoleRepository userRoleRepository;
public CustomUserDetailsService(
UserRepository userRepository,
UserRoleRepository userRoleRepository
) {
this.userRepository = userRepository;
this.userRoleRepository = userRoleRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("why?????????????" + username);
User user = userRepository.findByUsername(username);
System.out.println(user);
List<UserRole> userRole = userRoleRepository.findByUser(user);
System.out.println(userRole);
System.out.println("---------------------------");
List<String> urs = new ArrayList<>();
for (UserRole ur : userRole) {
urs.add(ur.getRole());
}
if (user == null) {
throw new UsernameNotFoundException("UsernameNotFound[" + username + "]");
}
CustomUserDetails userDetail = new CustomUserDetails(user, urs);
System.out.println(userDetail);
return userDetail;
}
/**
* User role check
* @param user
* @param userRole
* @return
*/
// private CustomUserDetails createUser(User user, List<UserRole> userRole) {
// CustomUserDetails userDetail =
//
//// if (userDetail.getSocial_type().getVaule().equals("FACEBOOK")) {
//// userDetail.setRoles(Arrays.asList("ROLE_FACEBOOK"));
//// } else {
//// userDetail.setRoles(Arrays.asList("ROLE_USER"));
//// }
// return userDetail;
// }
}

View File

@@ -1,52 +0,0 @@
package io.bluemoon.authorizationserver.service.user;
import io.bluemoon.authorizationserver.domain.user.User;
import io.bluemoon.authorizationserver.domain.user.UserDetail;
import io.bluemoon.authorizationserver.domain.user.UserRepository;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Arrays;
@Service
public class CustomUserDetailsServiceImpl implements UserDetailsService {
// User Info
private UserRepository userRepository;
public CustomUserDetailsServiceImpl(
UserRepository userRepository
) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUserName(username);
if (user == null) {
throw new UsernameNotFoundException("UsernameNotFound[" + username + "]");
}
UserDetail userDetail = createUser(user);
return userDetail;
}
/**
* User role check
* @param user
* @return
*/
private UserDetail createUser(User user) {
UserDetail userDetail = new UserDetail(user);
if (userDetail.getUserType().equals("1")) {
userDetail.setRoles(Arrays.asList("ROLE_ADMIN"));
} else {
userDetail.setRoles(Arrays.asList("ROLE_USER"));
}
return userDetail;
}
}

View File

@@ -1,19 +1,30 @@
server.port=8081
server.servlet.context-path=/mk-auth
server.use-forward-headers=false
security.oauth2.authorization.check-token-access=isAuthenticated()
spring.main.allow-bean-definition-overriding=true
spring.datasource.url=jdbc:mysql://127.0.0.1/oauth2?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=uneed3515
spring.datasource.password=bluemoon
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.platform=schema
spring.jpa.database = MYSQL
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDB53Dialect
spring.jpa.database=MYSQL
spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
#spring.jpa.generate-ddl=false
#spring.jpa.hibernate.ddl-auto=none
#spring.jpa.hibernate.ddl-auto=none
spring.security.oauth2.client.registration.facebook.client-id=715358882216622
spring.security.oauth2.client.registration.facebook.client-secret=a39d8f1e06e8c3863d12e8461f4991e8
spring.security.oauth2.client.registration.google.client-id=534360107417-qhkkasmnj4tml8iuk3v1dh72ipjvf2qp.apps.googleusercontent.com
spring.security.oauth2.client.registration.google.client-secret=XkmQ3KPTpmTkWb8X6_1WRR5i
#facebook.client.access-token-uri=https://graph.facebook.com/oauth/access_token
#facebook.client.user-authorization-uri=https://www.facebook.com/dialog/oauth
#facebook.client.token-name=oauth_token
#facebook.client.authentication-scheme=query
#facebook.client.client-authentication-scheme=form
#
#facebook.resource.user-info-uri=https://graph.facebook.com/me?fields=id
logging.level.web=debug
spring.http.log-request-details=true

View File

@@ -1,4 +1,4 @@
use uneed_oauth;
-- used in tests that use Mysql Local
create table oauth_client_details (
client_id VARCHAR(255) PRIMARY KEY,
@@ -7,8 +7,6 @@ create table oauth_client_details (
scope VARCHAR(255),
authorized_grant_types VARCHAR(255),
web_server_redirect_uri VARCHAR(255),
logout_uri VARCHAR(255),
base_uri VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,

View File

@@ -1,27 +0,0 @@
<html>
<head>
</head>
<body>
<div class="container">
<h2>Please Confirm</h2>
<p>
Do you authorize "${authorizationRequest.clientId}" at "${authorizationRequest.redirectUri}" to access your
protected resources
with scope ${authorizationRequest.scope?join(", ")}.
</p>
<form id="confirmationForm" name="confirmationForm"
action="../oauth/authorize" method="post">
<input name="user_oauth_approval" value="true" type="hidden"/>
<input type="hidden" id="csrf_token" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<button class="btn btn-primary" type="submit">Approve</button>
</form>
<form id="denyForm" name="confirmationForm"
action="../oauth/authorize" method="post">
<input name="user_oauth_approval" value="false" type="hidden"/>
<input type="hidden" id="csrf_token" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<button class="btn btn-primary" type="submit">Deny</button>
</form>
</div>
</body>
</html>

View File

@@ -0,0 +1,27 @@
<html>
<head>
</head>
<body>
<div class="container">
<h2>Please Confirm</h2>
<p>
Do you authorize "${authorizationRequest.clientId}" at "${authorizationRequest.redirectUri}" to access your
protected resources
with scope ${authorizationRequest.scope?join(", ")}.
</p>
<form action="../oauth/authorize" id="confirmationForm"
method="post" name="confirmationForm">
<input name="user_oauth_approval" type="hidden" value="true"/>
<input id="csrf_token" name="${_csrf.parameterName}" type="hidden" value="${_csrf.token}"/>
<button class="btn btn-primary" type="submit">Approve</button>
</form>
<form action="../oauth/authorize" id="denyForm"
method="post" name="confirmationForm">
<input name="user_oauth_approval" type="hidden" value="false"/>
<input id="csrf_token" name="${_csrf.parameterName}" type="hidden" value="${_csrf.token}"/>
<button class="btn btn-primary" type="submit">Deny</button>
</form>
</div>
</body>
</html>

View File

@@ -1,20 +0,0 @@
<html>
<head>
</head>
<body>
<div class="container">
<form role="form" action="login" method="post">
<div class="form-group">
<label for="username">Username:</label>
<input type="text" class="form-control" id="username" name="username"/>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" class="form-control" id="password" name="password"/>
</div>
<input type="hidden" id="csrf_token" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</body>
</html>

View File

@@ -0,0 +1,52 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<link crossorigin="anonymous" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" rel="stylesheet">
<head>
</head>
<body>
<div class="container">
<form method="post" role="form" th:action="@{/login}">
<div class="form-group row">
<label class="col-sm-2 col-form-label" for="username">ID</label>
<div class="col-sm-10">
<input class="form-control" id="username" name="username" placeholder="id" type="text">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label" for="password">Password</label>
<div class="col-sm-10">
<input class="form-control" id="password" name="password" placeholder="password" type="password">
</div>
</div>
<input id="csrf_token" name="${_csrf.parameterName}" type="hidden" value="${_csrf.token}"/>
<button class="btn btn-primary" type="submit">Sign in</button>
</form>
<div>
<a aria-pressed="false" class="btn btn-primary btn-lg active" href="/mk-auth/oauth2/authorization/facebook"
role="button">Facebook</a>
</div>
<div>
<a aria-pressed="false" class="btn btn-secondary btn-lg active" href="/mk-auth/oauth2/authorization/google"
role="button">Google</a>
</div>
</div>
</body>
<script crossorigin="anonymous"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script crossorigin="anonymous"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script crossorigin="anonymous"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
</html>

View File

@@ -13,15 +13,15 @@ import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class ContentsApplication {
public static void main(String[] args) {
SpringApplication.run(ContentsApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ContentsApplication.class, args);
}
}

View File

@@ -8,28 +8,20 @@ spring.main.allow-bean-definition-overriding=true
#spring.http.multipart.max-file-size=20mb
#spring.mvc.async.request-timeout=5000
#server.tomcat.uri-encoding=UTF-8
hystrix.command.CircuitBreakerCommandKey.execution.isolation.thread.timeoutInMilliseconds=2000
hystrix.command.CircuitBreakerCommandKey.circuitBreaker.requestVolumeThreshold=20
hystrix.command.CircuitBreakerCommandKey.errorThresholdPercentage=50
eureka.instance.prefer-ip-address=true
#eureka.client.service-url.defalutZone=http://127.0.0.1:8761/eureka
#eureka.client.registry-fetch-interval-seconds=1
#eureka.instance.lease-renewal-interval-in-seconds=1
# ribbon
#scheduler.ribbon.listOfServers=
# facebook
# actuator
#management.endpoints.web.exposure.exclude=env, beans
#management.endpoints.web.base-path=/schedulerActuator
#management.server.port=9991
#management.endpoints.web.path-mapping.health=healthCheck
# log
logging.file=./log/contents

View File

@@ -2,26 +2,21 @@ 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.core.annotation.AuthenticationPrincipal;
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 org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.client.RestTemplate;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@EnableZuulProxy
@SpringBootApplication
@@ -32,13 +27,31 @@ public class GatewayZuulApplication {
}
@Controller
@RequestMapping("/")
public static class TestController {
@RequestMapping(method = RequestMethod.GET)
public String test(Principal principal) {
System.out.println(principal.getName());
System.out.println(principal.toString());
return "aa";
@RequestMapping(value = "/gateway/logout", method = RequestMethod.GET)
public String signOut(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
Object details = authentication.getDetails();
String token = ((OAuth2AuthenticationDetails) details).getTokenValue();
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8081/mk-auth/revokeToken";
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + token);
HttpEntity<Object> requestEntity = new HttpEntity<Object>(headers);
restTemplate.exchange(url, HttpMethod.POST, requestEntity, Void.class);
HttpSession httpSession = request.getSession();
httpSession.invalidate();
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
cookie.setPath("/");
cookie.setSecure(true);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
return "redirect:" + "http://localhost:8765/mk-auth/rending";
}
}

View File

@@ -1,24 +1,13 @@
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.*;
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;
@@ -35,50 +24,7 @@ import java.util.regex.Pattern;
@Configuration
@EnableOAuth2Sso
@EnableResourceServer
@Order(value = -1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// @Bean
// @Primary
// public OAuth2ClientContextFilter dynamicOauth2ClientContextFilter() {
// return new DynamicOauth2ClientContextFilter();
// }
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.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;
}
};
}
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static Filter csrfHeaderFilter() {
@@ -104,6 +50,38 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
return repository;
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/mk-auth/**", "/login").permitAll().anyRequest().authenticated()
.and()
.logout().logoutSuccessUrl("/gateway/logout").logoutRequestMatcher(new AntPathRequestMatcher("/logout")).invalidateHttpSession(true).deleteCookies("JSESSIONID").clearAuthentication(true);
}
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;
}
};
}
}

View File

@@ -0,0 +1,56 @@
package io.bluemoon.gatewayzuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class AuthenticationFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
RequestContext ctx = RequestContext.getCurrentContext();
if (auth instanceof OAuth2Authentication) {
Object details = auth.getDetails();
if (details instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails oauth = (OAuth2AuthenticationDetails) details;
ctx = RequestContext.getCurrentContext();
return true;
}
}
return false;
}
@Override
public Object run() throws ZuulException {
String contextUri = RequestContext.getCurrentContext().getRequest().getRequestURI();
if (contextUri.equals("/uaa/user")) {
try {
RequestContext.getCurrentContext().getResponse().sendRedirect("http://localhost:8765/");
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}

View File

@@ -18,8 +18,8 @@ public class DynamicOauth2ClientContextFilter extends OAuth2ClientContextFilter
protected void redirectUser(UserRedirectRequiredException e, HttpServletRequest request, HttpServletResponse response) throws IOException {
String redirectUri = e.getRedirectUri();
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(redirectUri);
Map<String, String > requestParams = e.getRequestParams();
for (Map.Entry<String ,String> param : requestParams.entrySet()) {
Map<String, String> requestParams = e.getRequestParams();
for (Map.Entry<String, String> param : requestParams.entrySet()) {
builder.queryParam(param.getKey(), param.getValue());
}

View File

@@ -13,6 +13,5 @@ public class HeaderEnhanceFilter implements Filter {
// test if request url is permit all, then remove authorization from header
}
}

View File

@@ -1,39 +1,30 @@
server.port=8765
zuul.sensitive-headers=Cookie,Set-Cookie
zuul.routes.mk2-service.path=/service/**
zuul.sensitive-headers=
zuul.routes.mk2-service.path=/api/**
zuul.routes.mk2-service.url=http://127.0.0.1:8082
zuul.routes.mk2-service.sensitive-headers=Cookie,Set-Cookie
zuul.routes.mk2-service.sensitive-headers=
zuul.routes.mk2-oauth.path=/mk-auth/**
zuul.routes.mk2-oauth.url=http://127.0.0.1:8081
zuul.routes.mk2-oauth.sensitive-headers=Cookie,Set-Cookie
#zuul.routes.mk2-oauth.url=https://59a7bc58.ngrok.io
zuul.routes.mk2-oauth.url=http://localhost:8081
zuul.routes.mk2-oauth.sensitive-headers=
#zuul.routes.mk2-oauth.path=/mk2auth/**
zuul.routes.mk2-oauth.strip-prefix=false
zuul.add-proxy-headers=true
security.oauth2.sso.login-path=/login
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.user-info-uri=http://127.0.0.1:8081/mk-auth/user
#security.oauth2.resource.prefer-token-info=false
security.oauth2.client.access-token-uri=http://localhost:8081/mk-auth/oauth/token
# /oauth/authorize 요청은 클라이언트가 리소스 서버의 api를 사용하기 위해 사용자(리소스 소유자)에게
# 권한 위임 동의를 받기 위한 페이지를 출력하는 기능을 수행
security.oauth2.client.user-authorization-uri=http://localhost:8081/mk-auth/oauth/authorize
security.oauth2.resource.user-info-uri=http://localhost:8081/mk-auth/user
security.oauth2.resource.prefer-token-info=false
security.oauth2.client.client-id=system1
security.oauth2.client.client-secret=1234
#management.security.enabled=false
#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
logging.level.web=debug
spring.http.log-request-details=true

29
queue/.gitignore vendored
View File

@@ -1,29 +0,0 @@
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
/out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/

View File

@@ -1,40 +0,0 @@
buildscript {
ext {
springBootVersion = '2.1.1.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'io.bluemoon'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', 'Greenwich.SR1')
}
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-aws-messaging'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}

Binary file not shown.

View File

@@ -1,6 +0,0 @@
#Sat Mar 23 23:56:43 KST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip

172
queue/gradlew vendored
View File

@@ -1,172 +0,0 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
queue/gradlew.bat vendored
View File

@@ -1,84 +0,0 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,18 +1,14 @@
spring.application.name=queue
server.port=8080
spring.jackson.serialization.write-dates-as-timestamps=false
#spring.datasource.url=jdbc:mysql://rds.master.uneedcomms.net/mmc?charset=utf8
#spring.datasource.username=mmc_crud
#spring.datasource.password=s3art33c
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
cloud.aws.credentials.access-key=AKIAJG77K3AKVKQVE3LA
cloud.aws.credentials.secret-key=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
cloud.aws.region.auto=false
cloud.aws.region.static=ap-northeast-2
cloud.aws.stack.auto=false
#sqs.queue_name=sender-sms.fifo
#sqs.url=https://sqs.ap-northeast-2.amazonaws.com/203872522995/sender-sms.fifo

View File

@@ -1,6 +0,0 @@
pluginManagement {
repositories {
gradlePluginPortal()
}
}
rootProject.name = 'queue'

View File

@@ -1,23 +0,0 @@
package io.bluemoon.queue;
import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.aws.messaging.config.annotation.EnableSqs;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class QueueApplication {
public static void main(String[] args) {
SpringApplication.run(QueueApplication.class, args);
}
// @Bean
// public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSQSAsync) {
// return new QueueMessagingTemplate(amazonSQSAsync);
// }
}

View File

@@ -1,16 +0,0 @@
package io.bluemoon.queue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class QueueApplicationTests {
@Test
public void contextLoads() {
}
}

173
readme.md Normal file
View File

@@ -0,0 +1,173 @@
# Zuul 과 Authorization Server를 통해 SSO 시스템 개발
- overview
- usage
- gradle
- Goals
- keys points of sample
## 설명 *() 괄호 안의 내용은 프로젝트 이름*
- 스프링 클라우드를 사용해서 만든 OAuth2 SSO 시스템 개발. *(gateway-zuul, Authorization-server)*
## 환경
- java 8
- Spring boot 2.1.5
- Gradle
## 개발하다가
개발을 하면서 약간의 이슈는 SSO Login을 Zuul을 통해 AuthorizationServer에 대한 정보를 가리기 위함인데 아마 ```security.oauth2.client.user-authorization-uri```, ```security.oauth2.client.access-token-uri``` 이 두부분에 uri에 url을 넣어서 그런것 같은데 설정이 잘 못되었는지 서비스 디스커버리를 쓰지 않아서인지 uri만 입력시에 잘못된 uri라고 뜨더군요..
또 많은 예가 있었지만 AuthorizationServerConfigurerAdapter 의 config 설정이 in-memory로 되어 있어서 여러 유저가 존재할 때 mysql 기준으로 데이터베이스를 통해 인증을 할 수 있게 만들었습니다.
## 인증 네트워크
Zuul에 등록되어진 리소스 서비스에 접근하려고 할 때 인증 및 인가 네트워크
![Image of before_auth](https://github.com/liquidjoo/spring-cloud-oauth2-sso-mk2/blob/master/request_auth_network.png)
로그인이 성공했을 때
![Image of after_auth](https://github.com/liquidjoo/spring-cloud-oauth2-sso-mk2/blob/master/after_login_network.png)
## SSO Login Flow
![Image of flow](https://github.com/liquidjoo/spring-cloud-oauth2-sso-mk2/blob/master/zuul_flow.png)
```ref) https://github.com/kakawait/uaa-behind-zuul-sample ```
## Zuul
**gateway-zuul(SSO Gateway)**
security 내의 **security.oauth2.sso.login-path=/login** 프로퍼티 설정과 zuul의 게이트웨이 특성을 이용해 sso로 구현이 가능.
zuul이 zuul proxy server, resource server 역할을 수행.
```java
// SecurityConfig.java
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/mk-auth/**", "/login").permitAll().anyRequest().authenticated()
.and()
.logout().logoutSuccessUrl("/gateway/logout").logoutRequestMatcher(new AntPathRequestMatcher("/logout")).invalidateHttpSession(true).deleteCookies("JSESSIONID").clearAuthentication(true);
}
```
antMatchers에 접근하고자 하는 uri 접근제어.
**logout**
logout 요청시 (http://localhost:8765/logout) /gateway/logout을 통해 zuul 쿠키 및 세션을 삭제, token revoke 후
redirect를 통해 인증 서버에서도 쿠키 및 세션을 삭제
**주요 properties 설정**
```
security.oauth2.sso.login-path=/login
security.oauth2.client.access-token-uri=http://localhost:8765/mk-auth/oauth/token
# /oauth/authorize 요청은 클라이언트가 리소스 서버의 api를 사용하기 위해 사용자(리소스 소유자)에게
# 권한 위임 동의를 받기 위한 페이지를 출력하는 기능을 수행
security.oauth2.client.user-authorization-uri=http://localhost:8765/mk-auth/oauth/authorize
security.oauth2.resource.user-info-uri=http://localhost:8081/mk-auth/user
```
sso.login-path를 통해 인증서버와 통신 후 user-info-uri를 통해 인증이 데이터가 정상적으로 나오면 security, zuul.routes를 통해 sso처럼 접근이 가능.
## Authorization Server (User Account and Authentication Service -> UAA)
**권한 코드 방식(Authorization Code flow)** *[current project name = authorization-server]*
- 클라이언트가 다른 사용자 대신 특정 리소스에 접근을 요청할 때 사용되어짐.
- 리소스 접근을 위한 id, password, code(auth server)를 사용해서 리소스에 대한 엑세스 토큰 발급.
- 현재 SSO Login 시에 사용된 인증 방식으로 구현.
- gateway-zuul 프로젝트 내에서 ```security.oauth2.sso.login-path=/login``` 의 프로퍼티를 사용해서 login page로 이동 시켜준다.
물론 이 때 로그인패스는 UAA(auth server)의 로그인 페이지로 이동하며 properties(gateway-zuul)내의 client-id, client-secret, redirect_url 을 사용해 redirect_uri로 code를 발급 후
id, password를 받기 위해 login page로 이동하게 되어진다.
- 두 단계로 나누어서 설명 (위의 SSO Login Flow를 보게 되면 과정을 알 수가 있다.)
1. 코드 발급
```
curl -X GET http://localhost/oauth/authorize -G -d "client_id=system" -d "scope=read" -d "grant_type=authorization_code" -d "response_type=code" -d "redirect_uri=http://localhost/login"
```
2. 발급된 코드로 인증
인증이 완료가 되었으면 redirect_uri로 query_string이 code=asdf 이런 식으로 붙어서 오게 되며
```
curl -u client_id:client_secret http://localhost/oauth/token -d "grant_type=authorization_code" -d "code=asdf" -d "scope=read" -d "redirect_uri=http://localhost/login" -d "username=blue" -d "password=moon"
```
* ~~문제점 sso login form이 있는데 curl 을 통해 토큰을 발급하게 되면 로그인 페이지로 계속 리다이렉트 되어서 인증 토큰이 정상적으로 발급이 잘 안됨..(실력 부족 ㅜ)~~
* SSO는 Form Login 필수, Zuul(Gateway)을 통해 권한을 필요로 하는 경로에 접근시 Security Session을 찾을 수 없을 때 Login Page로 강제 리다이렉트가 처리됨.
* 위의 과정은 토큰을 받아서 Zuul 의 Routes에 직접적으로 접근할 때 사용할 수 있다.
**리소스 소유자 암호 자격 증명 타입(Resource Owner Password Credentials Grant Type)** *[current project name = authorization-server2]*
리소스 접근 시에 id, password, client-id, client-secret 사용해서 리소스에 대한 엑세스 토큰, 리프레쉬 토큰 발급
```
curl -u client_id:client_secret http://localhost/oauth/token -d "grant_type=password&username=blue&password=moon"
```
리프레쉬 토큰도 같이 발급되며 리프레쉬 토큰이 만료 되기 전까진 토큰을 생성하는게 아니라 리프레쉬 토큰으로 액세스 토큰을 갱신 (2ddb0b27-a62a-4719-b0c4-c8b23ab537bd <- 발급 받은 토큰)
```
curl -u client_id:client_secret http://localhost/oauth/token -d "grant_type=refresh_token&scope=read&refresh_token=2ddb0b27-a62a-4719-b0c4-c8b23ab537bd"
```
## 토큰 발급 후
- OAuth Token 사용 시
Cookie에 http only 속성을 추가 후에 토큰(암호화(복호화 가능하게))을 담는다 (access-token)
refresh-token은 별도의 storage가 필요 (보안을 위해)
Spring Security Context Holder, Principal 객체를 통해 토큰에 대한 유저 정보를 받아 볼 수 있다.
- JWT 사용 시
각 서비스 별로 JWT 해석기가 필요하며, JWT를 사용 시에는 인증서를 통해 jwt를 만들면 된다.
jwt는 이미 토큰에 정보를 갖고 있기에 db에 대한 레이턴시가 OAuth token에 비해 많이 줄어든다. 각 유저 정보와 jwt 토큰 관리하는 스토리지(redis)가 존재하면 된다.
- refresh token
1. 로그인 성공 시에 Access Token, Refresh Token을 발급.
2. 회원 관리 스토리지에 Refresh Token은 따로 저장 후 Access Token만 헤더에 실어서 요청을 보냄.
3. Validation Access Token (check_token uri 이용)
4. 각자의 로직이 들어감 예) Access Token이 만료 되었으면 갱신을 할 것인 가 또는 다시 로그인을 시킬 것인가 등의 작업이 필요.
## zuul dynamic routing
개발 하면서 zuul에서 라우팅할 때 포워딩, 라우팅 되어 있는 서비스들의 헬스 체크 같은걸 함.
라우팅 되어 있는 서비스가 끊겼을 때 zuul에서는 커넥션 에러와 포워딩 익셉션을 던지며, 서비스가 켜져 있더라고 하더라도
이미 끊겼다면 동적 라우팅 처리를 해줘야한다.
```bash
com.netflix.zuul.exception.ZuulException: Forwarding error
at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.handleException(SimpleHostRoutingFilter.java:257) ~[spring-cloud-netflix-zuul-2.1.1.RELEASE.jar!/:2.1.1.RELEASE]
at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.run(SimpleHostRoutingFilter.java:237) ~[spring-cloud-netflix-zuul-2.1.1.RELEASE.jar!/:2.1.1.RELEASE]
...
connection refuse ~
```
동적라우팅 적용 예정
## zuul filter
```java
@Override
public boolean shouldFilter() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
...
if (auth instanceof OAuth2Authentication) {
...
return true;
}
return false;
}
```
shouldFilter를 통해 인증을 먼저 체크
run() 메소드에서 민감한 uri, url 리다이렉트
## Keys Points of Sample
## 후기
서비스 디스커버리 (유레카)를 사용하지 않아서 조금의 차이가 있습니다... 서비스 디스커버리를 사용하게되면 개발 리소스(유지 및 보수) 가 추가되어서 빼고 Zuul에서 URL을 통해 라우트를 처리했습니다.
많은분들의 문서 및 레포지토리를 참고하여 만들었습니다.
## 새로운 작업
~~DDD 적용예정~~
전반적인 리팩토링
적용 예정 project [authorization server, test-service]
할 수 있는 부분은 자바8 사용 예정
## Ref
```
https://spring.io/guides/topicals/spring-security-architecture
https://github.com/kakawait/uaa-behind-zuul-sample
https://github.com/keets2012/microservice-integration
https://github.com/artemMartynenko/spring-cloud-gateway-oauth2-sso-sample-application
https://github.com/Baeldung/oauth-microservices
https://cheese10yun.github.io/oauth2
```

BIN
request_auth_network.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -1,4 +1 @@
rootProject.name = 'project-mark'
include 'eureka-server'
include 'contents'
include 'queue'
rootProject.name = 'project-mark'

View File

@@ -26,6 +26,9 @@ ext {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-oauth2'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
implementation 'com.google.code.gson:gson'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

View File

@@ -1,5 +1,6 @@
#Mon Jul 08 11:53:32 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

View File

@@ -1,18 +0,0 @@
//package io.bluemoon.testservice;
//
//import org.springframework.context.annotation.Configuration;
//import org.springframework.security.config.annotation.web.builders.HttpSecurity;
//import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
//import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
//import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
//
//@EnableResourceServer
//@Configuration
//public class ResourceServiceConfig extends ResourceServerConfigurerAdapter {
//
// @Override
// public void configure(HttpSecurity http) throws Exception {
// http.requestMatcher(new RequestHeaderRequestMatcher("Authorization"))
// .authorizeRequests().anyRequest().fullyAuthenticated();
// }
//}

View File

@@ -2,14 +2,16 @@ package io.bluemoon.testservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
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 org.springframework.web.bind.annotation.SessionAttributes;
import java.security.Principal;
@@ -17,20 +19,47 @@ import java.security.Principal;
@EnableResourceServer
public class TestServiceApplication {
public static void main(String[] args) {
SpringApplication.run(TestServiceApplication.class, args);
}
@Controller
@RequestMapping("/")
public static class TestController{
public static class TestController {
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public String helloMk2(Principal principal) {
System.out.println("-------------");
System.out.println(SecurityContextHolder.getContext().getAuthentication().getName());
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// UserDetails userDetails = (UserDetails) authentication.getPrincipal();
OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication;
OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) oAuth2Authentication.getDetails();
// System.out.println(userDetails.getUsername());
System.out.println(oAuth2AuthenticationDetails.getTokenValue());
System.out.println("-------------");
return principal == null ? "hello anonymous" : "heelo" + principal.getName();
}
@RequestMapping(method = RequestMethod.GET, value = "/tts")
@ResponseBody
public void helloMk3(Principal principal) {
System.out.println(SecurityContextHolder.getContext().getAuthentication().getName());
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
SecurityContextHolder.clearContext();
System.out.println(SecurityContextHolder.getContext().getAuthentication().getName());
SecurityContextHolder securityContextHolder = (SecurityContextHolder) SecurityContextHolder.createEmptyContext();
System.out.println(securityContextHolder);
}
@PreAuthorize("#oauth2.hasScope('read') and hasRole('ROLE_USER')")
@RequestMapping(value = "secret", method = RequestMethod.GET)
@ResponseBody
@@ -38,11 +67,6 @@ public class TestServiceApplication {
return principal == null ? "hello anonymous" : "heelo" + principal.getName();
}
@RequestMapping(method = RequestMethod.GET, value = "test")
@ResponseBody
public String test() {
return "test";
}
}
}

View File

@@ -1,12 +1,8 @@
server.port=8082
#security.oauth2.resource.jwt.key-value="abc"
#security.oauth2.resource.id=read
#security.oauth2.resource.service-id=${PREFIX:}resource
#security.oauth2.client.client-id=system1
#security.oauth2.client.client-secret=1234
#security.oauth2.resource.token-info-uri=http://127.0.0.1:8081/mk-auth/oauth/check_token
security.oauth2.resource.user-info-uri=http://127.0.0.1:8081/mk-auth/user
security.oauth2.resource.prefer-token-info=false
server.servlet.context-path=/test
#security.oauth2.resource.user-info-uri=http://oauth.keepgrow.world/uaa/user
security.oauth2.resource.user-info-uri=http://localhost:8765/uaa/user
security.oauth2.resource.token-info-uri=http://localhost:8081/uaa/oauth/check_token
security.oauth2.resource.prefer-token-info=false
logging.level.web=debug
spring.http.log-request-details=true

View File

@@ -0,0 +1,42 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<link crossorigin="anonymous" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" rel="stylesheet">
<head>
</head>
<body>
<div class="container">
<form method="post" role="form" th:action="@{/signInMiddleWare}">
<div class="form-group row">
<label class="col-sm-2 col-form-label" for="username">ID</label>
<div class="col-sm-10">
<input class="form-control" id="username" name="username" placeholder="id" type="text">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label" for="password">Password</label>
<div class="col-sm-10">
<input class="form-control" id="password" name="password" placeholder="password" type="password">
</div>
</div>
<input id="csrf_token" name="${_csrf.parameterName}" type="hidden" value="${_csrf.token}"/>
<button class="btn btn-primary" type="submit">Sign in</button>
</form>
</div>
</body>
<script crossorigin="anonymous"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script crossorigin="anonymous"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script crossorigin="anonymous"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
</html>

View File

@@ -0,0 +1,42 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<link crossorigin="anonymous" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" rel="stylesheet">
<head>
</head>
<body>
<div class="container">
<form method="post" role="form" th:action="@{/signUpMiddleWare}">
<div class="form-group row">
<label class="col-sm-2 col-form-label" for="username">ID</label>
<div class="col-sm-10">
<input class="form-control" id="username" name="username" placeholder="id" type="text">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label" for="password">Password</label>
<div class="col-sm-10">
<input class="form-control" id="password" name="password" placeholder="password" type="password">
</div>
</div>
<input id="csrf_token" name="${_csrf.parameterName}" type="hidden" value="${_csrf.token}"/>
<button class="btn btn-primary" type="submit">Sign in</button>
</form>
</div>
</body>
<script crossorigin="anonymous"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script crossorigin="anonymous"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script crossorigin="anonymous"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
</html>

View File

@@ -14,3 +14,7 @@ public class TestServiceApplicationTests {
}
}

BIN
zuul_flow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB