Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
15088eb
1주차 스터디 내용
pci2676 Mar 25, 2019
d382117
requestParam 내용추가
pci2676 Mar 28, 2019
b0a8494
Swagger 설정
pci2676 Mar 29, 2019
ac2db83
JPA 예제 추가
pci2676 Mar 29, 2019
679d3f6
프로퍼티 수정
pci2676 Mar 29, 2019
3fde496
프로퍼티 변경
pci2676 Mar 29, 2019
4c96d99
영속성을 이용한 update
pci2676 Mar 29, 2019
fec407b
주석 추가
pci2676 Mar 29, 2019
b4a8c37
transactional 어노테이션 추가
pci2676 Mar 29, 2019
d0ef661
jpa 매핑 예제 추가
pci2676 Mar 29, 2019
09cbfd9
초기화
pci2676 Mar 30, 2019
cebe4c4
CRUD 예제작성
pci2676 Mar 30, 2019
768abea
스터디 자료 업데이트
pci2676 Mar 30, 2019
b1d410e
접근제한자 변경
pci2676 Mar 30, 2019
c59e669
요구사항 추가
pci2676 Mar 30, 2019
7fd7d01
요구사항 추가
pci2676 Mar 30, 2019
ae839fd
JPA 예제 추가
pci2676 Mar 31, 2019
93cc439
오류제거를 위한 주석처리
pci2676 Mar 31, 2019
5bc7a48
H2 내장 데이터 베이스 설정 추가
pci2676 Apr 2, 2019
3388e89
주석제거
pci2676 Apr 2, 2019
cd2e7ed
QueryDSL 설정 및 테스트 환경 설정
pci2676 Apr 2, 2019
eaf2d5f
ignore 재설정
pci2676 Apr 2, 2019
94b9e79
commit
pci2676 Apr 4, 2019
268792e
양방향 h2 테스트
pci2676 Apr 4, 2019
e07b261
querydsl을 이용한 n+1 로딩 문제 극복
pci2676 Apr 4, 2019
681cf30
aop 및 jwt 설정중
pci2676 Apr 8, 2019
5d49142
jwt auth aspect 작성
pci2676 Apr 8, 2019
dfdf11c
spring security를 이용한 비밀번호 암호화
pci2676 Apr 8, 2019
cf9ddea
스프링 시큐리티를 이용한 암호화
pci2676 Apr 8, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
##QueryDSL
generated

