Spring/스프링 기반 REST API 개발
[스프링 기반 REST API 개발] 시큐리티 현재 사용자 조회하기
주멘이
2021. 1. 9. 22:01
SecurityContext
- 자바 ThreadLocal 기반 구현으로 인증 정보를 담고 있다.
- ※ ThreadLocal ? 오직 한 Thread에 의해서 read/write 가능한 variable을 생성할 수 있도록 하는 것(Thread Local Variable)
- 인증 정보를 꺼내는 방법
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// Anonymous인 경우 principal = "anonymousUser" 로 String 이다.
@AuthenticationPrincipal spring.security.User user
- 인증을 안했다 ? null : username과 authorities 참조 가능
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")
queryEvents를 호출할때 user가 있는 경우 create-event link 추가
@GetMapping
public ResponseEntity queryEvents(Pageable pageable, PagedResourcesAssembler<Event> assembler,
@CurrentUser Account account) {
// @CurrentUser Account account는 @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account") 이다
Page<Event> page = this.eventRepository.findAll(pageable);
PagedModel<EntityModel<Event>> entityModels = assembler.toModel(page, EventResource::new);
entityModels.add(new Link("/docs/index.html#resources-events-list").withRel("profile"));
if (account != null) {
entityModels.add(linkTo(EventController.class).withRel("create-event"));
}
return ResponseEntity.ok(entityModels);
}
spring.security.User를 상속받는 클래스를 구현하면 User 도메인을 받을 수 있다.
UserDetails로 부터 받을 수 있는 security.User 대신에 Account 도메인으로 받기 위해 Adapter를 구현한다.
- @AuthenticationPrincipal을 이용하여 Account 객체 가져오기 위함
- Adapter.getAccount().getId()
AccountAdapter 구현하기
public class AccountAdapter extends User {
private Account account;
public AccountAdapter(Account account) {
super(account.getEmail(), account.getPassword(), authorities(account.getRoles()));
this.account = account;
}
public Account getAccount() {
return account;
}
private static Collection<? extends GrantedAuthority> authorities(Set<AccountRole> roles) {
return roles.stream()
.map(r -> new SimpleGrantedAuthority("ROLE_" + r.name()))
.collect(Collectors.toSet());
}
}
AccountService의 loadByUsername이 AccountAdapter를 리턴하도록 수정하기
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException(username));
return new AccountAdapter(account);
}
- AccountAdapter는 User를 상속하고 있고, User는 UserDetails를 구현하고 있다.
@AuthenticationPrincipal 을 사용할 경우, anonymousUser인 경우 Object가 아니라 'anonymousUser' String이 넘어와서 오류가 발생할 수 있다.
그래서 expression에 Object를 반환할 수 있도록, 아래와 같이 설정해야 한다.
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")
public @interface CurrentUser {
}