๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Spring Boot/์ฝ”๋“œ๋กœ ๋ฐฐ์šฐ๋Š” ์Šคํ”„๋ง๋ถ€ํŠธ ์›น ํ”„๋กœ์ ํŠธ

[์ฝ”๋“œ๋กœ ๋ฐฐ์šฐ๋Š” ์Šคํ”„๋ง ๋ถ€ํŠธ ์›น ํ”„๋กœ์ ํŠธ] 10. Spring Boot์™€ Spring Security ์—ฐ๋™ (2)

by oliviarla 2022. 4. 22.
3. ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•œ JPA ์ฒ˜๋ฆฌ
4. ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์œ„ํ•œ UserDetailsService
5. Controller์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด ์ถœ๋ ฅํ•˜๊ธฐ

3. ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•œ JPA ์ฒ˜๋ฆฌ

ClubMember: ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค

ClubMemberRole: ์—”ํ‹ฐํ‹ฐ enum ํด๋ž˜์Šค

ClubMemberRepository: repository ์ธํ„ฐํŽ˜์ด์Šค

ClubAuthMemberDTO: DTO ํด๋ž˜์Šค(์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์˜ User ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•จ)

ClubUserDetailsService: service ํด๋ž˜์Šค, AuthenticationManager๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ํ˜ธ์ถœํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๋Š” ํด๋ž˜์Šค (UserDetailsService ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์†ํ•จ)

 

User: ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํšŒ์›, ๊ณ„์ •์— ๋Œ€ํ•œ ์šฉ์–ด
username: ํšŒ์›์„ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ์‹๋ณ„ ๋ฐ์ดํ„ฐ (์ผ๋ฐ˜์ ์ธ id์™€ ์œ ์‚ฌํ•œ ๊ธฐ๋Šฅ)

 

ClubMember

entity ํŒจํ‚ค์ง€ ๋‚ด์˜ ํด๋ž˜์Šค

addMemberRole ๋ฉ”์†Œ๋“œ๋กœ ์‚ฌ์šฉ์ž์˜ ์—ญํ•  ์ถ”๊ฐ€ ๊ฐ€๋Šฅ

package org.zerock.club.entity;


import lombok.*;

import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import java.util.Set;

@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@ToString
public class ClubMember extends BaseEntity {

    @Id
    private String email;

    private String password;

    private String name;

    private boolean fromSocial;

    @ElementCollection(fetch = FetchType.LAZY)
    private Set<ClubMemberRole> roleSet;

    public void addMemberRole(ClubMemberRole clubMemberRole){
        roleSet.add(clubMemberRole);
    }

}

ClubMemberRole

entity ํŒจํ‚ค์ง€ ๋‚ด์˜ enum ํด๋ž˜์Šค

์‚ฌ์šฉ์ž์˜ Role์„ ์ •์˜ํ•ด๋‘ 

package org.zerock.club.entity;

public enum ClubMemberRole {

    USER,MANAGER,ADMIN

}

ClubMemberRepository

package org.zerock.club.repository;


import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.zerock.club.entity.ClubMember;

import java.util.Optional;

public interface ClubMemberRepository extends JpaRepository<ClubMember, String> {

    @EntityGraph(attributePaths = {"roleSet"}, type = EntityGraph.EntityGraphType.LOAD)
    @Query("select m from ClubMember m where m.fromSocial = :social and m.email =:email")
    Optional<ClubMember> findByEmail(String email, boolean social);
}

์ด๋ฉ”์ผ, ์†Œ์…œ๋กœ ์ถ”๊ฐ€๋œ ํšŒ์› ์—ฌ๋ถ€ ์ž…๋ ฅ๋ฐ›์•„ ์‚ฌ์šฉ์ž ๊ฒ€์ƒ‰

@EntityGraph: left outer join์œผ๋กœ ClubMemberRole๋„ ๋กœ๋”ฉ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•จ

4. ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์œ„ํ•œ UserDetailsService

ClubAuthMemberDTO

UserDetails ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ User ํด๋ž˜์Šค๋ฅผ ์ƒ์†

