OAuth2 액세스 토큰 전파

This commit is contained in:
assu10
2020-09-20 21:01:25 +09:00
parent 848d2aeac9
commit 2c4df64f7a
13 changed files with 172 additions and 24 deletions

View File

@@ -174,6 +174,15 @@ HOW TO RUN
-- 액세스 토큰으로 사용자 정보 조회
[GET] http://localhost:8901/auth/user
-- OAuth2 로 회원 서비스 보호 후 API 호출
[GET] http://localhost:8090/member/name/rinda
-- 권한 있는 사용자(assuAdmin) 의 액세스 토큰과 함께 PUT 메서드 API 호출
[PUT] http://localhost:8090/member/rinda
-- oauth2 전파 (이벤트 서비스에서 회원서비스 호출)
[GET] http://localhost:5555/api/evt/event/userInfo/rinda
```
---

View File

@@ -10,15 +10,15 @@ import org.springframework.stereotype.Component;
public class CustomContext {
public static final String CORRELATION_ID = "assu-correlation-id";
private String correlationId = new String();
private static final ThreadLocal<String> correlationId = new ThreadLocal<>();
// 그 외 필요한 항목 넣을 수 있음 (인증 토큰 등...)
public String getCorrelationId() {
return correlationId;
public static String getCorrelationId() {
return correlationId.get();
}
public void setCorrelationId(String correlationId) {
this.correlationId = correlationId;
public static void setCorrelationId(String cid) {
correlationId.set(cid);
}
}

View File

@@ -56,6 +56,11 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@@ -1,17 +1,55 @@
package com.assu.cloud.eventservice;
import com.assu.cloud.eventservice.utils.CustomContextInterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoRestTemplateFactory;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import java.util.Collections;
import java.util.List;
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients
@EnableResourceServer
public class EventServiceApplication {
//@LoadBalanced
@Bean
public OAuth2RestTemplate restTemplate(UserInfoRestTemplateFactory factory) {
List interceptors = factory.getUserInfoRestTemplate().getInterceptors();
if (interceptors == null) {
factory.getUserInfoRestTemplate().setInterceptors(Collections.singletonList(new CustomContextInterceptor()));
} else {
interceptors.add(new CustomContextInterceptor());
factory.getUserInfoRestTemplate().setInterceptors(interceptors);
}
return factory.getUserInfoRestTemplate();
}
/*@LoadBalanced // 스프링 클라우드가 리본이 지원하는 RestTemplate 클래스 생성하도록 지시
@Bean
public RestTemplate getRestTemplate() {
// return new RestTemplate();
RestTemplate template = new RestTemplate();
List interceptors = template.getInterceptors();
if (interceptors == null) {
template.setInterceptors(Collections.singletonList(new CustomContextInterceptor()));
} else {
interceptors.add(new CustomContextInterceptor());
template.setInterceptors(interceptors);
}
return template;
}*/
public static void main(String[] args) {
SpringApplication.run(EventServiceApplication.class, args);
}
}

View File

@@ -0,0 +1,40 @@
package com.assu.cloud.eventservice.client;
import com.assu.cloud.eventservice.config.CustomConfig;
import com.assu.cloud.eventservice.utils.CustomContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.stereotype.Component;
@Component
public class MemberRestTemplateClient {
private final OAuth2RestTemplate restTemplate;
private final CustomConfig customConfig;
public MemberRestTemplateClient(OAuth2RestTemplate restTemplate, CustomConfig customConfig) {
this.restTemplate = restTemplate;
this.customConfig = customConfig;
}
String URL_PREFIX = "/api/mb/member/"; // 회원 서비스의 주울 라우팅경로와 회원 클래스 주소
private static final Logger logger = LoggerFactory.getLogger(MemberRestTemplateClient.class);
public String userInfo(String name) {
logger.debug("===== In Member Service.userInfo: {}", CustomContext.getCorrelationId());
ResponseEntity<String> restExchange =
restTemplate.exchange(
//"http://" + customConfig.getServiceIdZuul() + URL_PREFIX + "userInfo/{name}", // http://localhost:5555/api/mb/member/userInfo/rinda
"http://localhost:5555/api/mb/member/userInfo/{name}", // http://localhost:5555/api/mb/member/userInfo/rinda
HttpMethod.GET,
null, String.class, name
);
return restExchange.getBody();
}
}

View File

@@ -10,7 +10,14 @@ public class CustomConfig {
@Value("${your.name}")
private String yourName;
@Value("${service.id.zuul}")
private String serviceIdZuul;
public String getYourName() {
return yourName;
}
public String getServiceIdZuul() {
return serviceIdZuul;
}
}

View File

@@ -1,5 +1,6 @@
package com.assu.cloud.eventservice.controller;
import com.assu.cloud.eventservice.client.MemberRestTemplateClient;
import com.assu.cloud.eventservice.client.MemberFeignClient;
import com.assu.cloud.eventservice.config.CustomConfig;
import org.springframework.web.bind.annotation.GetMapping;
@@ -13,10 +14,12 @@ public class EventController {
private final CustomConfig customConfig;
private final MemberFeignClient memberFeignClient;
private final MemberRestTemplateClient memberRestTemplateClient;
public EventController(CustomConfig customConfig, MemberFeignClient memberFeignClient) {
public EventController(CustomConfig customConfig, MemberFeignClient memberFeignClient, MemberRestTemplateClient memberRestTemplateClient) {
this.customConfig = customConfig;
this.memberFeignClient = memberFeignClient;
this.memberRestTemplateClient = memberRestTemplateClient;
}
@GetMapping(value = "name/{nick}")
@@ -45,4 +48,9 @@ public class EventController {
public String gift(@PathVariable("name") String gift) {
return "[EVENT] Gift is " + gift;
}
@GetMapping("userInfo/{name}")
public String userInfo(@PathVariable("name") String name) {
return "[EVENT-MEMBER] " + memberRestTemplateClient.userInfo(name);
}
}

View File

@@ -0,0 +1,33 @@
package com.assu.cloud.eventservice.security;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
/**
* 접근 제어 규칙 정의
* 인증된 사용자는 모든 서비스에 접근 가능하거나,
* 특정 역할을 가진 애플리케이션만 PUT URL 로 접근하는 등 세밀하기 정의 가능
*/
@Configuration
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
/**
* 모든 접근 규칙을 재정의한 configure()
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
// 매서드로 전달된 HttpSecurity 객체로 모든 접근 규칙 구성
// 회원 서비스의 모든 URL 에 대해 인증된 사용자만 접근하도록 제한
//http.authorizeRequests().anyRequest().authenticated();
http.authorizeRequests()
.antMatchers(HttpMethod.PUT, "/member/**") // 쉼표로 구분하여 엔드 포인트 목록 받음
.hasRole("ADMIN") // ADMIN 권한을 가진 사용자만 PUT 호출 가능
.anyRequest() // 서비스의 모든 엔드포인트도 인증된 사용자만 접근 가능하도록 설정
.authenticated();
}
}

View File

@@ -10,15 +10,15 @@ import org.springframework.stereotype.Component;
public class CustomContext {
public static final String CORRELATION_ID = "assu-correlation-id";
private String correlationId = new String();
private static final ThreadLocal<String> correlationId = new ThreadLocal<>();
// 그 외 필요한 항목 넣을 수 있음 (인증 토큰 등...)
public String getCorrelationId() {
return correlationId;
public static String getCorrelationId() {
return correlationId.get();
}
public void setCorrelationId(String correlationId) {
this.correlationId = correlationId;
public static void setCorrelationId(String cid) {
correlationId.set(cid);
}
}

View File

@@ -9,8 +9,8 @@ import org.springframework.web.client.RestTemplate;
@Component
public class EventRestTemplateClient {
RestTemplate restTemplate;
CustomConfig customConfig;
private final RestTemplate restTemplate;
private final CustomConfig customConfig;
public EventRestTemplateClient(RestTemplate restTemplate, CustomConfig customConfig) {
this.restTemplate = restTemplate;

View File

@@ -40,4 +40,12 @@ public class MemberController {
public String member(@PathVariable("name") String name) {
return "[MEMBER-DELETE] " + name + " is deleted.";
}
/**
* 이벤트 서비스에서 OAuth2 로 호출 테스트
*/
@GetMapping("userInfo/{name}")
public String userInfo(@PathVariable("name") String name) {
return "[MEMBER] " + name;
}
}

View File

@@ -10,15 +10,15 @@ import org.springframework.stereotype.Component;
public class CustomContext {
public static final String CORRELATION_ID = "assu-correlation-id";
private String correlationId = new String();
private static final ThreadLocal<String> correlationId = new ThreadLocal<>();
// 그 외 필요한 항목 넣을 수 있음 (인증 토큰 등...)
public String getCorrelationId() {
return correlationId;
public static String getCorrelationId() {
return correlationId.get();
}
public void setCorrelationId(String correlationId) {
this.correlationId = correlationId;
public static void setCorrelationId(String cid) {
correlationId.set(cid);
}
}

View File

@@ -10,15 +10,15 @@ import org.springframework.stereotype.Component;
public class CustomContext {
public static final String CORRELATION_ID = "assu-correlation-id";
private String correlationId = new String();
private static final ThreadLocal<String> correlationId = new ThreadLocal<>();
// 그 외 필요한 항목 넣을 수 있음 (인증 토큰 등...)
public String getCorrelationId() {
return correlationId;
public static String getCorrelationId() {
return correlationId.get();
}
public void setCorrelationId(String correlationId) {
this.correlationId = correlationId;
public static void setCorrelationId(String cid) {
correlationId.set(cid);
}
}