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

[BE] 전시현 로그인 #5

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
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
93 changes: 93 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,103 @@ repositories {
}

dependencies {
/*
}
// web + jpa
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

// queryDsl
implementation 'com.querydsl:querydsl-core:5.0.0'
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'

annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'

// security + thymeleaf + jwt + validation
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2'
runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.11.2') {
exclude group: 'org.json', module: 'json'
}

// develop tools
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'

// database
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'

// socket
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.webjars:sockjs-client:1.5.1'
implementation 'org.webjars:stomp-websocket:2.3.4'
implementation 'org.springframework:spring-messaging:6.0.3'
implementation 'org.springframework.security:spring-security-messaging:6.0.2'

// json 라이브러리 추가
implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.3'
implementation 'org.json:json:20210307'

// XML처리를 위한 JAXB 의존성 추가
implementation 'javax.xml.bind:jaxb-api:2.3.1'
implementation 'com.sun.xml.bind:jaxb-impl:2.3.1'
implementation 'com.sun.xml.bind:jaxb-core:2.3.0.1'

// test
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
*/

testImplementation 'junit:junit:4.13.1'
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
annotationProcessor('org.projectlombok:lombok')
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testAnnotationProcessor('org.projectlombok:lombok')

implementation 'org.springframework.boot:spring-boot-starter-validation'

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-mustache'
implementation 'org.springframework.boot:spring-boot-starter-web'

runtimeOnly 'com.h2database:h2'
//runtimeOnly "mysql:mysql-connector-java"
runtimeOnly 'com.mysql:mysql-connector-j'

implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.projectlombok:lombok:1.18.28'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'javax.xml.bind:jaxb-api:2.3.1'

// Spring Security
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-web'
implementation 'org.springframework.security:spring-security-config'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'org.springframework.boot:spring-boot-starter-json'
testImplementation 'org.springframework.security:spring-security-test'

// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

implementation 'jakarta.servlet:jakarta.servlet-api:5.0.0'



}

tasks.named('test') {
useJUnitPlatform()
jvmArgs '-Xshare:off'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package leets.attendance.config;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import leets.attendance.config.jwt.TokenProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@RequiredArgsConstructor
public class TokenAuthenticationFilter extends OncePerRequestFilter {
private final TokenProvider tokenProvider;

///private final static String HEADER_AUTHORIZATION = "Authorization";
private final static String HEADER_AUTHORIZATION = "Refresh-Token";
private final static String TOKEN_PREFIX = "Bearer ";

@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {

String authorizationHeader = request.getHeader(HEADER_AUTHORIZATION);
String token = getAccessToken(authorizationHeader);

if (tokenProvider.validToken(token)) {
Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}

filterChain.doFilter(request, response);
}

private String getAccessToken(String authorizationHeader) {
if (authorizationHeader != null && authorizationHeader.startsWith(TOKEN_PREFIX)) {
return authorizationHeader.substring(TOKEN_PREFIX.length());
}

return null;
}
}
119 changes: 119 additions & 0 deletions src/main/java/leets/attendance/config/WebSecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package leets.attendance.config;

import leets.attendance.config.jwt.JwtProperties;
import leets.attendance.service.UserDetailService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import static org.springframework.boot.autoconfigure.security.servlet.PathRequest.toH2Console;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import leets.attendance.config.jwt.TokenProvider;

@RequiredArgsConstructor
@Configuration
public class WebSecurityConfig {



private final UserDetailService userService;

//스프링 시큐리티 기능 비활성화
@Bean
public WebSecurityCustomizer configure(){
return (web)->web.ignoring()
.requestMatchers(toH2Console())
.requestMatchers("/static/**");
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
//CSRF
http.csrf(AbstractHttpConfigurer::disable);

//CORS
http.cors(Customizer.withDefaults());

//FormLogin, BasicHttp disable
http.formLogin(AbstractHttpConfigurer::disable);
http.httpBasic(AbstractHttpConfigurer::disable);
http.logout(AbstractHttpConfigurer::disable);

http.sessionManagement(sessionManagement ->
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

//권한 규칙
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/users/login", "/users/register", "/users/check-duplicate-id").permitAll()
.requestMatchers("/attendances", "/attendances/rates").authenticated() // "/users"에 대한 인증 필요
.anyRequest().authenticated());
JwtProperties jwtProperties = new JwtProperties();
http.addFilterBefore(new TokenAuthenticationFilter(new TokenProvider(jwtProperties)), UsernamePasswordAuthenticationFilter.class);
// 보안 필터 체인 반환
return http.build();
}

/*
//스프링 시큐리티 기능 비활성화
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
return http
.authorizeRequests()
.requestMatchers("/login", "/signup", "/user").permitAll() //특정 요청과 일치하는 url에 대해 누구나 접근 가능
.anyRequest() //위에서 설정한 url 이외의 요청에 대해
.authenticated() //별도의 인가는 필요하지 않지만 인증해 접근
.and()
.formLogin() //폼 기반 로그인 설정 :
.loginPage("/login") //로그인 페이지로 경로 설정
.defaultSuccessUrl("/articles") //로그인 완료됐을 때 이동할 경로
.and()
.logout() //로그아웃 설정 :
.logoutSuccessUrl("/login")// 로그아웃이 완료 되었을 떄 이동할 경로
.invalidateHttpSession(true) //로그아웃 이후 세션을 전체 삭제할지 여부
.and()
.csrf().disable() //csrf 설정 비활성화
.build();
}

*/

//인증 관리자 관련 설정
/*
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http,
BCryptPasswordEncoder bCryptPasswordEncoder, UserDetailService userDetailService)
throws Exception{
return http.getSharedObject(AuthenticationManagerBuilder.class) //사용자 서비스 설정
.userDetailsService(userService) // 사용자 정보를 가져올 서비스를 설정. 이때 설정하는 서비스 클래스는 반드시 UserDetailsService를 상속받은 클래스여야 함
.passwordEncoder(bCryptPasswordEncoder) // 비밀번호를 암호화하기 위한 인코더
.and()
.build();
}*/



//패스워드 인코더로 사용할 빈 등록

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}








}
38 changes: 38 additions & 0 deletions src/main/java/leets/attendance/config/jwt/AuthService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package leets.attendance.config.jwt;

import leets.attendance.domain.User;
import leets.attendance.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.Optional;

@RequiredArgsConstructor
@Service
public class AuthService implements UserDetailsService {

@Autowired
private final UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Optional<User> user = userRepository.findByEmail(email);
if (user == null) {
throw new UsernameNotFoundException("User not found with email: " + email);
}
return new org.springframework.security.core.userdetails.User(user.get().getEmail(), user.get().getPassword(), Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")));
}

public boolean authenticate(String email, String password) {
UserDetails userDetails = loadUserByUsername(email);
System.out.println(userDetails != null && new BCryptPasswordEncoder().matches(password, userDetails.getPassword()));
return userDetails != null && new BCryptPasswordEncoder().matches(password, userDetails.getPassword());
}
}
19 changes: 19 additions & 0 deletions src/main/java/leets/attendance/config/jwt/JwtProperties.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package leets.attendance.config.jwt;

import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Setter
@Getter
@Component
@ConfigurationProperties("jwt")
public class JwtProperties {

@Value("${jwt.issuer}")
private String issuer;
@Value("${jwt.secret_key}")
private String secretKey;
}
Loading