Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b69cc45ff0 | ||
|
|
f83a3ab606 | ||
|
|
6457af3084 | ||
|
|
bd4a0708cf | ||
|
|
593d4b66dd | ||
|
|
7758e1d961 | ||
|
|
bf1e576ffa | ||
|
|
c4d7977aa2 | ||
|
|
0253a6340d | ||
|
|
2ec6e54a0a | ||
|
|
8cb7884951 | ||
|
|
e0988ca3f5 | ||
|
|
7563884d03 | ||
|
|
6bdf42e640 | ||
|
|
0611b52851 | ||
|
|
5cb9aa8434 | ||
|
|
698ebcfb0d | ||
|
|
f3700246c0 | ||
|
|
93452cfeb1 | ||
|
|
d2dbddb9d2 | ||
|
|
1cad6d916b | ||
|
|
2b7f23b7e7 | ||
|
|
8db3469c6f | ||
|
|
7139f1b098 | ||
|
|
a793b89656 | ||
|
|
bbd02d44e7 | ||
|
|
98d4fef9a7 | ||
|
|
4aa8d9dcb3 | ||
|
|
690ae8b69e | ||
|
|
f8f19562ea | ||
|
|
238bf1d77c | ||
|
|
062269d122 | ||
|
|
4b56d5a3f3 | ||
|
|
9ff670ffcf | ||
|
|
8077c4715d | ||
|
|
a436507e59 | ||
|
|
c6d01e0653 | ||
|
|
2165b4c7c8 | ||
|
|
72097cc8ad | ||
|
|
80c51b5be9 | ||
|
|
2dc0144eb0 | ||
|
|
b4432151eb | ||
|
|
9a4bd7a13e | ||
|
|
6688e51adc | ||
|
|
4dbbb8bbd9 | ||
|
|
c1a5d7fb59 | ||
|
|
d7d166d989 | ||
|
|
935a50318b | ||
|
|
056f80c5ac | ||
|
|
64da987eca | ||
|
|
3bcd036b82 | ||
|
|
e6d04205e6 | ||
|
|
7eee47237e | ||
|
|
3ec25e9a79 | ||
|
|
41e2175468 | ||
|
|
2016155dd6 | ||
|
|
992796000b | ||
|
|
319ad587bb | ||
|
|
0fd06bc576 | ||
|
|
3671ae00cb | ||
|
|
939392c7f7 | ||
|
|
a83500bd4c |
BIN
after_login_network.png
Normal file
BIN
after_login_network.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
@@ -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'
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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 붙이기
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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:/";
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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")));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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).");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
// }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
52
authorization-server/src/main/resources/templates/login.html
Normal file
52
authorization-server/src/main/resources/templates/login.html
Normal 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>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,5 @@ public class HeaderEnhanceFilter implements Filter {
|
||||
// test if request url is permit all, then remove authorization from header
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
29
queue/.gitignore
vendored
@@ -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/
|
||||
@@ -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}"
|
||||
}
|
||||
}
|
||||
BIN
queue/gradle/wrapper/gradle-wrapper.jar
vendored
BIN
queue/gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
@@ -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
172
queue/gradlew
vendored
@@ -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
84
queue/gradlew.bat
vendored
@@ -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
|
||||
Binary file not shown.
@@ -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=jCz0WxtU08pWv2opnQfSE4qL8cBZunX6yQuO2zAF
|
||||
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
|
||||
Binary file not shown.
@@ -1,6 +0,0 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
rootProject.name = 'queue'
|
||||
@@ -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);
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -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
173
readme.md
Normal 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에 등록되어진 리소스 서비스에 접근하려고 할 때 인증 및 인가 네트워크
|
||||

|
||||
|
||||
로그인이 성공했을 때
|
||||

|
||||
|
||||
## SSO Login Flow
|
||||

|
||||
```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
BIN
request_auth_network.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
@@ -1,4 +1 @@
|
||||
rootProject.name = 'project-mark'
|
||||
include 'eureka-server'
|
||||
include 'contents'
|
||||
include 'queue'
|
||||
rootProject.name = 'project-mark'
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
// }
|
||||
//}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
42
test-service/src/main/resources/templates/signIn.html
Normal file
42
test-service/src/main/resources/templates/signIn.html
Normal 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>
|
||||
42
test-service/src/main/resources/templates/signUp.html
Normal file
42
test-service/src/main/resources/templates/signUp.html
Normal 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>
|
||||
@@ -14,3 +14,7 @@ public class TestServiceApplicationTests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
zuul_flow.png
Normal file
BIN
zuul_flow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
Reference in New Issue
Block a user