Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/security #13

Merged
merged 5 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 7 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ configurations {

repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}

dependencies {
Expand All @@ -28,7 +29,9 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'com.github.leehj050211:bsm-oauth-java:1.0.3'

testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand All @@ -38,8 +41,11 @@ dependencies {
developmentOnly 'org.springframework.boot:spring-boot-devtools'

compileOnly 'org.projectlombok:lombok'
compileOnly 'io.jsonwebtoken:jjwt-api:0.11.5'

runtimeOnly 'com.mysql:mysql-connector-j'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/insert/ogbsm/OgBsmApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableJpaAuditing
@ConfigurationPropertiesScan
@EnableJpaRepositories
@SpringBootApplication
public class OgBsmApplication {

Expand Down
24 changes: 24 additions & 0 deletions src/main/java/com/insert/ogbsm/domain/auth/AuthId.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.insert.ogbsm.domain.auth;

import lombok.*;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.TimeToLive;
import org.springframework.data.redis.core.index.Indexed;

import javax.persistence.Id;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@RedisHash
public class AuthId {
@Id
String id;

@Indexed
String authId;

@TimeToLive
private long ttl;
}
34 changes: 34 additions & 0 deletions src/main/java/com/insert/ogbsm/domain/auth/RefreshToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.insert.ogbsm.domain.auth;

import lombok.*;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.TimeToLive;
import org.springframework.data.redis.core.index.Indexed;

import javax.persistence.Id;
import java.time.ZonedDateTime;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@RedisHash
public class RefreshToken {
@Id
private String id;
@Indexed
private String refreshToken;

private String role;

@TimeToLive
private long ttl;

private ZonedDateTime expiredAt;

public RefreshToken update(final String refreshToken, final long ttl) {
this.refreshToken = refreshToken;
this.ttl = ttl;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.insert.ogbsm.domain.auth.exception;

import com.insert.ogbsm.domain.user.exception.UserNotFoundException;
import com.insert.ogbsm.global.error.exception.BsmException;
import com.insert.ogbsm.global.error.exception.ErrorCode;

public class InvalidClientException extends BsmException {
public static final UserNotFoundException EXCEPTION = new UserNotFoundException(ErrorCode.BSM_AUTH_INVALID_CLIENT);

public InvalidClientException(ErrorCode errorCode) {
super(errorCode);
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/insert/ogbsm/domain/auth/repo/AuthIdRepo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.insert.ogbsm.domain.auth.repo;

import com.insert.ogbsm.domain.auth.AuthId;
import org.springframework.data.repository.CrudRepository;

import java.util.Optional;

public interface AuthIdRepo extends CrudRepository<AuthId, String> {
Optional<AuthId> findByAuthId(String authId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.insert.ogbsm.domain.auth.repo;

import com.insert.ogbsm.domain.auth.RefreshToken;
import org.springframework.data.repository.CrudRepository;

import java.util.Optional;

public interface RefreshTokenRepo extends CrudRepository<RefreshToken, String> {
Optional<RefreshToken> findById(String authId);
Optional<RefreshToken> findByRefreshToken(String refreshToken);
}
61 changes: 38 additions & 23 deletions src/main/java/com/insert/ogbsm/domain/user/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,26 @@

import com.insert.ogbsm.domain.user.authority.Authority;
import com.insert.ogbsm.domain.user.role.Role;
import jakarta.persistence.*;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import leehj050211.bsmOauth.dto.resource.BsmStudent;
import leehj050211.bsmOauth.dto.resource.BsmTeacher;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;


@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {

Expand All @@ -20,42 +31,46 @@ public class User {
@Column(unique = true, nullable = false)
private String nickname;

@Column(unique = true,nullable = false)
@Column(unique = true, nullable = false)
private String email;

@Column()
private String name;

@Column()
private String profile_image;

@Enumerated(EnumType.STRING)
private Authority authority;

@Enumerated(EnumType.STRING)
private Role role;

@Min(2021)
private Long enroll;

@Column()
@Min(1)
@Max(3)
private Long grade;
private Short grade;

@Column()
@Min(1)
@Max(4)
private Long class_number;
private Short class_number;

@Column()
@Min(1)
@Max(16)
private Long student_number;

@Column()
private String profile_image;

@Enumerated(EnumType.STRING)
@Column()
private Authority authority;

@Enumerated(EnumType.STRING)
@Column()
private Role role;


private Short student_number;

public void setTeacherValue(BsmTeacher teacher) {
this.name = teacher.getName();
this.role = Role.TEACHER;
}

public void setStudentValue(BsmStudent student) {
this.name = student.getName();
this.role = Role.STUDENT;
this.enroll = student.getEnrolledAt().longValue();
this.grade = student.getGrade().shortValue();
this.class_number = student.getClassNo().shortValue();
this.student_number = student.getStudentNo().shortValue();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.insert.ogbsm.domain.user.exception;

import com.insert.ogbsm.global.error.exception.BsmException;
import com.insert.ogbsm.global.error.exception.ErrorCode;

public class UserNotFoundException extends BsmException {

public static final UserNotFoundException EXCEPTION = new UserNotFoundException(ErrorCode.USER_NOT_FOUND);

public UserNotFoundException(ErrorCode errorCode) {
super(errorCode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.insert.ogbsm.domain.user.exception;

import com.insert.ogbsm.global.error.exception.BsmException;
import com.insert.ogbsm.global.error.exception.ErrorCode;

public class UserNotLoginException extends BsmException {

public static final UserNotFoundException EXCEPTION = new UserNotFoundException(ErrorCode.USER_NOT_LOGIN);

public UserNotLoginException(ErrorCode errorCode) {
super(errorCode);
}
}
6 changes: 4 additions & 2 deletions src/main/java/com/insert/ogbsm/domain/user/repo/UserRepo.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import com.insert.ogbsm.domain.user.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

public interface UserRepo extends JpaRepository<User, Long> {

import java.util.Optional;

public interface UserRepo extends JpaRepository<User, Long> {
Optional<User> findByEmail(String id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.insert.ogbsm.global.error;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.insert.ogbsm.global.error.exception.ErrorCode;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import java.io.IOException;

@RequiredArgsConstructor
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

private final ObjectMapper objectMapper;

@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
ErrorCode errorCode = ErrorCode.FORBIDDEN;
String errorResponseJson = objectMapper.writeValueAsString(
new ErrorResponse(errorCode.getStatus(), errorCode.getCode(), errorCode.getMessage()));

response.setStatus(errorCode.getStatus());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write(errorResponseJson);
}
}
23 changes: 23 additions & 0 deletions src/main/java/com/insert/ogbsm/global/error/ErrorResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.insert.ogbsm.global.error;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class ErrorResponse {
private int status;
private String code;
private String message;

public String toString() {
ObjectMapper objectMapper = new ObjectMapper();
try {
return objectMapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
42 changes: 42 additions & 0 deletions src/main/java/com/insert/ogbsm/global/error/ExceptionFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.insert.ogbsm.global.error;

import com.insert.ogbsm.global.error.exception.BsmException;
import com.insert.ogbsm.global.error.exception.ErrorCode;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

public class ExceptionFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException {
try {
filterChain.doFilter(request, response);
} catch (BsmException e) {
writeErrorCode(response, e.getErrorCode());
} catch (ExpiredJwtException e) {
writeErrorCode(response, ErrorCode.EXPIRED_JWT);
} catch (JwtException e) {
writeErrorCode(response, ErrorCode.INVALID_TOKEN);
} catch (Exception e) {
e.printStackTrace();
writeErrorCode(response, ErrorCode.INTERNAL_SERVER_ERROR);
}
}

private void writeErrorCode(HttpServletResponse response, ErrorCode errorCode) throws IOException {
ErrorResponse errorResponse = new ErrorResponse(
errorCode.getStatus(), errorCode.getCode(), errorCode.getMessage()
);

response.setStatus(errorResponse.getStatus());
response.setCharacterEncoding("UTF-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write(errorResponse.toString());
}
}
Loading