팀과제에서 회원가입기능과 인증(Authentication) 기능 구현을 맞게 됐다.
@Entity
@Getter
@Setter
@NoArgsConstructor
public class User extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String userId;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String userName;
@Column(nullable = false)
private String email;
private String intro;
@Column(nullable = false)
private String status;
private String refreshToken;
// @JoinColumn(name = "")
// @OneToMany
// private List<Content>contentList = new ArrayList<>();
public User(String userId, String password, String name, String email, String intro, String status) {
this.userId = userId;
this.password = password;
this.userName = name;
this.email = email;
this.intro = intro;
this.status = status;
}
public void update(String name, String email, String intro) {
this.userName = name;
this.email = email;
this.intro = intro;
}
public void updatePassword(String newPassword) {
this.password = newPassword;
}
}
@Getter
@Setter
@NoArgsConstructor
public class User extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String userId;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String userName;
@Column(nullable = false)
private String email;
private String intro;
@Column(nullable = false)
private String status;
private String refreshToken;
// @JoinColumn(name = "")
// @OneToMany
// private List<Content>contentList = new ArrayList<>();
public User(String userId, String password, String name, String email, String intro, String status) {
this.userId = userId;
this.password = password;
this.userName = name;
this.email = email;
this.intro = intro;
this.status = status;
}
public void update(String name, String email, String intro) {
this.userName = name;
this.email = email;
this.intro = intro;
}
public void updatePassword(String newPassword) {
this.password = newPassword;
}
}
우선 유저 엔티티를 만들고 맵핑관계는 content클래스와 1대N 으로 하려고하는대 우선 주석처리해두었다.
UserService에 회원가입 기능을 구현 회원가입dto을 만들어서 요청을 받도록했고 중복을 허용하지 않는 userId와 email부분은 옵셔널을 사용하여 null체크를 하였다.
public void singUp(SignUpRequestDto signUpRequestDto){
String userId = signUpRequestDto.getUserId();
String password = passwordEncoder.encode(signUpRequestDto.getPassword());
String name = signUpRequestDto.getUsername();
String email = signUpRequestDto.getEmail();
String intro = signUpRequestDto.getIntro();
String status = signUpRequestDto.getStatus();
Optional<User> checkUserId = userRepository.findByUserId(userId);
Optional<User> checkEmail = userRepository.findByEmail(email);
if(checkUserId.isPresent()){
throw new IllegalArgumentException("이미 존재하는 id 입니다.");
}
if(checkEmail.isPresent()){
throw new IllegalArgumentException("이미 존재하는 email 입니다.");
}
User user = new User(userId,password,name,email,intro,status);
userRepository.save(user);
}
String userId = signUpRequestDto.getUserId();
String password = passwordEncoder.encode(signUpRequestDto.getPassword());
String name = signUpRequestDto.getUsername();
String email = signUpRequestDto.getEmail();
String intro = signUpRequestDto.getIntro();
String status = signUpRequestDto.getStatus();
Optional<User> checkUserId = userRepository.findByUserId(userId);
Optional<User> checkEmail = userRepository.findByEmail(email);
if(checkUserId.isPresent()){
throw new IllegalArgumentException("이미 존재하는 id 입니다.");
}
if(checkEmail.isPresent()){
throw new IllegalArgumentException("이미 존재하는 email 입니다.");
}
User user = new User(userId,password,name,email,intro,status);
userRepository.save(user);
}
@Getter
@Slf4j(topic = "로그인 및 JWT생성")
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private JwtUtil jwtUtil;
private ObjectMapper objectMapper = new ObjectMapper();
public JwtAuthenticationFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
setFilterProcessesUrl("/api/user/login");
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if("application/json".equals(request.getContentType())){
try{
//요청받은 json을 객체 형태로 변환
LoginRequestDto loginRequestDto = objectMapper.readValue(request.getInputStream(),LoginRequestDto.class);
log.info("Received login request: " + loginRequestDto.getUserId() + " " + loginRequestDto.getPassword());
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(loginRequestDto.getUserId(),loginRequestDto.getPassword());
//추가적인 요청정보를 authRequest에 설정
setDetails(request,authRequest);
//athentication manager를 통해 인증 시도
return this.getAuthenticationManager().authenticate(authRequest);
}catch(IOException e){
throw new RuntimeException(e);
}
}
//요청 json 형식이 아닐경우 부모클래스의 기본 동작 수행
return super.attemptAuthentication(request, response);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
log.info("로그인 성공 및 JWT생성");
String userId = ((UserDetailsImpl) authResult.getPrincipal()).getUsername();
String accessToken = jwtUtil.generateToken(userId, jwtUtil.ACCESS_TOKEN_EXPIRATION , "access");
String refreshToken = jwtUtil.generateToken(userId , jwtUtil.REFRESH_TOKEN_EXPIRATION,"refresh");
jwtUtil.addJwtToCookie(response, accessToken,jwtUtil.ACCESS_TOKEN_HEADER);
jwtUtil.addJwtToCookie(response, refreshToken,jwtUtil.REFRESH_TOKEN_HEADER);
log.info("accesstoken : "+accessToken);
log.info("refreshToken : "+refreshToken);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
log.info("로그인 실패!!");
response.setStatus(401); //인증실패 401코드 전달
}
}
@Slf4j(topic = "로그인 및 JWT생성")
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private JwtUtil jwtUtil;
private ObjectMapper objectMapper = new ObjectMapper();
public JwtAuthenticationFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
setFilterProcessesUrl("/api/user/login");
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if("application/json".equals(request.getContentType())){
try{
//요청받은 json을 객체 형태로 변환
LoginRequestDto loginRequestDto = objectMapper.readValue(request.getInputStream(),LoginRequestDto.class);
log.info("Received login request: " + loginRequestDto.getUserId() + " " + loginRequestDto.getPassword());
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(loginRequestDto.getUserId(),loginRequestDto.getPassword());
//추가적인 요청정보를 authRequest에 설정
setDetails(request,authRequest);
//athentication manager를 통해 인증 시도
return this.getAuthenticationManager().authenticate(authRequest);
}catch(IOException e){
throw new RuntimeException(e);
}
}
//요청 json 형식이 아닐경우 부모클래스의 기본 동작 수행
return super.attemptAuthentication(request, response);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
log.info("로그인 성공 및 JWT생성");
String userId = ((UserDetailsImpl) authResult.getPrincipal()).getUsername();
String accessToken = jwtUtil.generateToken(userId, jwtUtil.ACCESS_TOKEN_EXPIRATION , "access");
String refreshToken = jwtUtil.generateToken(userId , jwtUtil.REFRESH_TOKEN_EXPIRATION,"refresh");
jwtUtil.addJwtToCookie(response, accessToken,jwtUtil.ACCESS_TOKEN_HEADER);
jwtUtil.addJwtToCookie(response, refreshToken,jwtUtil.REFRESH_TOKEN_HEADER);
log.info("accesstoken : "+accessToken);
log.info("refreshToken : "+refreshToken);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
log.info("로그인 실패!!");
response.setStatus(401); //인증실패 401코드 전달
}
}
인증부분 인대 path가 /api/user/login 일 때 필터가 돌아가도록했고 인증시도 성공 실패 부분을 구현했다.
테스트 해볼 때 로그인시 실패만 나와서 이유를 찾아보니 내가 구현한 User엔티티에서는 userId로 회원의 아이디 필드를 정의했다. 그리고 username은 회원의 id가 아닌 이름으로 정의했다. 그럼으로 UserDetails에 getUsername메소드가 user.getUserId를 리턴해야하는대 user.getUsername을 리턴하도록 했기 때문에 회원정보를 찾지못하고 실패했던 거 였다.
@Override
public String getUsername() {
return user.getUserId();
}
public String getUsername() {
return user.getUserId();
}
'TIL' 카테고리의 다른 글
(2024-06-10) 팀과제 마무리 (0) | 2024.06.11 |
---|---|
(2024-06-07) 토큰 저장 방식 헤더로 변경,refresh token엔티티에 저장 (0) | 2024.06.10 |
(2024-06-04) 팀과제 시작 (0) | 2024.06.05 |
(2024-06-03) Entity 연관관계 (0) | 2024.06.04 |
(2024-05-31) RestTemplate (0) | 2024.06.03 |