-> DTO์™€ ๊ฐ™์€ ๊ฐœ๋…์œผ๋กœ ๋ณ„๋„ ํด๋ž˜์Šค(User)๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ํ™œ์šฉํ•˜๋Š” ๋ฐฉ์‹

UserDetails ํƒ€์ž…
- getAuthorities() : ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ€์ง€๋Š” ๊ถŒํ•œ ์ •๋ณด ํš๋“
- getPassword(): ์ธ์ฆ์„ ๋งˆ๋ฌด๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ํŒจ์Šค์›Œ๋“œ ์ •๋ณด ํš๋“
- getUsername(): ์ธ์ฆ์— ํ•„์š”ํ•œ ์•„์ด๋”” ๊ฐ™์€ ์ •๋ณด ํš๋“
- ๊ณ„์ • ๋งŒ๋ฃŒ/์ž ๊น€ ์—ฌ๋ถ€ ํ™•์ธ
package org.zerock.club.security.dto;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.util.Collection;

@Log4j2
@Getter
@Setter
@ToString
public class ClubAuthMemberDTO extends User {


    private String email;

    private String name;

    private boolean fromSocial;

    public ClubAuthMemberDTO(String username, String password, boolean fromSocial,
                             Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
        this.email = username;
        this.fromSocial = fromSocial;
    }

}

 

super(...): ๋ถ€๋ชจ ํด๋ž˜์Šค์ธ User ํด๋ž˜์Šค์˜ ์‚ฌ์šฉ์ž ์ •์˜ ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœ

password๋Š” ๋ถ€๋ชจ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•˜์ง€ ์•Š์Œ

ClubUserDetailsService

UserDetailsService ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ํด๋ž˜์Šค

 

package org.zerock.club.security.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.zerock.club.entity.ClubMember;
import org.zerock.club.repository.ClubMemberRepository;
import org.zerock.club.security.dto.ClubAuthMemberDTO;

import java.util.Optional;
import java.util.stream.Collectors;

@Log4j2
@Service
@RequiredArgsConstructor
public class ClubUserDetailsService  implements UserDetailsService {

    private final ClubMemberRepository clubMemberRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        log.info("ClubUserDetailsService loadUserByUsername " + username);


        Optional<ClubMember> result = clubMemberRepository.findByEmail(username, false);

        if(result.isEmpty()){
            throw new UsernameNotFoundException("Check User Email or from Social ");
        }

        ClubMember clubMember = result.get();

        log.info("-----------------------------");
        log.info(clubMember);

        ClubAuthMemberDTO clubAuthMember = new ClubAuthMemberDTO(
                clubMember.getEmail(),
                clubMember.getPassword(),
                clubMember.isFromSocial(),
                clubMember.getRoleSet().stream()
                        .map(role -> new SimpleGrantedAuthority("ROLE_"+role.name()))
                        .collect(Collectors.toSet())
        );

        clubAuthMember.setName(clubMember.getName());
        clubAuthMember.setFromSocial(clubMember.isFromSocial());

        return clubAuthMember;
    }
}

- ClubMemberRepository๋ฅผ ์ฃผ์ž…๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๋กœ ๋ณ€๊ฒฝ + @RequiredArgsConstructor ์ฒ˜๋ฆฌ

- ClubMemberRepository์˜ findByEmail ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ

- ClubMember๋ฅผ UserDetailsํƒ€์ž…์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ClubAuthMemberDTOํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜

- ClubMemberRole์„ SimpleGrantedAuthority๋กœ ๋ณ€ํ™˜ ์ฒ˜๋ฆฌ

 

5. Controller์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด ์ถœ๋ ฅํ•˜๊ธฐ

SampleController

๋กœ๊ทธ์ธ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•

1) SecurityContextHolder ๊ฐ์ฒด ์‚ฌ์šฉ

2) ์ง์ ‘ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ

SampleController ๋‚ด์˜ exMember ๋ฉ”์†Œ๋“œ

AuthenticationPrincipal ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉ

@GetMapping("/member")
    public void exMember(@AuthenticationPrincipal ClubAuthMemberDTO clubAuthMember){

        log.info("exMember..........");

        log.info("-------------------------------");
        log.info(clubAuthMember);

    }

๋Œ“๊ธ€