Skip to content

Commit

Permalink
Merge PR(#28) from feature/login-#26 휴면 회원 관련 기능
Browse files Browse the repository at this point in the history
  • Loading branch information
woody35545 authored May 9, 2024
2 parents f77d361 + 92d6054 commit 087b17c
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
import com.t3t.authenticationapi.account.entity.Refresh;
import com.t3t.authenticationapi.account.exception.JsonFieldNotMatchException;
import com.t3t.authenticationapi.account.service.TokenService;
import com.t3t.authenticationapi.member.entity.Member;
import com.t3t.authenticationapi.member.repository.MemberRepository;
import com.t3t.authenticationapi.member.service.MemberService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
Expand All @@ -16,8 +20,8 @@
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StreamUtils;

import javax.servlet.FilterChain;
Expand All @@ -30,17 +34,22 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.UUID;

/**
* 로그인 과정을 담당하는 LoginFilter
*
* @author joohyun1996 (이주현)
*/
@RequiredArgsConstructor
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
private final JWTUtils jwtUtils;
private final TokenService tokenService;
private final MemberService memberService;

/**
* 사용자가 입력한 login 정보를 가지고 인증 시도를 하는 메소드
*
* @param request,response
* @return Authentication
* @author joohyun1996 (이주현)
Expand All @@ -54,7 +63,7 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
loginDto = mapper.readValue(messageBody, LoginDto.class);
}catch (IOException e){
} catch (IOException e) {
throw new JsonFieldNotMatchException(e);
}

Expand All @@ -65,47 +74,53 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, password, null);
return authenticationManager.authenticate(authToken);
}

/**
* 로그인 성공시 진행되는 메소드
* 성공시 UserDetails에서 id, pw, authority 등을 꺼내 jwt 토큰 생성
* access token은 response header에 담아 전달, refresh token은 redis에 저장
* @return 200_OK, "Authorization : Bearer + accesstoken"
*
* @param request,response,chain,authentication
* @return 200_OK, "Authorization : Bearer + accesstoken"
* @author joohyun1996 (이주현)
*/
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) {
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) {
CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal();

String userId = customUserDetails.getUserId();

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
GrantedAuthority grantedAuthority = iterator.next();

String role = grantedAuthority.getAuthority();

String uuid = UUID.randomUUID().toString();
String access = jwtUtils.createJwt("access", userId, role, uuid, 900000l); // 15분
String refresh = jwtUtils.createJwt("refresh", userId, role, uuid,1800000l); // 30분
String refresh = jwtUtils.createJwt("refresh", userId, role, uuid, 1800000l); // 30분

tokenService.saveRefreshToken(Refresh.builder().token(refresh).uuid(uuid).build());

memberService.updateMemberLoginAt(Long.parseLong(userId));

response.addHeader("Authorization", "Bearer " + access);
response.setStatus(HttpServletResponse.SC_OK);
}

/**
* 로그인 실패시 수행되는 메소드
* @return 401_Unauthorized, error message
*
* @param request,response,failed
* @return 401_Unauthorized, error message
* @author joohyun1996 (이주현)
*/
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
String errorMessage = null;
if(failed instanceof BadCredentialsException){
if (failed instanceof BadCredentialsException) {
errorMessage = "invalid id or password";
}else{
} else {
errorMessage = "auth failed";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
import com.t3t.authenticationapi.account.repository.RefreshRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;
/**
* Redis에 저장된 Refresh, BlackList 토큰의 CRUD를 담당하는 service 클래스
* @author joohyun1996 (이주현)
*/
@Service
@Transactional
@RequiredArgsConstructor
public class TokenService {
private final RefreshRepository refreshRepository;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
package com.t3t.authenticationapi.config;

import com.t3t.authenticationapi.account.auth.CustomUserDetails;
import com.t3t.authenticationapi.account.component.CustomAuthenticationProvider;
import com.t3t.authenticationapi.account.component.JWTUtils;
import com.t3t.authenticationapi.account.filter.CommonExceptionFilter;
import com.t3t.authenticationapi.account.filter.CustomLogoutFilter;
import com.t3t.authenticationapi.account.filter.LoginFilter;
import com.t3t.authenticationapi.account.service.DefaultUserDetailsService;
import com.t3t.authenticationapi.account.service.TokenService;
import com.t3t.authenticationapi.member.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
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.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;

/**
* Spring Security 등록을 위한 configuration 클래스
* @author joohyun1996 (이주현)
Expand All @@ -37,6 +33,7 @@ public class SecurityConfig {
private final JWTUtils jwtUtils;
private final TokenService tokenService;
private final CustomAuthenticationProvider provider;
private final MemberService memberService;

@Autowired
public void globalConfigure(AuthenticationManagerBuilder auth) throws Exception{
Expand Down Expand Up @@ -67,7 +64,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.antMatchers("/logout").authenticated()
.anyRequest().authenticated())
.addFilterBefore(new CommonExceptionFilter(), LoginFilter.class)
.addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtils, tokenService), UsernamePasswordAuthenticationFilter.class)
.addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtils, tokenService, memberService), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new CustomLogoutFilter(jwtUtils, tokenService), LogoutFilter.class)
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
Expand Down
22 changes: 18 additions & 4 deletions src/main/java/com/t3t/authenticationapi/member/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@
import javax.persistence.Id;
import javax.persistence.Table;
import java.time.LocalDate;
import java.time.LocalDateTime;

@Getter
@Entity@Table(name = "members")
@Entity
@Table(name = "members")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id
@Column(name = "member_id")
private Long id;
@Column(name = "grade_id")
@Column(name = "member_grade_id")
private Integer gradeId;
@Column(name = "member_name")
private String name;
Expand All @@ -29,16 +31,17 @@ public class Member {
@Column(name = "member_birthdate")
private LocalDate birthdate;
@Column(name = "member_latest_login")
private LocalDate latestLogin;
private LocalDateTime latestLogin;
@Column(name = "member_point")
private Integer point;
@Column(name = "member_status")
private String status;
@Column(name = "member_role")
private String role;

@Builder
public Member(Long id, Integer gradeId, String name, String phone, String email,
LocalDate birthdate, LocalDate latestLogin, Integer point,
LocalDate birthdate, LocalDateTime latestLogin, Integer point,
String status, String role) {
this.id = id;
this.gradeId = gradeId;
Expand All @@ -51,4 +54,15 @@ public Member(Long id, Integer gradeId, String name, String phone, String email,
this.status = status;
this.role = role;
}

/**
* 마지막 로그인 시간을 갱신한다.
*
* @author woody35545(구건모)
*/
public void updateLastLogin() {
if (!this.status.equals("INACTIVE")) {
this.latestLogin = LocalDateTime.now();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.t3t.authenticationapi.member.exception;

/**
* 존재하지 않는 회원에 대한 조회를 시도할 때 발생하는 예외
*
* @author woody35545
*/
public class MemberNotFoundException extends RuntimeException {

public MemberNotFoundException() {
super("존재하지 않는 회원 입니다.");
}

public MemberNotFoundException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.t3t.authenticationapi.member.service;

import com.t3t.authenticationapi.member.exception.MemberNotFoundException;
import com.t3t.authenticationapi.member.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class MemberService {

private final MemberRepository memberRepository;

/**
* 인증 성공시 로그인 시간을 업데이트한다.
* @param memberId 로그인 시간을 업데이트 할 회원 식별자
* @author woody35545(구건모)
*/
public void updateMemberLoginAt(Long memberId) {
memberRepository.findById(memberId)
.orElseThrow(() -> new MemberNotFoundException())
.updateLastLogin();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

import java.time.LocalDate;
import java.time.LocalDateTime;

@DataJpaTest
class AccountRepositoryTest {
@Autowired
Expand All @@ -18,7 +20,7 @@ class AccountRepositoryTest {
private AccountRepository accountRepository;

@Test
public void testLoadUserEntity(){
public void testLoadUserEntity() {
Member member = Member.builder()
.id(3l)
.name("foo")
Expand All @@ -28,7 +30,7 @@ public void testLoadUserEntity(){
.email("[email protected]")
.birthdate(LocalDate.now())
.status("ACTIVE")
.latestLogin(LocalDate.now())
.latestLogin(LocalDateTime.of(2024, 1, 1, 0, 0))
.gradeId(1)
.build();

Expand Down

0 comments on commit 087b17c

Please sign in to comment.