2.스프링 시큐리티 [OAuth 로그인 -3]

dasd412·2022년 1월 31일
0

포트폴리오

목록 보기
16/41

Authentication 사용하기

스프링 시큐리티는 인증된 정보를 Authentication으로 감싸서 Security Context Holder에 보관해 놓는다.


일반 로그인 유저 세션 정보 얻기

아래와 같이 메서드 파라미터로 Authentication을 받거나, @AuthenticationPrincipal 을 부착한

UserDetails 구현체를 받으면(PrincipalDetails implements UserDetails) 로그인한 유저의 세션 정보를 얻을 수 있다.

참고로 Authentication 객체의 getPrincipal()의 리턴 타입은 Object다.

@Controller
public class IndexController {
	@GetMapping("/test/login")
    public @ResponseBody
    String testLogin(Authentication authentication, @AuthenticationPrincipal PrincipalDetails userDetails) {
        System.out.println("/test/login======");
        PrincipalDetails principalDetails = (PrincipalDetails) authentication.getPrincipal();
        System.out.println("authentication : " + principalDetails.getUser());
        System.out.println("userDetails:" + userDetails.getUser());
        return "세션 정보 확인하기";
    }

}

로그를 보면, 결과가 똑같다.

authentication : User(id=1, username=ssar, password=$2a$10$2qissCjGbv8EBCpbp.bfyusrCakaSgeOLnzDTDuHTTpexp5FUJIxq, email=ssar@naver.com, role=ROLE_USER, provider=null, providerId=null, createDate=2021-12-30 11:11:59.0)
userDetails:User(id=1, username=ssar, password=$2a$10$2qissCjGbv8EBCpbp.bfyusrCakaSgeOLnzDTDuHTTpexp5FUJIxq, email=ssar@naver.com, role=ROLE_USER, provider=null, providerId=null, createDate=2021-12-30 11:11:59.0)

OAuth 로그인 유저의 세션 정보 얻기

OAuth 로그인 유저의 세션 정보를 얻는 방법은 약간 다르다.

OAuth 로그인 유저의 경우 UserDetails 구현체로 다운 캐스팅하면 에러가 난다.

대신, OAuth2User로 다운 캐스팅해야 에러가 발생하지 않고 세션 정보를 얻을 수 있다.

이 역시 로그를 보면 결과가 같다. (보안상 이유로 생략)

@Controller
public class IndexController {
		@GetMapping("/test/oauth/login")
    public @ResponseBody
    String testOauthLogin(Authentication authentication, @AuthenticationPrincipal OAuth2User oauth) {
        System.out.println("/test/login======");
        OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal(); // Oauth 의 경우엔 OAuth2User 로 다운 캐스팅해야 에러 안난다.
        System.out.println("authentication : " + oAuth2User.getAttributes());
        System.out.println("oauth : "+oauth.getAttributes());
        return "oauth 세션 정보 확인하기";
    }
}

Authentication 객체가 가질 수 있는 타입 2가지

인증 정보를 담는 Authentication은 UserDetails 구현체 또는 OAuth2User 구현체를 가질 수 있다.

전자의 경우 일반 로그인 유저의 인증 정보를 담고, 후자의 경우는 OAuth 로그인 유저의 인증정보를 담는다.

하지만 이로 발생할 수 있는 문제가 있다.

로그인 방법마다 인증 정보를 담는 객체의 타입이 다르다면, 컨트롤러의 메서드마다 두 구현체를 파라미터로 받아야 할까?

다음과 같이 로직은 똑같은데, 로그인 방법에 따라 파라미터를 달리 해야한다면 유지보수 상에 엄청난 비용이 따른다.

public doSomething(@AuthenticationPrincipal UserDetials userDetails){}
public doSomething(@AuthenticationPrincipal OAuth2User oauth){}

해결책
UserDetials 와 OAuth2User를 모두 implements 한 클래스를 만들고, 이를 사용하면 된다.

public class PrincipalDetails implements UserDetails, OAuth2User {

    private final User user;//합성을 이용
    private Map<String,Object>attributes;

    //일반 로그인 시 사용되는 생성자
    public PrincipalDetails(User user) {
        this.user = user;
    }

    //Oauth 로그인 시 사용되는 생성자.
    public PrincipalDetails(User user, Map<String, Object> attributes) {
        this.user = user;
        this.attributes = attributes;
    }

    //해당 User 의 권한을 리턴하는 메서드
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> collect = new ArrayList<>();

        collect.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                return user.getRole();
            }
        });

        return collect;
    }


    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    //비밀 번호 만기 여부
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    //계정 활성화 여부.
    //휴면 계정 전환할 때 쓰임.
    @Override
    public boolean isEnabled() {
        return true;
    }

    //아래부터 Oauth 관련 메서드. OAuth2User 오버라이드 메서드임.

    @Override
    public Map<String, Object> getAttributes() {
        return attributes;
    }

    //Oauth 의 기본키 getter 메서드. 잘 안씀.
    @Override
    public String getName() {
        return null;
    }
}

이렇게 하면, 로그인 방식에 따라 메소드를 여러개 만들 필요가 없어진다. 예를 들면 다음과 같다.

    @GetMapping("/user")
    public @ResponseBody
    String user(@AuthenticationPrincipal PrincipalDetails principalDetails) {
        return "user";
    }
profile
아키텍쳐 설계와 테스트 코드에 관심이 많음.

0개의 댓글