diff --git a/cookie/chapter4/build.gradle b/cookie/chapter4/build.gradle index 3e4ca22..820e688 100644 --- a/cookie/chapter4/build.gradle +++ b/cookie/chapter4/build.gradle @@ -44,6 +44,15 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.13' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.13' + // Security + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' + + // Jwt + implementation 'io.jsonwebtoken:jjwt-api:0.12.3' + implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' + implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' + implementation 'org.springframework.boot:spring-boot-configuration-processor' } tasks.named('test') { diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/controller/MemberController.java b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/controller/MemberController.java index b558def..ca054cb 100644 --- a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/controller/MemberController.java +++ b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/controller/MemberController.java @@ -4,7 +4,9 @@ import com.example.chapter4.domain.member.dto.MemberResDTO; import com.example.chapter4.domain.member.exception.code.MemberSuccessCode; import com.example.chapter4.domain.member.service.command.MemberCommandService; +import com.example.chapter4.domain.member.service.query.MemberQueryService; import com.example.chapter4.global.apiPayload.ApiResponse; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -13,9 +15,10 @@ @RestController @RequiredArgsConstructor public class MemberController { - + private MemberQueryService memberQueryService; private final MemberCommandService memberCommandService; + // 회원가입 @PostMapping("/sign-up") public ApiResponse signUp( @@ -23,4 +26,13 @@ public ApiResponse signUp( ){ return ApiResponse.onSuccess(MemberSuccessCode.FOUND, memberCommandService.signup(dto)); } -} \ No newline at end of file + + // 로그인 + @PostMapping("/login") + public ApiResponse login( + @RequestBody @Valid MemberReqDTO.LoginDTO dto + ){ + return ApiResponse.onSuccess(MemberSuccessCode.FOUND, memberQueryService.login(dto)); + } +} + diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/converter/MemberConverter.java b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/converter/MemberConverter.java index 3c9b5ff..3ef172c 100644 --- a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/converter/MemberConverter.java +++ b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/converter/MemberConverter.java @@ -3,10 +3,14 @@ import com.example.chapter4.domain.member.entity.Member; import com.example.chapter4.domain.member.dto.MemberReqDTO; import com.example.chapter4.domain.member.dto.MemberResDTO; +import com.example.chapter4.global.auth.enums.Role; public class MemberConverter { + private static String password; + private static Role role; + // Entity -> DTO public static MemberResDTO.JoinDTO toJoinDTO( Member member @@ -19,10 +23,14 @@ public static MemberResDTO.JoinDTO toJoinDTO( // DTO -> Entity public static Member toMember( - MemberReqDTO.JoinDTO dto - ){ + MemberReqDTO.JoinDTO dto, + String salt, Role roleUser){ + return Member.builder() .name(dto.name()) + .email(dto.email()) + .password(password) + .role(role) .birth(dto.birth()) .address(dto.address()) .address(dto.specAddress()) diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/dto/MemberReqDTO.java b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/dto/MemberReqDTO.java index b7e646a..1a9d749 100644 --- a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/dto/MemberReqDTO.java +++ b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/dto/MemberReqDTO.java @@ -1,21 +1,26 @@ package com.example.chapter4.domain.member.dto; -import lombok.Builder; -import lombok.Getter; +import jakarta.validation.constraints.NotBlank; +import lombok.*; import java.time.LocalDate; import java.util.List; +import java.util.Optional; @Getter public class MemberReqDTO { + @Builder public static class JoinDTO { + private final String name; private final LocalDate birth; // LocalDate 또는 String 등 타입 맞춰서 private final String address; private final String specAddress; // 상세주소 등 private final String gender; private final List preferredFoodIds; + private final String email; + public final String password; // Getter 또는 record 문법 등 사용 가능 public String name() { return name; } @@ -24,5 +29,28 @@ public static class JoinDTO { public String specAddress() { return specAddress; } public String gender() { return gender; } public List preferredFoodIds() { return preferredFoodIds; } + + public String email() { + return email; + } + + public CharSequence password() { + return password; + } + + // 로그인 + + } + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class LoginDTO { + private String username; + private String password; + + public Optional email() { + return Optional.empty(); + } } } diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/dto/MemberResDTO.java b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/dto/MemberResDTO.java index d6daab5..f3d872d 100644 --- a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/dto/MemberResDTO.java +++ b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/dto/MemberResDTO.java @@ -13,4 +13,9 @@ public static class JoinDTO { public Long getMemberId() { return memberId; } public String getCreateAt() { return createAt; } } + @Builder + public record LoginDTO( + Long memberId, + String accessToken + ){} } diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/entity/Member.java b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/entity/Member.java index 9e55448..c2b3927 100644 --- a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/entity/Member.java +++ b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/entity/Member.java @@ -1,6 +1,7 @@ package com.example.chapter4.domain.member.entity; import com.example.chapter4.domain.Alarm.entity.Alarm; +import com.example.chapter4.global.auth.enums.Role; import com.example.chapter4.global.entity.BaseEntity; import com.example.chapter4.domain.mission.entity.ReceivedMission; import com.example.chapter4.domain.review.entity.Review; @@ -19,14 +20,20 @@ @AllArgsConstructor // Lombok: 모든 필드를 인자로 받는 생성자를 생성합니다. public class Member extends BaseEntity { + @Column(nullable = false, unique = true) + private String email; + + @Column(nullable = false) + private String password; + + @Enumerated(EnumType.STRING) + private Role role; + @Id // 이 필드가 테이블의 기본 키(PK)임을 나타냅니다. @GeneratedValue(strategy = GenerationType.IDENTITY) // 기본 키 생성을 데이터베이스에 위임합니다. (e.g., MySQL의 AUTO_INCREMENT) private Long id; - @Column(nullable = false, length = 20) // 데이터베이스 컬럼에 매핑됩니다. nullable=false는 NOT NULL 제약조건을 의미합니다. - private String email; - @Column(length = 10) private String provider; // 소셜 로그인 제공자 (e.g., "kakao", "google") diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/repository/MemberRepository.java b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/repository/MemberRepository.java index eaaec12..f5810c5 100644 --- a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/repository/MemberRepository.java +++ b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/repository/MemberRepository.java @@ -8,6 +8,7 @@ import org.springframework.data.repository.query.Param; import java.util.List; +import java.util.Optional; public interface MemberRepository extends JpaRepository { @@ -21,4 +22,6 @@ public interface MemberRepository extends JpaRepository { @Query("SELECT r FROM Review r WHERE r.member.id = :memberId AND r.status = 'ACTIVE' ORDER BY r.createdAt DESC") List findReviewsByMemberId(@Param("memberId") Long memberId); + + Optional findByEmail(String email); } diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/service/command/MemberCommandServiceImpl.java b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/service/command/MemberCommandServiceImpl.java index 6cd645e..4db62e7 100644 --- a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/service/command/MemberCommandServiceImpl.java +++ b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/service/command/MemberCommandServiceImpl.java @@ -8,7 +8,9 @@ import com.example.chapter4.domain.member.repository.FoodRepository; import com.example.chapter4.domain.member.repository.MemberRepository; import com.example.chapter4.domain.member.converter.MemberConverter; +import com.example.chapter4.global.auth.enums.Role; import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -21,15 +23,24 @@ public class MemberCommandServiceImpl implements MemberCommandService { private final MemberRepository memberRepository; private final FoodRepository foodRepository; + private final PasswordEncoder passwordEncoder; @Override @Transactional - public MemberResDTO.JoinDTO signup(MemberReqDTO.JoinDTO dto) { + public MemberResDTO.JoinDTO signup( + MemberReqDTO.JoinDTO dto + ){ + // 솔트된 비밀번호 생성 + String salt = passwordEncoder.encode(dto.password()); + + // 사용자 생성: 유저 / 관리자는 따로 API 만들어서 관리 + Member member = MemberConverter.toMember(dto, salt, Role.ROLE_USER); + // 음식 엔티티 목록 조회 List foods = foodRepository.findAllById(dto.preferredFoodIds()); // DTO를 Member 엔티티로 변환 - Member member = MemberConverter.toMember(dto); + member = MemberConverter.toMember(dto, salt, Role.ROLE_USER); // 선호 Preference 리스트 생성 List preferenceList = new ArrayList<>(); diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/service/query/MemberQueryService.java b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/service/query/MemberQueryService.java new file mode 100644 index 0000000..ccc12fd --- /dev/null +++ b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/service/query/MemberQueryService.java @@ -0,0 +1,12 @@ +package com.example.chapter4.domain.member.service.query; + +import com.example.chapter4.domain.member.dto.MemberReqDTO; +import com.example.chapter4.domain.member.dto.MemberResDTO; +import jakarta.validation.Valid; + +public class MemberQueryService { + + public MemberResDTO.LoginDTO login(MemberReqDTO.@Valid LoginDTO dto) { + return null; + } +} diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/service/query/MemberQueryServiceImpl.java b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/service/query/MemberQueryServiceImpl.java new file mode 100644 index 0000000..2266a23 --- /dev/null +++ b/cookie/chapter4/src/main/java/com/example/chapter4/domain/member/service/query/MemberQueryServiceImpl.java @@ -0,0 +1,47 @@ +package com.example.chapter4.domain.member.service.query; + +import com.example.chapter4.domain.member.converter.MemberConverter; +import com.example.chapter4.domain.member.dto.MemberReqDTO; +import com.example.chapter4.domain.member.dto.MemberResDTO; +import com.example.chapter4.domain.member.entity.Member; +import com.example.chapter4.domain.member.exception.code.MemberErrorCode; +import com.example.chapter4.domain.member.repository.MemberRepository; +import com.example.chapter4.global.auth.jwt.CustomUserDetails; +import com.example.chapter4.global.auth.jwt.JwtUtil; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class MemberQueryServiceImpl implements MemberQueryService{ + + private final MemberRepository memberRepository; + private final JwtUtil jwtUtil; + private final PasswordEncoder encoder; + + @Override + public MemberResDTO.LoginDTO login( + MemberReqDTO.@Valid LoginDTO dto + ) { + + // Member 조회 + Member member = memberRepository.findByEmail((dto.email()) + .orElseThrow(() -> new MemberException(MemberErrorCode.NOT_FOUND)); + + // 비밀번호 검증 + if (!encoder.matches(dto.password(), member.getPassword())){ + throw new MemberException(MemberErrorCode.INVALID); + } + + // JWT 토큰 발급용 UserDetails + CustomUserDetails userDetails = new CustomUserDetails(member); + + // 엑세스 토큰 발급 + String accessToken = jwtUtil.createAccessToken(userDetails); + + // DTO 조립 + return MemberConverter.toLoginDTO(member, accessToken); + } +} \ No newline at end of file diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/domain/review/controller/ReviewController.java b/cookie/chapter4/src/main/java/com/example/chapter4/domain/review/controller/ReviewController.java index 75a8a46..6f9cc11 100644 --- a/cookie/chapter4/src/main/java/com/example/chapter4/domain/review/controller/ReviewController.java +++ b/cookie/chapter4/src/main/java/com/example/chapter4/domain/review/controller/ReviewController.java @@ -1,5 +1,6 @@ package com.example.chapter4.domain.review.controller; +import com.example.chapter4.annotation.ValidPage; import com.example.chapter4.domain.review.dto.MyReviewDto; import com.example.chapter4.domain.review.dto.ReviewReqDTO; import com.example.chapter4.domain.review.dto.ReviewResDTO; diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/enums/Role.java b/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/enums/Role.java new file mode 100644 index 0000000..87d20d8 --- /dev/null +++ b/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/enums/Role.java @@ -0,0 +1,5 @@ +package com.example.chapter4.global.auth.enums; + +public enum Role { + ROLE_ADMIN, ROLE_USER +} \ No newline at end of file diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/jwt/CustomUserDetails.java b/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/jwt/CustomUserDetails.java new file mode 100644 index 0000000..62ea3c2 --- /dev/null +++ b/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/jwt/CustomUserDetails.java @@ -0,0 +1,19 @@ +package com.example.chapter4.global.auth.jwt; + +import com.example.chapter4.domain.member.entity.Member; + +import java.util.Collection; + +public class CustomUserDetails { + public CustomUserDetails(Member member) { + + } + + public String getUsername() { + return null; + } + + public Collection getAuthorities() { + return null; + } +} diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/jwt/CustomUserDetailsService.java b/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/jwt/CustomUserDetailsService.java new file mode 100644 index 0000000..6cb1c7d --- /dev/null +++ b/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/jwt/CustomUserDetailsService.java @@ -0,0 +1,9 @@ +package com.example.chapter4.global.auth.jwt; + +import org.springframework.security.core.userdetails.UserDetails; + +public class CustomUserDetailsService { + public UserDetails loadUserByUsername(String email) { + return null; + } +} diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/jwt/JwtAuthFilter.java b/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/jwt/JwtAuthFilter.java new file mode 100644 index 0000000..a375ee9 --- /dev/null +++ b/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/jwt/JwtAuthFilter.java @@ -0,0 +1,71 @@ +package com.example.chapter4.global.auth.jwt; + +import com.example.chapter4.global.apiPayload.ApiResponse; +import com.example.chapter4.global.apiPayload.code.GeneralErrorCode; +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.NonNull; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@RequiredArgsConstructor +public class JwtAuthFilter extends OncePerRequestFilter { + + private final JwtUtil jwtUtil; + private final CustomUserDetailsService customUserDetailsService; + + @Override + protected void doFilterInternal( + @NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain + ) throws ServletException, IOException { + + try { + // 토큰 가져오기 + String token = request.getHeader("Authorization"); + // token이 없거나 Bearer가 아니면 넘기기 + if (token == null || !token.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + } + // Bearer이면 추출 + token = token.replace("Bearer ", ""); + // AccessToken 검증하기: 올바른 토큰이면 + if (jwtUtil.isValid(token)) { + // 토큰에서 이메일 추출 + String email = jwtUtil.getEmail(token); + // 인증 객체 생성: 이메일로 찾아온 뒤, 인증 객체 생성 + UserDetails user = customUserDetailsService.loadUserByUsername(email); + Authentication auth = new UsernamePasswordAuthenticationToken( + user, + null, + user.getAuthorities() + ); + // 인증 완료 후 SecurityContextHolder에 넣기 + SecurityContextHolder.getContext().setAuthentication(auth); + } + filterChain.doFilter(request, response); + } catch (Exception e) { + response.setContentType("application/json;charset=UTF-8"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + + ApiResponse errorResponse = ApiResponse.onFailure( + GeneralErrorCode.UNAUTHORIZED, + null + ); + + ObjectMapper mapper = new ObjectMapper(); + mapper.writeValue(response.getOutputStream(), errorResponse); + } + } +} \ No newline at end of file diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/jwt/JwtUtil.java b/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/jwt/JwtUtil.java new file mode 100644 index 0000000..60ee3e2 --- /dev/null +++ b/cookie/chapter4/src/main/java/com/example/chapter4/global/auth/jwt/JwtUtil.java @@ -0,0 +1,94 @@ +package com.example.chapter4.global.auth.jwt; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import lombok.Value; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.stream.Collectors; + +@Component +public class JwtUtil { + + private final SecretKey secretKey; + private final Duration accessExpiration; + + @Autowired + public JwtUtil( + @Value("${jwt.token.secretKey}") String secret, + @Value("${jwt.token.eSxpiration.access}") Long accessExpiration + ) { + this.secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); + this.accessExpiration = Duration.ofMillis(accessExpiration); + } + + // AccessToken 생성 + public String createAccessToken(CustomUserDetails user) { + return createToken(user, accessExpiration); + } + + /** 토큰에서 이메일 가져오기 + * + * @param token 유저 정보를 추출할 토큰 + * @return 유저 이메일을 토큰에서 추출합니다 + */ + public String getEmail(String token) { + try { + return getClaims(token).getPayload().getSubject(); // Parsing해서 Subject 가져오기 + } catch (JwtException e) { + return null; + } + } + + /** 토큰 유효성 확인 + * + * @param token 유효한지 확인할 토큰 + * @return True, False 반환합니다 + */ + public boolean isValid(String token) { + try { + getClaims(token); + return true; + } catch (JwtException e) { + return false; + } + } + + // 토큰 생성 + private String createToken(CustomUserDetails user, Duration expiration) { + Instant now = Instant.now(); + + // 인가 정보 + String authorities = user.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.joining(",")); + + return Jwts.builder() + .subject(user.getUsername()) // User 이메일을 Subject로 + .claim("role", authorities) + .claim("email", user.getUsername()) + .issuedAt(Date.from(now)) // 언제 발급한지 + .expiration(Date.from(now.plus(expiration))) // 언제까지 유효한지 + .signWith(secretKey) // sign할 Key + .compact(); + } + + // 토큰 정보 가져오기 + private Jws getClaims(String token) throws JwtException { + return Jwts.parser() + .verifyWith(secretKey) + .clockSkewSeconds(60) + .build() + .parseSignedClaims(token); + } +} \ No newline at end of file diff --git a/cookie/chapter4/src/main/java/com/example/chapter4/global/config/SecurityConfig.java b/cookie/chapter4/src/main/java/com/example/chapter4/global/config/SecurityConfig.java new file mode 100644 index 0000000..aec86fb --- /dev/null +++ b/cookie/chapter4/src/main/java/com/example/chapter4/global/config/SecurityConfig.java @@ -0,0 +1,64 @@ +package com.example.chapter4.global.config; + +import com.example.chapter4.global.auth.jwt.JwtAuthFilter; +import com.example.chapter4.global.auth.jwt.JwtUtil; +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.EnableWebSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@EnableWebSecurity +@Configuration +public class SecurityConfig { + + private final String[] allowUris = { + // Swagger 허용 + "/swagger-ui/**", + "/swagger-resources/**", + "/v3/api-docs/**", + // 회원가입 경로 허용 + "/signup", + "/signup/**", + // 로그인 경로 허용 + "/login", + "/login/**", + }; + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests(requests -> requests + .requestMatchers(allowUris).permitAll() + .requestMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ) + .formLogin(form -> form + .defaultSuccessUrl("/swagger-ui/index.html", true) + .permitAll() + ) + .logout(logout -> logout + .logoutUrl("/logout") + .logoutSuccessUrl("/login?logout") + .permitAll() + ); + + return http.build(); + } + @Bean + public JwtAuthFilter jwtAuthFilter(JwtUtil jwtUtil) { + return new JwtAuthFilter(jwtUtil, customUserDetailsService); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} \ No newline at end of file diff --git a/cookie/chapter4/src/main/resources/application.yml b/cookie/chapter4/src/main/resources/application.yml index d217bdf..e4c98fd 100644 --- a/cookie/chapter4/src/main/resources/application.yml +++ b/cookie/chapter4/src/main/resources/application.yml @@ -1,6 +1,11 @@ spring: application: name: "chapter4" +jwt: + token: + secretKey: chapter4-secret-key-min-256-bits-long-secret-key-for-hs256-algorithm-should-be-very-long-string + expiration: + access: 3600000 datasource: driver-class-name: com.mysql.cj.jdbc.Driver # MySQL JDBC ???? ??? ??