Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
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
Binary file added JWT + AOP.pptx
Binary file not shown.
79 changes: 69 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,27 +1,86 @@
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'

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,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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.dailystudio.springbootstudy.controller;

import lombok.RequiredArgsConstructor;
import org.dailystudio.springbootstudy.auth.jwt.JwtAuth;
import org.dailystudio.springbootstudy.auth.jwt.JwtInfo;
import org.dailystudio.springbootstudy.repository.StoreRepository;
import org.dailystudio.springbootstudy.repository.querydsl.StoreRepositorySupport;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("api/test")
public class StoreController {

private final StoreRepository storeRepository;
private final StoreRepositorySupport storeRepositorySupport;

@GetMapping("/get")
@JwtAuth
public void getMapping(@RequestHeader(JwtInfo.HEADER_NAME) String token) {

System.out.println(token);
}

@PostMapping("/post")
public void postMapping(int i) {

}

}
23 changes: 23 additions & 0 deletions src/main/java/org/dailystudio/springbootstudy/domain/Clerk.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.dailystudio.springbootstudy.domain;

import lombok.Getter;

import javax.persistence.*;
import javax.validation.constraints.NotNull;

@Entity
@Table(name = "clerk")
@Getter
public class Clerk {

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "clerk_id")
private Long id;

@NotNull
private String name;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "store_id")
private Store store;
}
Loading