Oauth2.0 Authentication객체가 가질 수 있는 2가지 타입
ClientRegistration : 로그인과 관련된 서버의 정보가 담겨있다.
어떤 Oauth로 로그인했는지 확인이 가능하다.
IndexController
package com.example.security1.controller;
import com.example.security1.auth.PrincipalDetails;
import com.example.security1.model.User;
import com.example.security1.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class IndexController {
@Autowired
private UserRepository userRepository;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@GetMapping("/test/login")
public @ResponseBody String testLogin(
Authentication authentication,
@AuthenticationPrincipal PrincipalDetails userDetails) { //DI의존성 주입
System.out.println("/test/login =====================");
//Object타입을 다운캐스팅하여 정보를 가져온다.
PrincipalDetails principalsDetails = (PrincipalDetails) authentication.getPrincipal();
System.out.println("authentication" + principalsDetails.getUser());
//@AuthenticationPrincipal을 통해서 getUser를 할수 있다.
System.out.println("userDetails = " + userDetails.getUser());
return "세션 정보 확인하기";
}
@GetMapping({"","/"})
public String index() {
return "index";
}
@GetMapping("/user")
public @ResponseBody String user() {
return "user";
}
@GetMapping("/admin")
public @ResponseBody String admin() {
return "admin";
}
@GetMapping("/manager")
public @ResponseBody String manager() {
return "manager";
}
@GetMapping("/loginForm")
public String login() {
return "loginForm";
}
@GetMapping("joinForm")
public String joinForm() {
return "joinForm";
}
@PostMapping("/join")
public String join(User user) {
user.setRole("ROLE_USER");
System.out.println(user);
String rawPassword = user.getPassword();
String encPassword = bCryptPasswordEncoder.encode(rawPassword);
user.setPassword(encPassword);
userRepository.save(user);
return "redirect:/loginForm";
}
@Secured("ROLE_ADMIN")
@GetMapping("/info")
public @ResponseBody String info() {
return "개인정보";
}
@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
@GetMapping("/data")
public @ResponseBody String data() {
return "데이터정보";
}
}
세션정보를 확인해보기 위해 테스트 메서드를 작성하였다.
/test/login =====================
authenticationUser(id=1, username=user1, password=$2a$10$Q8mcbqq.JZMFtKI7bKVB3Oz4u21x/n3Q9HW9pZ.J4JsAqpUYCP2C6, email=user1@naver.com, role=ROLE_USER, provider=null, providerId=null, createDate=2023-05-26 20:40:11.473)
userDetails = User(id=1, username=user1, password=$2a$10$Q8mcbqq.JZMFtKI7bKVB3Oz4u21x/n3Q9HW9pZ.J4JsAqpUYCP2C6, email=user1@naver.com, role=ROLE_USER, provider=null, providerId=null, createDate=2023-05-26 20:40:11.473)
둘 다 같은 정보를 가지고 있다.
둘 다 user의 정보에 접근이 가능하다.
둘은 다운캐스팅의 유무의 차이가 있다.
testLogin을 UserDetails로 바꿔서 확인해 보기
@GetMapping("/test/login")
public @ResponseBody String testLogin(
Authentication authentication,
@AuthenticationPrincipal UserDetails userDetails) { //DI의존성 주입
System.out.println("/test/login =====================");
PrincipalDetails principalsDetails = (PrincipalDetails) authentication.getPrincipal();
System.out.println("authentication" + principalsDetails.getUser());
System.out.println("userDetails = " + userDetails.getUsername());
return "세션 정보 확인하기";
}
결과
/test/login =====================
authenticationUser(id=1, username=user1, password=$2a$10$Q8mcbqq.JZMFtKI7bKVB3Oz4u21x/n3Q9HW9pZ.J4JsAqpUYCP2C6, email=user1@naver.com, role=ROLE_USER, provider=null, providerId=null, createDate=2023-05-26 20:40:11.473)
userDetails = user1
userDetails를 바로 프린트해 보기
@GetMapping("/test/login")
public @ResponseBody String testLogin(
Authentication authentication,
@AuthenticationPrincipal UserDetails userDetails) { //DI의존성 주입
System.out.println("/test/login =====================");
PrincipalDetails principalsDetails = (PrincipalDetails) authentication.getPrincipal();
System.out.println("authentication" + principalsDetails.getUser());
System.out.println("userDetails = " + userDetails);
return "세션 정보 확인하기";
}
결과
authenticationUser(id=1, username=user1, password=$2a$10$Q8mcbqq.JZMFtKI7bKVB3Oz4u21x/n3Q9HW9pZ.J4JsAqpUYCP2C6, email=user1@naver.com, role=ROLE_USER, provider=null, providerId=null, createDate=2023-05-26 20:40:11.473)
userDetails = PrincipalDetails(user=User(id=1, username=user1, password=$2a$10$Q8mcbqq.JZMFtKI7bKVB3Oz4u21x/n3Q9HW9pZ.J4JsAqpUYCP2C6, email=user1@naver.com, role=ROLE_USER, provider=null, providerId=null, createDate=2023-05-26 20:40:11.473))
testOauthLogin 메서드 추가
@GetMapping("/test/oauth/login")
public @ResponseBody String testOauthLogin(
Authentication authentication) { //DI의존성 주입
System.out.println("/test/oauth/login =====================");
//Object타입을 다운캐스팅하여 정보를 가져온다.
OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();
System.out.println("authentication" + oAuth2User.getAttributes());
return "Oauth 세션 정보 확인하기";
}
Oauth2로 로그인한 정보는 PrincipalDetails로 캐스팅이 되지 않아
함수를 추가적으로 작성해 주어야 한다.
결과
getAttributes : {sub=000000000000000000000, name=xxx, given_name=xx, family_name=x, picture=https://lh3.googleusercontent.com/a/AAcHTtcU7e8jEeWSfsxjs8_sxp_U1XoKdWQ5HJFQNPqt=s96-c, email=xxx@gmail.com, email_verified=true, locale=ko}
/test/oauth/login =====================
authentication{sub=000000000000000000000, name=xxx, given_name=xx, family_name=x, picture=https://lh3.googleusercontent.com/a/AAcHTtcU7e8jEeWSfsxjs8_sxp_U1XoKdWQ5HJFQNPqt=s96-c, email=xxx@gmail.com, email_verified=true, locale=ko}
PrincipalOauth2UserService에서 loadUser의
getAttributes 정보와 일치한다.
@AuthenticationPrincipal Oauth2User oauth 추가
@GetMapping("/test/oauth/login")
public @ResponseBody String testOauthLogin(
Authentication authentication,
@AuthenticationPrincipal OAuth2User oauth) { //DI의존성 주입
System.out.println("/test/oauth/login =====================");
//Object타입을 다운캐스팅하여 정보를 가져온다.
OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();
System.out.println("authentication" + oAuth2User.getAttributes());
System.out.println("OAuth2User : " + oauth.getAttributes());
return "Oauth 세션 정보 확인하기";
}
결과
getAttributes : {sub=000000000000000000000, name=xxx, given_name=xx, family_name=x, picture=https://lh3.googleusercontent.com/a/AAcHTtcU7e8jEeWSfsxjs8_sxp_U1XoKdWQ5HJFQNPqt=s96-c, email=xxx@gmail.com, email_verified=true, locale=ko}
/test/oauth/login =====================
authentication{sub=000000000000000000000, name=xxx, given_name=xx, family_name=x, picture=https://lh3.googleusercontent.com/a/AAcHTtcU7e8jEeWSfsxjs8_sxp_U1XoKdWQ5HJFQNPqt=s96-c, email=xxx@gmail.com, email_verified=true, locale=ko}
OAuth2User : {sub=000000000000000000000, name=xxx, given_name=xx, family_name=x, picture=https://lh3.googleusercontent.com/a/AAcHTtcU7e8jEeWSfsxjs8_sxp_U1XoKdWQ5HJFQNPqt=s96-c, email=xxx@gmail.com, email_verified=true, locale=ko}
3개의 결과가 같다.
따라서 Authentication 또는 @AuthenticationPrincipal 어노테이션으로
user정보에 접근이 가능하다.
차이는 (Oauth2User)로 다운캐스팅 유무이다.
결론
SpringSecuirty 세션 안에
Authentication 객체만 있다.
하지만 Authentication객체 안에는
UserDetails와 Oauth2User가 들어갈 수 있다.
Authentication 객체 안에 UserDetails 타입이 들어가는 경우 : 일반 로그인
Authentication 객체 안에 Oauth2User 타입이 들어가는 경우 : 소셜 로그인
PrincipalDetails는 UserDetails로 부터 구현을 하고 있어 UserDetails 타입도 가능하다.
여기에 Oauth2User도 같이 implements를 시킨다면
PrincipalDetails 하나로 타입이 다른 두 가지의 로그인 경우를 통합할 수 있다.
package com.example.security1.auth;
import com.example.security1.model.User;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
@Data
public class PrincipalDetails implements UserDetails, OAuth2User {
private User user;
public PrincipalDetails(User user) {
this.user = user;
}
@Override
public Map<String, Object> getAttributes() {
return null;
}
@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;
}
@Override
public String getName() {
return null;
}
}
implements UserDetails -> implements UserDetails, OAuth2User로 변경
이에 따라 구현해야 하는 메서드가 2개 추가되었다.
'자바 탐구' 카테고리의 다른 글
스프링) SpringSecurity - 10) Oauth2.0 페이스북 로그인 완료 (1) | 2023.05.26 |
---|---|
스프링) SpringSecurity - 9) Oauth2.0 구글 로그인 및 자동 회원 가입 진행 완료 (0) | 2023.05.26 |
스프링) SpringSecurity - 7) Oauth2.0 구글 회원 프로필 정보 받아보기 (0) | 2023.05.26 |
스프링) SpringSecurity - 6) Oauth2.0 구글 로그인 준비 (0) | 2023.05.26 |
스프링) SpringSecurity - 5) Security 권한 처리 (0) | 2023.05.26 |