티스토리 뷰
본 포스팅은 백기선님의 스프링과 JPA 기반 웹 애플리케이션 개발 강의를 참고하여 작성하였습니다.
소스 코드는 여기 있습니다. (commit hash: 73571fb)> git clone https://github.com/lcalmsky/spring-boot-app.git > git checkout 73571fb
ℹ️ squash merge를 사용해 기존 branch를 삭제하기로 하여 앞으로는 commit hash로 포스팅 시점의 소스 코드를 공유할 예정입니다.
Overview
스프링 시큐리티 기능을 활용하여 현재 인증된 사용자 정보를 참조하는 방법을 살펴보겠습니다.
Implementation
@AuthenticationPrincipal
애너테이션은 Authentication
객체의 getPrincipal()
를 가져오기 위해 사용합니다.
직접 객체 의존성을 주입하고 처리할 필요 없이 애너테이션을 사용하여 간단하게 가져올 수 있는데요, 사용 방법은 여러 가지가 있지만 소개하는 내용은 Custom Annotation을 생성하는 방법입니다.
여기서 Principal
은 Authentication
객체를 생성할 때 필요한 첫 번째 파라미터로 사용자의 인증 정보를 담고있습니다.
이 프로젝트에서 사용한 UsernamePasswordAuthenticationToken
(Authentication
을 상속)을 간단히 살펴보면,
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
setAuthenticated(false);
}
public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true); // must use super, as we override
}
두 생성자 모두 첫 번째 파라미터가 principal
이라고 되어있는 것을 확인할 수 있습니다.
CurrentUser 애너테이션 작성
account.support
패키지 내에 CurrentUser
애너테이션을 작성합니다.
package io.lcalmsky.app.account.support;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // (1)
@Target(ElementType.PARAMETER) // (2)
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account") // (3)
public @interface CurrentUser {
}
- Runtime시 유지되어야 합니다.
- 파라미터에 사용할 수 있어야 합니다.
- spEL을 이용하여 인증정보가 존재하지 않으면
null
을, 존재하면account
라는 property를 반환합니다.
MainController 작성
처리하는 도메인이 다르므로 main
이라는 패키지를 생성한 뒤 endpoint.controller
패키지 하위에 MainController
클래스를 작성합니다.
package io.lcalmsky.app.main.endpoint.controller;
import io.lcalmsky.app.account.domain.entity.Account;
import io.lcalmsky.app.account.support.CurrentUser;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MainController {
@GetMapping("/")
public String home(@CurrentUser Account account, Model model) { // (1)
if (account != null) {
model.addAttribute(account);
}
return "index";
}
}
- // (1) @CurrentUser의 영향을 받아 현재 인증된 사용자 정보에 따라 객체가 할당됩니다.
현재 로그인 할 때 사용한 Principal
(UsernamePasswordAuthenticationToken
)에는 Account
객체가 없습니다.
AccountService.login 참조
public void login(Account account) { UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(account.getNickname(), account.getPassword(), Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"))); SecurityContextHolder.getContext().setAuthentication(token); // AuthenticationManager를 쓰는 방법이 정석적인 방ㅇ법 }
따라서 Principal
에서 Account
를 가져오기 위해선 중간 adaptor
역할을 하는 객체가 필요합니다.
UserAccount 클래스 작성
account.domain
패키지 내에 UserAccount
클래스를 생성합니다.
UserDetailsService
를 구현하고있는 User
를 상속해야 합니다.
package io.lcalmsky.app.account.domain;
import io.lcalmsky.app.account.domain.entity.Account;
import lombok.Getter;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.List;
public class UserAccount extends User {
@Getter
private final Account account; // (1)
public UserAccount(Account account) {
super(account.getNickname(), account.getPassword(), List.of(new SimpleGrantedAuthority("ROLE_USER"))); // (2)
this.account = account;
}
}
@CurrentUser
애너테이션에서account
를 반환하도록 하였기 때문에 변수 이름을 반드시account
로 설정해야 합니다.- User 객체를 생성하기 위해선
username
,password
,authorities
가 필요한데 우리가 사용하는 객체인Account
에서 각각 추출해줍니다. (권한은 기존AccountService
에서 사용하던 것으로 동일하게 넣어줍니다)
이렇게 수정했으면 AccountService
에서 로그인 처리하는 부분도 변경해줘야겠죠?
AccountService 수정
public void login(Account account) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(new UserAccount(account), // (1)
account.getPassword(), Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")));
SecurityContextHolder.getContext().setAuthentication(token); // AuthenticationManager를 쓰는 방법이 정석적인 방ㅇ법
}
- 기존에
nickname
을 전달했던 파라미터를UserAccount
객체로 대체합니다.
이렇게 수정해줬다면 MainController
에서 @CurrentUser
애너테이션에 의해 @AuthenticationPrincipal
이 적용되고, 인증 여부에 따라 account
를 반환해서 넘겨줄 수 있게 되는 것입니다.
Test
애플리케이션을 실행한 뒤 회원 가입, 이메일 인증을 하고 다시 루트 (/)로 이동했을 때 로그인 된 상태로 표시되면 성공입니다!
'SpringBoot > Web Application 만들기' 카테고리의 다른 글
스프링 부트 웹 애플리케이션 제작(14): 로그인, 로그아웃 기능 구현 (0) | 2022.01.16 |
---|---|
스프링 부트 웹 애플리케이션 제작(13): 인증 이메일 확인 유도 및 재전송 (2) | 2022.01.13 |
스프링 부트 웹 애플리케이션 제작(11): 아이콘 추가 및 프로필 아바타 설정 (0) | 2021.12.03 |
스프링 부트 웹 애플리케이션 제작(10): View 중복 코드 제거(feat. fragment) (0) | 2021.11.25 |
스프링 부트 웹 애플리케이션 제작(9): 프론트엔드 라이브러리 및 빌드 설정 (2) | 2021.10.30 |
- Total
- Today
- Yesterday
- gRPC
- Spring Boot Tutorial
- Java
- 함께 자라기 후기
- 스프링 데이터 jpa
- 알고리즘
- spring boot app
- QueryDSL
- 클린 아키텍처
- Linux
- Spring Data JPA
- spring boot jwt
- 스프링부트
- Jackson
- Spring Boot
- JSON
- 스프링 부트 튜토리얼
- intellij
- proto3
- Spring Boot JPA
- 함께 자라기
- r
- spring boot application
- 스프링 부트 애플리케이션
- @ManyToOne
- leetcode
- 헥사고날 아키텍처
- 스프링 부트
- 스프링 부트 회원 가입
- JPA
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |