Member(회원)와 Post(게시글) entity가 1 : N 관계일 때,
로그인 한 회원이 글을 작성했을 때 DB에 회원 정보와 함께 저장되도록 하는 기능을 구현해보았습니다.
방법을 몰라서 이런 저런 시도를 두 시간 가량 해보다가 구글링과 그 간 배웠던 지식들을 토대로 결국에는 구현해낼 수 있었습니다.
필요하신 분께 도움이 되면 좋겠고, 더 좋은 방법이 있다면 알려주시면 감사하겠습니다.
여기서는 Form login을 사용했고 session id로 인증하는 방법을 사용했습니다.
1. SecurityFilterChain을 보면
"/v1/post/**" 경로에는 인증된 사용자만이 접근하여 글을 작성할 수 있도록 권한 설정을 해두었습니다.
@Configuration
public class AppSecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
};
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable();
http.headers().frameOptions().disable();
http.authorizeRequests()
.antMatchers("/v1/post/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login");
return http.build();
}
}
여기까지는 어렵지 않게 구현했는데, /v1/post 경로로 진입하여 게시글을 저장하려고 할 때 Post table에 게시글 정보를 저장하는 방법을 알아내는데 노력이 필요했습니다.
<무엇을 알아야 했었는가?>
1. 로그인 한 회원의 정보를 어떻게 가져올 수 있을 지 알아야 했습니다.
2. 로그인 한 회원의 정보를 알 수 있게 되었다면 게시글(Post) DB에 회원 정보와 같이 저장할 수 있어야 했습니다.
로그인은 성공했지만, 현재 로그인한 유저의 정보를 어떻게 알 수 있는지 그 방법을 찾아야 했습니다.
UserDetails에 현재 로그인된 유저의 정보를 알 수 있다는 것을 떠올렸고 이를 활용하는 방법으로 시도해봤습니다.
postPost(메서드명이 좀 헷갈리게 지어진 것 같습니다 ㅎㅎ;) method의 파라미터로 @AuthenticationPrincipal을 추가해서 현재 로그인된 유저의 정보를 받아오게 해보았습니다.
@PostMapping
public ResponseEntity postPost(@AuthenticationPrincipal PrincipalDetails principalDetails,
@Valid PostDto.Post postDto) {
//postDto to Post so that Service can do its job
Post post = postMapper.postDtoToPost(postDto);
long memberId = principalDetails.getUserId();
Member foundMember = memberService.findVerifiedMember(memberId);
post.setMember(foundMember);
//service
Post createdPost = postService.createPost(post);
//post entity to response dto
PostDto.Response response = postMapper.postToPostResponse(createdPost);
log.info(post.getMember().getName() + " post Success");
return new ResponseEntity<>(response, HttpStatus.CREATED);
}
@AuthenticationPrincipal에 대한 공식적인 설명은 아래 사진에 나온 것 처럼
"Authentication.getPrincipal()을 메서드의 파라미터로 사용할 수 있도록 해주는 annotation" 입니다.
Principal이 인증된 유저의 정보니까 이 annotation을 사용하면 되는 게 맞는 것 같습니다.
🏷 해당 애노테이션에 대해 찾아보며 참고한 블로그(엔꾸꾸님)에서는
"@AuthenticationPrincipal 애노테이션을 사용하면 UserDetailsService에서 Return한 객체를 파라메터로 직접 받아 사용할 수 있다." 라고 설명해주셨습니다.
이 방법으로 "1. 로그인 한 회원의 정보를 어떻게 가져올 수 있는가" 부분을 해결했습니다.
postPost 메서드 파라미터에서 사용한 PrincipalDetails은 UserDetails를 구현하고 있는 제가 만든 구현체입니다. 그리고 PrincipalDetails class 내부에 기본적으로 override 해야하는 메서드 외에 getUserId() method를 추가하여 유저의 id를 가져올 수 있도록 하였습니다. 유저 id를 이용해서 저장된 유저 entity를 찾고, 게시글 등록 시에 필요한 유저 정보를 제공할 수 있었습니다.
이렇게 해서 알아내야 하는 항목 두번째 "2. 게시글(Post) DB에 회원 정보와 같이 저장하기"도 해결할 수 있었습니다.
한 번 테스트한 결과를 보여드리면,
이렇게 MEMBER_ID와 잘 연결되어 DB에 저장된 것을 확인할 수 있었습니다.
방법을 알기전에는 좀 막막했었는데, 해당 내용을 공부하면서 security에서 user 정보를 어떻게 다루는 지 조금 더 알게되어 뿌듯합니다. ㅎㅎ
다음번에는 JWT를 사용하는 방식으로 구현해보려고 합니다!
참고자료
https://docs.spring.io/spring-security/site/docs/4.0.1.RELEASE/reference/htmlsingle/#jc-form
https://docs.spring.io/spring-security/reference/servlet/authentication/rememberme.html
[핵심적인 도움이 되었던 자료👏]
https://www.codejava.net/frameworks/spring-boot/get-logged-in-user-details
'Java Spring > Security' 카테고리의 다른 글
RestController 테스트 시 JWT 생성하기 (2) | 2022.09.20 |
---|---|
JWT란? JWT 생성과 사용 방법 알아보기 (0) | 2022.08.05 |
Authorization(인가)-인가 내부 절차 & 권한 설정하기 (0) | 2022.08.01 |
Authentication(인증) (2) - 로그인 절차 & 코드구현 (0) | 2022.07.30 |
Authentication(인증) (1) - 사용자 정보 저장하기 & 비밀번호 암호화하여 저장하기(DelegatingPasswordEncoder는 무엇인가?) (1) | 2022.07.29 |