https://growth-coder.tistory.com/135
이전 포스팅에 이어서 구글로부터 받은 정보를 데이터베이스에 저장하고 권한 설정을 해보려한다.
이전 포스팅에서 jpa 및 데이터베이스 세팅을 해뒀기 때문에 이번 포스팅에서는 코드만 작성하면 된다.
구글로부터 회원 정보를 받으면 DefaultOAuth2UserService를 확장한 클래스의 loadUser 메소드가 실행된다.
즉 회원 정보를 데이터베이스에 저장하는 과정은 이 메소드에서 일어나게 된다.
새로운 Member를 생성하여 데이터베이스에 저장해야하기 때문에 생성자를 만들어준다.
생성자는 Builder 패턴을 사용한다.
@Entity
@NoArgsConstructor
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; //기본키
private String name; //유저 이름
private String password; //유저 비밀번호
private String email; //유저 구글 이메일
private String role; //유저 권한 (일반 유저, 관리지ㅏ)
private String provider; //공급자 (google, facebook ...)
private String providerId; //공급 아이디
@Builder
public Member(String name, String password, String email, String role, String provider, String providerId) {
this.name = name;
this.password = password;
this.email = email;
this.role = role;
this.provider = provider;
this.providerId = providerId;
}
}
OAuth2User를 사용해도 되지만 굳이 새로운 클래스를 만드는 이유는 OAuth2User에는 멤버 엔티티가 존재하지 않기 때문이다.
그래서 내부에 멤버 엔티티를 가지는 PrincipalDetails 클래스를 만드는 것이다.
@Getter
public class PrincipalDetails implements OAuth2User {
private Member member;
private Map<String, Object> attributes;
public PrincipalDetails(Member member) {
this.member=member;
}
public PrincipalDetails(Member member, Map<String, Object> attributes) {
this.member=member;
this.attributes=attributes;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collect = new ArrayList<>();
collect.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return member.getRole();
}
});
return collect;
}
@Override
public String getName() {
return "name";
}
}
OAuth2로 로그인하는 것이 아니라 직접 로그인을 구현할 때는 UserDetails를 구현해야 한다.
만약 OAuth2도 사용하고 직접 로그인도 사용한다면 UserDetails와 OAuth2User를 동시에 구현하면 된다.
ex) public class PrincipalDetails extends UserDetails, OAuth2User {...}
BcryptPasswordEncoder를 빈으로 등록한다.
@Configuration 어노테이션이 붙은 곳에서 등록해도 되지만 간단하게 Application에서 등록을 했다.
@SpringBootApplication
public class Oauth2GoogleApplication {
public static void main(String[] args) {
SpringApplication.run(Oauth2GoogleApplication.class, args);
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
빈으로 등록한 encoder를 loadUser 메소드에서 사용한다.
@Service
@RequiredArgsConstructor
public class OAuth2MemberService extends DefaultOAuth2UserService {
private final BCryptPasswordEncoder encoder;
private final MemberRepository memberRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);
String provider = userRequest.getClientRegistration().getClientId();
String providerId = oAuth2User.getAttribute("sub");
String username = provider + "_" + providerId; //중복이 발생하지 않도록 provider와 providerId를 조합
String email = oAuth2User.getAttribute("email");
String role = "ROLE_USER"; //일반 유저
Optional<Member> findMember = memberRepository.findByName(username);
if (findMember.isEmpty()) { //찾지 못했다면
Member member = Member.builder()
.name(username)
.email(email)
.password(encoder.encode("password"))
.role(role)
.provider(provider)
.providerId(providerId).build();
memberRepository.save(member);
}
return oAuth2User;
}
}
username을 provider와 providerId를 조합해서 만들면 이 username이 존재하는지 확인해야한다.
username을 검색해서 존재하지 않는다면 데이터베이스에 저장하고 존재한다면 저장하지 않는다.
그리고 이전 포스팅에서 관리자만 접근 가능한 페이지를 만들었는데 관리자만 접근을 할 수 있도록 설정을 하지 않아서 SecurityConfig에 해당 설정을 해준다.
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig {
private final OAuth2MemberService oAuth2MemberService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{
return httpSecurity
.httpBasic().disable()
.csrf().disable()
.cors().and()
.authorizeRequests()
.requestMatchers("/private/**").authenticated() //private로 시작하는 uri는 로그인 필수
.requestMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") //admin으로 시작하는 uri는 관릴자 계정만 접근 가능
.anyRequest().permitAll() //나머지 uri는 모든 접근 허용
.and().oauth2Login()
.loginPage("/loginForm") //로그인이 필요한데 로그인을 하지 않았다면 이동할 uri 설정
.defaultSuccessUrl("/") //OAuth 구글 로그인이 성공하면 이동할 uri 설정
.userInfoEndpoint()//로그인 완료 후 회원 정보 받기
.userService(oAuth2MemberService).and().and().build(); //
}
}
보다시피 admin 하위 경로는 role이 "ROLE_ADMIN"인 유저만 접근할 수 있다.
우선은 데이터베이스에 유저 정보를 저장할 때 무조건 "ROLE_USER"를 저장하도록 구현했는데 이 유저로 로그인하면 private는 접근 가능해야하고 admin은 접근할 수 없어야한다.
이후 데이터베이스에 저장된 유저 정보를 삭제하고 loadUser 메소드에서 "ROLE_USER"가 아닌 "ROLE_ADMIN"으로 바꿔서 구글 로그인을 통해 데이터베이스에 유저 정보를 저장하면 admin 경로에 접근이 가능하다.
참고
'공부 > Spring' 카테고리의 다른 글
[Spring] 스프링 OAuth2 네이버 로그인 (OAuth2 스프링 4편) (0) | 2023.04.01 |
---|---|
[Spring] 스프링 OAuth2 페이스북 로그인 (OAuth2 스프링 3편) (6) | 2023.03.30 |
[Spring] 스프링 Oauth2 구글 로그인과 jpa 사용하여 유저 정보 데이터베이스에 저장 (OAuth2 스프링 1편) (0) | 2023.03.23 |
[Spring][인프런 스프링 MVC] 파일 업로드 및 저장 방법 (0) | 2023.03.20 |
[Spring][인프런 스프링 MVC] 필터와 인터셉터의 차이 (0) | 2023.03.12 |
댓글