HELP.md
.gradle
/build/
Expand Down
83 changes: 73 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,27 +1,90 @@
plugins {
id 'org.springframework.boot' version '2.1.3.RELEASE'
id 'java'
buildscript {
ext {
springBootVersion = '2.1.3.RELEASE'
querydslPluginVersion = '1.0.10'
}
repositories {
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" } // plugin 저장소
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:${querydslPluginVersion}")
}
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'org.dailystudio'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'


repositories {
mavenCentral()
}


configurations {
compileOnly {
extendsFrom annotationProcessor
}
}

repositories {
mavenCentral()
}


dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'


compile("com.querydsl:querydsl-jpa") // querydsl
compile("com.querydsl:querydsl-apt") // querydsl

runtimeOnly('com.h2database:h2')//h2
compile("org.springframework.boot:spring-boot-devtools")

runtimeOnly 'mysql:mysql-connector-java'//mysql
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'//jpa

implementation('io.springfox:springfox-swagger2:+')//swagger
implementation('io.springfox:springfox-swagger-ui:+')

compile "org.projectlombok:lombok:+"//lombok
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation('com.auth0:java-jwt:3.3.0') //jwt
compile('org.springframework.boot:spring-boot-starter-aop') // aop
testImplementation 'org.springframework.boot:spring-boot-starter-test'

implementation('org.springframework.boot:spring-boot-starter-security') //security
testImplementation('org.springframework.security:spring-security-test')

testCompile('org.assertj:assertj-core:3.9.0')//test용
}

// querydsl 적용
apply plugin: "com.ewerk.gradle.plugins.querydsl" // Plugin 적용
def querydslSrcDir = 'src/main/generated' //QClass 생성 위치

querydsl {
library = "com.querydsl:querydsl-apt"
jpa = true
querydslSourcesDir = querydslSrcDir
}

sourceSets {
main {
java {
srcDirs 'src/main/java', querydslSrcDir
}
}
}

//JPA Annotation Processor Error를 잡아준다.
compileQuerydsl{
options.annotationProcessorPath = configurations.querydsl
}

configurations {
querydsl.extendsFrom compileClasspath
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy
public class SpringbootstudyApplication {

public static void main(String[] args) {
SpringApplication.run(SpringbootstudyApplication.class, args);
}
public static void main(String[] args) {
SpringApplication.run(SpringbootstudyApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.dailystudio.springbootstudy.auth.jwt;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JwtAuth {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.dailystudio.springbootstudy.auth.jwt;

import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.dailystudio.springbootstudy.util.LogAspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
@Aspect
@RequiredArgsConstructor
public class JwtAuthAspect {

private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
private final HttpServletRequest httpServletRequest;


@Around("@annotation(org.dailystudio.springbootstudy.auth.jwt.JwtAuth)")
public Object validateJWT(final ProceedingJoinPoint pjp) throws Throwable {
final String token = httpServletRequest.getHeader(JwtInfo.HEADER_NAME);
//토큰 존재 여부 확인
checkToken(token);
//토큰 검사
validateToken(token);
//토큰 해독
final DecodedJWT decodedJWT = JwtFactory.decode(token);
logger.info("Authorized Token Access - " + decodedJWT.getClaim(JwtInfo.USER_IDX).asString());
return pjp.proceed(pjp.getArgs());

}

private void checkToken(String token) {
if (token == null) {
logger.info("비어있는 토큰 접근.");
throw new RuntimeException("토큰없음");
}
}

private void validateToken(String token) {
if (!JwtFactory.isValid(token)) {
logger.info("유효하지 않은 토큰 접근.");
throw new RuntimeException("유효성 작살.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.dailystudio.springbootstudy.auth.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;

public class JwtFactory {

private static final LocalDateTime NOW = LocalDateTime.now();

public static String create(final Long idx) {
final Date expiredDate = Date.from(NOW.plusDays(JwtInfo.EXPIRES_LIMIT).toInstant(ZoneOffset.ofHours(9)));
return create(idx, expiredDate);
}

public static String create(final Long idx, final Date expiredDate) throws JWTCreationException {
return JWT.create()
.withClaim(JwtInfo.USER_IDX, idx.toString())
.withIssuer(JwtInfo.ISSUER)
.withExpiresAt(expiredDate)
.sign(JwtInfo.getAlgorithm());
}

public static Boolean isValid(final String token) {
try {
JWTVerifier jwtVerifier = JWT.require(JwtInfo.getAlgorithm()).build();
jwtVerifier.verify(token);

return true;
} catch (JWTVerificationException verificationException) {

return false;
}
}

public static DecodedJWT decode(final String token) {
try {
return JWT.decode(token);
} catch (JWTDecodeException decodeException) {
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.dailystudio.springbootstudy.auth.jwt;

import com.auth0.jwt.algorithms.Algorithm;

import java.io.UnsupportedEncodingException;

public class JwtInfo {

public static final String HEADER_NAME = "JWT";

public static final String ISSUER = "daily-studio";

public static final String TOKEN_KEY = "pci";

public static final Long EXPIRES_LIMIT = 100L;

public static final String USER_IDX = "idx";

public static final String USER_AUTHORITY = "authority";

public static Algorithm getAlgorithm() {
try {
return Algorithm.HMAC256(JwtInfo.TOKEN_KEY);
} catch (IllegalArgumentException | UnsupportedEncodingException e) {
return Algorithm.none();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.dailystudio.springbootstudy.config;

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Configuration
public class QueryDSLConfig {

@PersistenceContext
private EntityManager entityManager;

@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.dailystudio.springbootstudy.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/h2/*")
.permitAll()
.and()
.csrf().disable()
.headers()
.frameOptions()
.disable();
}

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.dailystudio.springbootstudy.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any()) // 현재 RequestMapping으로 할당된 모든 URL 리스트를 추출
.paths(PathSelectors.ant("/api/**")) // 그중 /api/** 인 URL들만 필터링
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.dailystudio.springbootstudy.controller;

import org.dailystudio.springbootstudy.auth.jwt.JwtFactory;
import org.dailystudio.springbootstudy.auth.jwt.JwtInfo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;

@RestController
@RequestMapping("api/login")
public class LoginController {

@PostMapping
public String createToken(HttpServletResponse res, Long idx) {
final String token = JwtFactory.create(idx);

res.addHeader(JwtInfo.HEADER_NAME, token);
res.setCharacterEncoding("UTF-8");
res.setContentType("application/json");

return token;
}
}
Loading