Skip to content
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
3 changes: 3 additions & 0 deletions src/main/java/com/retrip/auth/AuthApplication.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.retrip.auth;

import com.retrip.auth.application.config.JwtConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties({JwtConfig.class})
public class AuthApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.retrip.auth.application.config;


import com.retrip.auth.domain.entity.Member;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.stream.Collectors;


public class CustomUserDetails implements UserDetails {

private final Member member;

public CustomUserDetails(Member member) {
this.member = member;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return member.getAuthorities().getValues().stream()
.map(authority -> new SimpleGrantedAuthority(authority.getGrant().getCode()))
.collect(Collectors.toList());
}

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

@Override
public String getUsername() {
return member.getEmail().getValue();
}
}
29 changes: 29 additions & 0 deletions src/main/java/com/retrip/auth/application/config/JwtConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.retrip.auth.application.config;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;


@Getter
@RequiredArgsConstructor
@ConfigurationProperties("token.jwt")
public class JwtConfig {
private final String secret;
private final String header;
private final String prefix;
private final AccessConfig access;
private final RefreshConfig refresh;

@Getter
@RequiredArgsConstructor
public static class AccessConfig {
private final int expireMin;
}

@Getter
@RequiredArgsConstructor
public static class RefreshConfig {
private final int expireMin;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.retrip.auth.application.config;

import com.retrip.auth.application.in.MemberService;
import com.retrip.auth.infra.adapter.in.rest.filter.JwtAuthenticationFilter;
import com.retrip.auth.infra.adapter.in.rest.filter.LoginAuthenticationFilter;
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.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

@Configuration
@RequiredArgsConstructor
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); //μ•”ν˜Έ 인코더 μ •μ˜
}

@Bean
public LoginAuthenticationFilter loginAuthenticationFilter(JwtConfig jwtConfig, AuthenticationManager authenticationManager) {
return new LoginAuthenticationFilter(jwtConfig, authenticationManager);
}

@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter(JwtConfig jwtConfig){
return new JwtAuthenticationFilter(jwtConfig);
}
@Bean
public AuthenticationManager authenticationManager(
HttpSecurity http,
UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider,
MemberService memberService) throws Exception {
return http.authenticationProvider(usernamePasswordAuthenticationProvider)
.userDetailsService(memberService)
.getSharedObject(AuthenticationManagerBuilder.class)
.build();
}


@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, LoginAuthenticationFilter loginAuthenticationFilter, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.addFilterAt(loginAuthenticationFilter, BasicAuthenticationFilter.class)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated());

return http.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.retrip.auth.application.config;

import java.util.Collection;

import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

public class UsernamePasswordAuthentication extends UsernamePasswordAuthenticationToken {
//인증 μ™„λ£Œ
public UsernamePasswordAuthentication(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}

//인증 X
public UsernamePasswordAuthentication(Object principal, Object credentials) {
super(principal, credentials);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.retrip.auth.application.config;

import com.retrip.auth.application.in.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class UsernamePasswordAuthenticationProvider implements AuthenticationProvider {

private final MemberService memberService;
private final PasswordEncoder passwordEncoder;

@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String username = authentication.getName();
String password = String.valueOf(authentication.getCredentials());

UserDetails user = memberService.loadUserByUsername(username);

if (!passwordEncoder.matches(password, user.getPassword())) {
throw new BadCredentialsException("Bad credentials");
}

return new UsernamePasswordAuthenticationToken(
username,
password,
user.getAuthorities().stream().toList()
);
}

@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthentication.class.isAssignableFrom(authentication);
}
}
28 changes: 28 additions & 0 deletions src/main/java/com/retrip/auth/application/in/MemberService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.retrip.auth.application.in;

import com.retrip.auth.application.config.CustomUserDetails;
import com.retrip.auth.application.in.usercase.ManageMemberUseCase;
import com.retrip.auth.application.out.repository.MemberQueryRepository;
import com.retrip.auth.application.out.repository.MemberRepository;
import com.retrip.auth.domain.entity.Member;
import com.retrip.auth.domain.exception.MemberNotFoundException;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
@Transactional
public class MemberService implements ManageMemberUseCase, UserDetailsService {

private final MemberQueryRepository memberQueryRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Member member = memberQueryRepository.findByEmailWithAuthorities(username).orElseThrow(MemberNotFoundException::new);
return new CustomUserDetails(member);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.retrip.auth.application.in.request;

import org.springframework.security.crypto.password.PasswordEncoder;

public record LoginRequest (
String email,
String password
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.retrip.auth.application.in.response;

public record LoginResponse(
TokenResponse token
) {
public record TokenResponse(String accessToken, String refreshToken) {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.retrip.auth.application.in.usercase;



public interface ManageMemberUseCase {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.retrip.auth.application.out.repository;

import com.retrip.auth.domain.entity.Member;

import java.util.Optional;
import java.util.UUID;

public interface MemberQueryRepository {
Optional<Member> findByEmailWithAuthorities(String email);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.retrip.auth.application.out.repository;

import com.retrip.auth.domain.entity.Member;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberRepository extends JpaRepository<Member, Long> {
}
31 changes: 31 additions & 0 deletions src/main/java/com/retrip/auth/domain/entity/Authorities.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.retrip.auth.domain.entity;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Embeddable;
import jakarta.persistence.OneToMany;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

import static lombok.AccessLevel.PROTECTED;

@Getter
@Embeddable
@NoArgsConstructor(access = PROTECTED, force = true)
public class Authorities {
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private final List<Authority> values = new ArrayList<>();

public Authorities(List<String> authorities, Member member) {
validate(authorities);
authorities.forEach(authority -> this.values.add(Authority.create(authority, member)));
}

private void validate(List<String> authorities) {
if (authorities == null || authorities.isEmpty()) {
throw new IllegalArgumentException("κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€.");
}
}
}
43 changes: 43 additions & 0 deletions src/main/java/com/retrip/auth/domain/entity/Authority.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.retrip.auth.domain.entity;

import com.retrip.auth.domain.vo.AuthorityGrant;
import jakarta.persistence.*;

import java.time.LocalDate;
import java.util.UUID;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.security.authentication.jaas.AuthorityGranter;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Authority extends BaseEntity {
@Id
@Column(columnDefinition = "varbinary(16)")
private UUID id;

@Column(name = "grant", length = 50, nullable = false)
private AuthorityGrant grant;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
name = "member_id",
nullable = false,
columnDefinition = "varbinary(16)",
foreignKey = @ForeignKey(name = "fk_authority_to_member")
)
private Member member;

private Authority(String grant, Member member) {
this.id = UUID.randomUUID();
this.grant = AuthorityGrant.codeOf(grant);
this.member = member;
}

public static Authority create(String grant, Member member) {
return new Authority(grant, member);
}
}
22 changes: 22 additions & 0 deletions src/main/java/com/retrip/auth/domain/entity/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.retrip.auth.domain.entity;

import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;

import java.time.LocalDateTime;

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public abstract class BaseEntity {

@CreatedDate
private LocalDateTime createdAt;

@LastModifiedDate
private LocalDateTime editedAt;
}

Loading