'logbook' 카테고리의 다른 글
| [log] 클라우드네이티브자바 저자(Josh Long)와 역자(정윤진, 오명운) 사인회 (0) | 2018.11.08 |
|---|---|
| [독후감] 클라우드 네이티브 자바 (2) | 2018.08.09 |
| [daily] Spring Camp 로고 초안 (0) | 2018.02.22 |
| [diary] 개발장비 업그레이드 (0) | 2018.02.01 |
| [소개] 협업도구 Microsoft Team Services (0) | 2017.11.08 |
| [log] 클라우드네이티브자바 저자(Josh Long)와 역자(정윤진, 오명운) 사인회 (0) | 2018.11.08 |
|---|---|
| [독후감] 클라우드 네이티브 자바 (2) | 2018.08.09 |
| [daily] Spring Camp 로고 초안 (0) | 2018.02.22 |
| [diary] 개발장비 업그레이드 (0) | 2018.02.01 |
| [소개] 협업도구 Microsoft Team Services (0) | 2017.11.08 |
새로운 프로젝트를 시작하면서 스프링 시큐리티 5.0을 적용하고 있다. 어떤 새로운 기능이 있는지 확인하기 위해서 참고문서(What’s New in Spring Security 5.0)를 살펴봤다. 새로운 기능이 추가되었는데 대략 다음과 같다.
새로운 기능
이 중에서 크게 관심을 끄는 항목은 '현대화된 비밀번호 인코딩' 항목이었다. 이전까지는 BcryptPasswordEncoder를 기본으로 단방향 암호화인코더로 사용해왔다.
;
위의 내용을 구글번역기로 돌려보면
대충 정리하면, 스프링 시큐리티에서 제공하는 PasswordEncoder는 사용자가 등록한 비밀번호를 단방향으로 변환하여 저장하는 용도로 사용된다. 그리고 시대적인 흐름에 따라서 점점 고도화된 암호화 알고리즘 구현체가 적용되어간다. 이런 과정에서 서비스에 저장된 비밀번호에 대한 암호화 알고리즘을 변경하는 일은 상당히 많은 노력을 요구하게 된다.
단방향의 변환된 암호를 풀어서 다시 암호화해야 하는데 그게 말처럼 쉬운 일은 아니다. |
그래서 스프링시큐리티에서 내놓은 해결책이 DelegatingPasswordEncoder 다.
사용방법은 간단하다.
}
PasswordEncoderFactories.createDelegatingPasswordEncoder()로 생성한 PasswordEncoder는 BCryptPasswordEncoder가 사용되며 앞에 {id}로 PasswordEncoder 유형이 정의된다. |
|
생성되는 암호화코드의 종류는 대략 다음과 같다.
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
{noop}password
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0{id}가 없는 비밀번호의 경우에는 다음과 같이 선언해서 확인작업이 가능하다.
public void 암호변환기 {;; // bcrypt;delegatingPasswordEncoder;;}
DelegatingPasswordEncoder를 이용하면 암호화 알고리즘 변경에 대한 걱정은 크게 하지 않아도 되겠다. 사용 전략에 대해서는 코드를 살펴보고 각자가 판가름하기 바란다.
테스트 코드는 다음과 같다.
기존에 저장되어 있는 암호화된 비밀번호를 DelegatingPasswordEncoder에서 사용할 수 있도록 이전하는 작업은 간단하다.
$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG를
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG처럼 앞에 {bcrypt}만 넣어주면 된다.
그 다음 단계에 대해서는 각자 고민해보자.
PasswordEncoder 자체가 단방향 암호화를 목적으로 생성되었다. 보안상의 원인으로 DB에 저장된 비밀번호가 유출되지 않는다면 변환된 비밀번호 앞에 {id}가 붙는다고 해서 크게 문제가 되지는 않는다고 생각한다… 유출되었을 때는 문제가 될 수도 있으려나?
최근 팀 커뮤니케이션으로 많은 사람들의 사랑을 받고 있는 슬랙(Slack).
슬랙에서는 외부에서 슬랙채널에 메시지를 보낼 수 있는 WebHook API를 제공하고 있다. 웹훅은 슬랙으로 데이터를 보내는 Incoming WebHook 과 특정조건에 부합되었을 때 외부의 데이터를 가져오는 Outgoing WebHook 이 있다.
웹애플리케이션에서 슬랙채널로 메시지를 보내는 것은 Incoming WebHook을 이용하게 된다.
그러기 위해서는 우선 팀슬랙에 Incomming WebHook을 설정한다.
NOTE |
|
작업을 진행하기에 앞서서 채널을 하나 개설한다. 그후 통합Integration 으로 이동하여 'incoming webhook' 을 검색하여 설치하고 채널을 지정한다. 필요하다면 아이콘을 변경하는 작업을 한다. 화면에 나오는 웹훅 URL 을 복사해둔다.
스프링부트 프로젝트를 생성한다.
build.gradlebuildscript {
ext {
springBootVersion = '1.3.5.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'spring-boot'
jar {
baseName = 'slack-incoming-webhook'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.projectlombok:lombok:1.16.8')
compile('org.springframework.boot:spring-boot-starter-web')
compile('com.google.guava:guava:19.0')
testCompile('org.springframework.boot:spring-boot-starter-test')
}WebConfigurationpackage io.honeymon.springboot.slack.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class WebConfiguration {
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}SlackNotifierpackage io.honeymon.springboot.slack.integration;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* Slack Notifier
*
* @author honeymon
*
*/
@Slf4j
@Component
public class SlackNotifier {
@Autowired
private RestTemplate restTemplate;
public enum SlackTarget {
// TODO webHookUrl 은 자신의 슬랙 IncomingWebHookAPI로 변경하세요.
CH_INCOMING("https://hooks.slack.com/services/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", "incoming");
String webHookUrl;
String channel;
SlackTarget(String webHookUrl, String channel) {
this.webHookUrl = webHookUrl;
this.channel = channel;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static class SlackMessageAttachement {
private String color;
private String pretext;
private String title;
private String title_link;
private String text;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static class SlackMessage {
private String text;
private String channel;
private List<SlackMessageAttachement> attachments;
void addAttachment(SlackMessageAttachement attachement) {
if (this.attachments == null) {
this.attachments = Lists.newArrayList();
}
this.attachments.add(attachement);
}
}
public boolean notify(SlackTarget target, SlackMessageAttachement message) {
log.debug("Notify[target: {}, message: {}]", target, message);
SlackMessage slackMessage = SlackMessage.builder().channel(target.channel)
.attachments(Lists.newArrayList(message)).build();
try {
restTemplate.postForEntity(target.webHookUrl, slackMessage, String.class);
return true;
} catch (Exception e) {
log.error("Occur Exception: {}", e);
return false;
}
}
}SlackSenderControllerpackage io.honeymon.springboot.slack.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import io.honeymon.springboot.slack.integration.SlackNotifier;
import io.honeymon.springboot.slack.integration.SlackNotifier.SlackMessageAttachement;
import io.honeymon.springboot.slack.integration.SlackNotifier.SlackTarget;
@RestController
public class SlackSendController {
@Autowired
private SlackNotifier slackNotifier;
@RequestMapping(value = "/", method = RequestMethod.POST)
public ResponseEntity<Boolean> send(@RequestBody SlackMessageAttachement message) { (1)
return ResponseEntity.ok(slackNotifier.notify(SlackTarget.CH_INCOMING, message));
}
}POST ` 방식으로 전송을 할 때 `@RequestBody 로 클래스를 정의하면 자동으로 매핑된다.
slack-incoming-webhook 실행$ git clone https://github.com/ihoneymon/slack-incoming-webhook
$ cd slack-incoming-webhook
$ ./gradlew springboot팀채널로 많이 사용하는 슬랙.
배포한 앱에서 중요한 사항(항상 상태를 체크해야하는 상황)에 대해서 슬랙 채널로 공지하도록 하는 기능을 간단하게 구현해봤다. @_@)> 생각보다 쉽다. 많이.
예제에서는 컨트롤러에서 요청을 받아서 처리하는 방식으로 구현했다.
| 부트 스프링 부트(Boot Spring Boot) 쓰기 시작 (2) | 2016.08.07 |
|---|---|
| [springboot] 스프링부트 개발가이드 작성 시작 (0) | 2016.07.21 |
| [springboot] 앱 프로세스ID(pid) 관리 (0) | 2016.04.07 |
| [스프링부트] 빌드시 깃 커밋버전 정보 포함시키기 (0) | 2016.02.26 |
| [스프링부트] 생성물 위치 (0) | 2016.02.24 |