Home UserDetails 와 UserDetailsService
Post
Cancel

UserDetails 와 UserDetailsService

UserDetailsService, UserDetails구현

  • UserDetailsService와 UserDetails 구현체만 구현하면 스프링 시큐리티가 나머지는 쉽게 사용할 수 있도록 도움을 많이 주기 때문에 UserDetails와 UserDetailsService를 구현해야한다.
  • Username password의 인증 방식
    • UsernamePasswordAuthenticationFilter가 request로부터 username, password로 인증이 완료되지 않은 UsernamePasswordAuthenticationToken을 만들어 ProviderManager에게 전달한다.
    • ProviderManager는 전달받은 Authentication을 인증할 수 있는 Provider를 찾아 인증 책임을 위임한다.
    • UsernamePasswordAuthenticationToken은 DaoAuthenticationProvider가 처리하며 해당 Provider는 Bean으로 등록 된 UserDetailsService로부터 해당 username을 가진 user를 요청한다(loadUserByUsername).
    • 이후 비밀번호 일치 등 검증을 수행한 뒤 검증에 성공한다면 검증 여부가 true로 된 Authentication객체를 새로 생성해 반환한다.

      member.domain

  • Member (UserDetails 구현)

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @Builder
      @Entity
      public class Member implements UserDetails {
        
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long memberId;
        
          //set이기 때문에 oneToMany로 join , fetch는 user를 올릴때 항상 authority를 같이 fetch
          @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) //user와 life cycle같음
          @JoinColumn(name = "member_id", foreignKey = @ForeignKey(name="member_id"))
          private Set<MemberAuthority> authorities;
        
          private String email;
        
          private String password;
        
          private boolean enabled;
        
          @Override
          public String getUsername() {      //id를 email로 사용할 예정
              return email;
          }
        
          @Override
          public boolean isAccountNonExpired() { 
              return enabled;
          }
        
          @Override
          public boolean isAccountNonLocked() {
              return enabled;
          }
        
          @Override
          public boolean isCredentialsNonExpired() {
              return enabled;
          }
      }
    
    • getAuthorities() : 계정의 권한 목록 리턴
    • getPassword() : 계정의 비밀번호 리턴
    • getUsername() : 계정의 고유한 값 리턴
    • isAccountNonExpired() : 계정 만료 여부 리턴 (true가 만료 안됨)
    • isAccountNonLocked() : 계정의 잠김 여부 리턴 (true가 잠김 안됨)
    • isCredentialNonExpired() : 비밀번호 만료 여부 리턴 (true가 만료 안됨)
    • isEnabled() : 계정의 활성화 여부 리턴 (true가 활성화)
  • MemberAuthority (GrantedAuthority 구현)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @Builder
      @Entity
      @IdClass(MemberAuthority.class)
      public class MemberAuthority implements GrantedAuthority {
        
          @Id
          @Column(name="member_id")
          private Long memberId;
        
          @Id
          private String authority;
        
      }
    

member.service

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@Service
@Transactional
public class MemberService implements UserDetailsService {
    
    private final MemberRepository memberRepository;
    
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return memberRepository.findByEmail(username).orElseThrow(()->new UsernameNotFoundException(username));
    }

    public Optional<Member> findUser(String email){
        return memberRepository.findByEmail(email);
    }

    public Member save(Member member){
        return memberRepository.save(member);
    }

    public void addAuthority(Long memberId, String authority){
        memberRepository.findById(memberId).ifPresent(member->{
            MemberAuthority newRole = new MemberAuthority(member.getMemberId(), authority);
            if(member.getAuthorities() == null){
                HashSet<MemberAuthority> authorities = new HashSet<>();
                authorities.add(newRole);
                member.setAuthorities(authorities);
                save(member);
            }else if(!member.getAuthorities().contains(newRole)){
                HashSet<MemberAuthority> authorities = new HashSet<>();
                authorities.addAll(member.getAuthorities());
                authorities.add(newRole);
                member.setAuthorities(authorities);
                save(member);
            }
        });
    }  

    public void removeAuthority(Long memberId, String authority){
        memberRepository.findById(memberId).ifPresent(member->{
        if(member.getAuthorities()==null) return;
        MemberAuthority targetRole = new MemberAuthority(member.getMemberId(), authority);
        if(member.getAuthorities().contains(targetRole)){
            member.setAuthorities(
                member.getAuthorities().stream().filter(auth->!auth.equals(targetRole))
                .collect(Collectors.toSet())
                );
                save(member);
            }
        });
    }
}
This post is licensed under CC BY 4.0 by the author.