Skip to content
Open
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
48 changes: 24 additions & 24 deletions .github/workflows/create-jira-issue.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
name: Create Jira issue # 1
on: # 2
issues:
types: [opened]

jobs: # 3
create-issue: # 4
name: Create Jira issue # 5
runs-on: ubuntu-latest # 6
steps: # 7
- name: Login
uses: atlassian/gajira-login@v3 # 8
env: # 9
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}

- name: Create Issue
uses: atlassian/gajira-create@v3
with:
project: KAN
issuetype: Task
summary: '${{ github.event.issue.title }}'
description: '${{ github.event.issue.html_url }}'
#name: Create Jira issue # 1
#on: # 2
# issues:
# types: [opened]
#
#jobs: # 3
# create-issue: # 4
# name: Create Jira issue # 5
# runs-on: ubuntu-latest # 6
# steps: # 7
# - name: Login
# uses: atlassian/gajira-login@v3 # 8
# env: # 9
# JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
# JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
# JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
#
# - name: Create Issue
# uses: atlassian/gajira-create@v3
# with:
# project: KAN
# issuetype: Task
# summary: '${{ github.event.issue.title }}'
# description: '${{ github.event.issue.html_url }}'

# - name: Checkout develop code
# uses: actions/checkout@v4
Expand Down
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ build/
!**/src/main/**/build/
!**/**/build/
!**/src/test/**/build/
*/Api/build
*/Common/build
*/Search/build
**/build/

### STS ###
.apt_generated
Expand Down
2 changes: 2 additions & 0 deletions Api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation project(':Common')
implementation project(':Domain')
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'
runtimeOnly 'com.mysql:mysql-connector-j'
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
Expand Down
Binary file not shown.
Binary file not shown.
72 changes: 72 additions & 0 deletions Api/src/main/java/org/momo/security/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.momo.security;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.momo.security.filter.JwtFilter;
import org.momo.security.handler.JwtAccessDeniedHandler;
import org.momo.security.handler.JwtAuthenticationEntryPoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;

import java.util.Collections;

@Configuration
@EnableWebSecurity //스프링 시큐리티 필터(Security Config)가 스프링 필터 체인에 등록이 된다.
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception{
return configuration.getAuthenticationManager();
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http
.cors(cors -> cors.configurationSource(new CorsConfigurationSource() {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("*"));
configuration.setAllowedMethods(Collections.singletonList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Collections.singletonList("*"));
configuration.setMaxAge(3600L);
configuration.setExposedHeaders(Collections.singletonList("Authorization"));
return configuration;
}
}))
.csrf(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

.exceptionHandling((exception) -> exception
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.accessDeniedHandler(jwtAccessDeniedHandler))

.authorizeHttpRequests((request) -> request
.requestMatchers("/login").permitAll()
.anyRequest().authenticated())
.addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
67 changes: 67 additions & 0 deletions Api/src/main/java/org/momo/security/filter/JwtFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.momo.security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.momo.Member.Entity.Member;
import org.momo.common.BaseResponseDto;
import org.momo.common.status.ErrorStatus;
import org.momo.exception.handler.JwtExpiredHandler;
import org.momo.exception.handler.JwtInvalidHandler;
import org.momo.security.principal.PrincipalDetails;
import org.momo.util.JwtUtil;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.io.PrintWriter;

@RequiredArgsConstructor
@Slf4j
public class JwtFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String accessToken = request.getHeader("Authorization");
if (accessToken == null) {
filterChain.doFilter(request,response);
return;
}
try{
JwtUtil.validateAccessToken(accessToken);
String email = JwtUtil.getEmail(accessToken);

Member member = Member.builder()
.email(email)
.build();
PrincipalDetails principalDetails = PrincipalDetails.createPrincipalDetails(member);

Authentication authentication = new UsernamePasswordAuthenticationToken(principalDetails, null, principalDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}catch (JwtExpiredHandler e){
BaseResponseDto<Object> baseResponseDto = BaseResponseDto.onFailure(
ErrorStatus.JWT_ACCESS_TOKEN_EXPIRED.getCode(),
ErrorStatus.JWT_ACCESS_TOKEN_EXPIRED.getMessage(),
null
);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(response.getOutputStream(),baseResponseDto);
return;
}catch (JwtInvalidHandler e){
BaseResponseDto<Object> baseResponseDto = BaseResponseDto.onFailure(
ErrorStatus.JWT_TOKEN_INVALID.getCode(),
ErrorStatus.JWT_TOKEN_INVALID.getMessage(),
null
);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(response.getOutputStream(),baseResponseDto);
return;
}
filterChain.doFilter(request,response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.momo.security.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.momo.common.BaseResponseDto;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Slf4j
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
log.error("JwtAccessDeniedHandler 실행");
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
BaseResponseDto<Object> baseResponseDto = BaseResponseDto.onFailure(403, "권한이 없습니다.", null);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(response.getOutputStream(), baseResponseDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.momo.security.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.momo.common.BaseResponseDto;
import org.momo.common.status.ErrorStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Slf4j
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
log.error("JwtAuthenticationEntryPoint 실행");
response.setContentType("application/json");
BaseResponseDto<Object> baseResponseDto =
BaseResponseDto.onFailure(
ErrorStatus.JWT_TOKEN_NOT_FOUND.getCode(),
ErrorStatus.JWT_TOKEN_NOT_FOUND.getMessage(),
null);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(response.getOutputStream(), baseResponseDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.momo.security.principal;

import lombok.RequiredArgsConstructor;
import org.momo.Member.Entity.Member;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
@RequiredArgsConstructor
public class PrincipalDetails implements UserDetails {

private final Member member;

public static PrincipalDetails createPrincipalDetails(Member member) {
return new PrincipalDetails(member);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collect = new ArrayList<>();
collect.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return null;
}
});
return collect;
}

@Override
public String getPassword() {
return member.getPassword();
}

@Override
public String getUsername() {
return member.getName();
}

@Override
public boolean isAccountNonExpired() {
return true;
}

@Override
public boolean isAccountNonLocked() {
return true;
}

@Override
public boolean isCredentialsNonExpired() {
return true;
}

@Override
public boolean isEnabled() {
return true;
}
}
2 changes: 1 addition & 1 deletion Api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ spring:
hibernate:
format_sql: true
use_sql_comments: true
# show_sql: true
# show_sql: true
9 changes: 6 additions & 3 deletions Auth/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id 'java'
id 'java-library'
}

group = 'org.momo'
Expand All @@ -13,13 +14,15 @@ bootJar { enabled = false }
jar { enabled = true }

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
api project(':Common')
implementation project(':Domain')
//implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.springframework.security:spring-security-test'
//testImplementation 'org.springframework.security:spring-security-test'

}

test {
useJUnitPlatform()
}
}
Binary file not shown.
Binary file not shown.
22 changes: 22 additions & 0 deletions Auth/src/main/java/org/momo/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.momo.controller;

import lombok.RequiredArgsConstructor;
import org.momo.Base.Entity.Base;
import org.momo.common.BaseResponseDto;
import org.momo.common.status.SuccessStatus;
import org.momo.dto.AuthRequest;
import org.momo.dto.AuthResponse;
import org.momo.service.AuthService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;
@PostMapping("/login")
public BaseResponseDto<AuthResponse.LoginResponseDto> login(@RequestBody AuthRequest.LoginDto loginDto) {
return BaseResponseDto.of(SuccessStatus.LOGIN_SUCCESS.getCode(),SuccessStatus.LOGIN_SUCCESS.getMessage(), authService.login(loginDto));
}
}
Loading