::SpringBoot:: Google Social 로그인 임시 구현_2

MinJeongKim·2022년 9월 26일
0

SpringBoot

목록 보기
3/3

Google Social 로그인

OAuth2UserService

1. DefaultOAuth2UserService 클래스를 상속해서 구현
2. 이메일을 이용한 회원가입 처리
// ClubOAuth2UserDetailsService
// import문 생략

@RequiredArgsConstructor
@Log4j2
@Service
public class ClubOAuthUserDetailsService extends DefaultOAuth2UserService {
    private final ClubMemberRepository clubMemberRepository;

    private final PasswordEncoder passwordEncoder;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        log.info("---------------Start---------------");
        log.info("userRequest: " + userRequest);

        // userRequest 정보 확인
        String clientName = userRequest.getClientRegistration().getClientName();

        log.info("clientName: " + clientName);
        log.info(userRequest.getAdditionalParameters());

        OAuth2User oAuth2User = super.loadUser(userRequest);

        log.info("==============================");
        oAuth2User.getAttributes().forEach((k,v) -> {
            log.info(k + ":" + v); // sub, picture, email, email_verified ..등이 출력
        });

        String email = null;

        // 구글 이용하는 경우 chk
        if(clientName.equals("Google")) {
            email = oAuth2User.getAttribute("email");
        }

        log.info("EMAIL: " + email);

        /*
        ClubMember clubMember = saveSocialMember(email);

        return oAuth2User;
        */

        ClubMember clubMember = saveSocialMember(email);

        ClubAuthMemberDTO clubAuthMemberDTO = new ClubAuthMemberDTO(
                clubMember.getEmail(),
                clubMember.getPassword(),
                true,
                clubMember.getRoleSet().stream().map(
                        role -> new SimpleGrantedAuthority("ROLE_" + role.name())

                ).collect(Collectors.toList()),
                oAuth2User.getAttributes()
        );

        clubAuthMemberDTO.setName(clubMember.getName());

        return clubAuthMemberDTO;
    }

    private ClubMember saveSocialMember(String email) {
        // 기존에 동일한 이메일로 가입한 회원이 있는 경우 그대로 조회만..
        Optional<ClubMember> result = clubMemberRepository.findByEmail(email, true);

        if(result.isPresent()) {
            return result.get();
        }

        // 없을 경우 회원 추가, 이름: 이메일 주소 / 패스워드: 1111
        ClubMember clubMember = ClubMember.builder().email(email)
                .name(email)
                .password(passwordEncoder.encode("1111"))
                .fromSocial(true)
                .build();

        clubMember.addMemberRole(ClubMemberRole.USER);

        clubMemberRepository.save(clubMember);

        return clubMember;
    }
}
3. OAuth2User 타입
3-1. OAuth2User 타입을 ClubAuthMemberDTO 타입으로 사용할 수 있도록 DTO 수정
// ClubAuthMemberDTO
// import문 생략

@Log4j2
@Getter
@Setter
@ToString
public class ClubAuthMemberDTO extends User implements OAuth2User {
    /* User 클래스를 상속 후 생성자 호출
    public ClubAuthMemberDTO(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
    }
    */

    // DTO와 유사하게 구성(DTO 역할을 수행하는 클래스이며 Spring Security 인가/인증 작업에 사용할 수 있음)
    private String email;

    private String password;

    private String name;

    private boolean fromSocial;

    private Map<String, Object> attr;

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

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

    @Override
    public Map<String, Object> getAttributes() {
        return this.attr;
    }
}
4. 로그인 성공 이후 처리
4-1. AuthenticationSuccessHandler, AuthenticationFailureHandler
// ClubLoginSuccessHandler
// import문 생략

@Log4j2
public class ClubLoginSuccessHandler implements AuthenticationSuccessHandler {
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    private PasswordEncoder passwordEncoder;

    public ClubLoginSuccessHandler(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        log.info("----------------------------");
        log.info("onAuthenticationSuccess");

        ClubAuthMemberDTO authMember = (ClubAuthMemberDTO)authentication.getPrincipal();

        boolean fromSocial = authMember.isFromSocial();

        log.info("Need Modify Member?" + fromSocial);

        boolean passwordResult = passwordEncoder.matches("1111", authMember.getPassword());

        if(fromSocial && passwordResult) {
            redirectStrategy.sendRedirect(request, response, "/member/modify?from=social");
        }

    }
}
// SecurityConfig
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        /*
        http.authorizeRequests()
                .antMatchers("/sample/all").permitAll()
                .antMatchers("/sample/ex*").permitAll()
                .antMatchers("/sample/member").hasRole("USER");
         */

        http.formLogin(); // 인가 or 인증에 문제 시 로그인 화면 반환
        http.csrf().disable(); // csrf 토큰 비활성화

        http.logout(); // invalidatedHttpSession() deleteCookies() 쿠키나 세션을 무효화 시키는 설정 추가 가능

        // http.oauth2Login();
        // 소셜 로그인 처리 추가
        http.oauth2Login().successHandler(successHandler());

        // remember me 설정(초단위로 설정), 7일, 소셜 로그인은 x
        http.rememberMe().tokenValiditySeconds(60*60*7).userDetailsService(userDetailsService);

        return http.build();
}
    
5. Remember me
5-1. 웹 인증 방식 중에 쿠키를 사용하는 방식
> 한번 로그인한 사용자가 브라우저를 닫은 후에 다시 서비스에 접속해도 별도의 로그인 절차 없이 바로 로그인 처리가 진행 됨
5-2. Remember me 설정
SecurityConfig

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        /*
        http.authorizeRequests()
                .antMatchers("/sample/all").permitAll()
                .antMatchers("/sample/ex*").permitAll()
                .antMatchers("/sample/member").hasRole("USER");
         */

        http.formLogin(); // 인가 or 인증에 문제 시 로그인 화면 반환
        http.csrf().disable(); // csrf 토큰 비활성화

        http.logout(); // invalidatedHttpSession() deleteCookies() 쿠키나 세션을 무효화 시키는 설정 추가 가능

        // http.oauth2Login();
        // 소셜 로그인 처리 추가
        http.oauth2Login().successHandler(successHandler());

        // remember me 설정(초단위로 설정), 7일, 소셜 로그인은 x
        http.rememberMe().tokenValiditySeconds(60*60*7).userDetailsService(userDetailsService);

        return http.build();
}

6. 어노테이션 기반의 접근 제한 설정
6-1. @EnableGlobalMethodSecurity의 적용
- securedEnabled > 예전 버전의 @Secure 어노테이션이 사용 가능한지 지정
- @PreAuthoriuze를 이용하기 위해 prePostEnable 속성을 사용
6-2. @PreAuthorize
- 접근 제한이 필요한 컨트롤러의 메서드에 @PreAuthorize 적용 (value로는 문자열로 된 표현식을 넣어줌, # 기호 사용)
// SecurityConfig

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Log4j2
public class SecurityConfig {
	// 생략
}
// SampleController

 @GetMapping("/admin")
 @PreAuthorize("hasRole('ADMIN')")
 public void exAdmin() {
     log.info("관리자가능..");
 }
 
 @GetMapping("/all")
 @PreAuthorize("permitAll()")
 public void exAll() {
     log.info("전체가능..");
 }
 
 // @PreAuthorize 어노테이션 확인
 @PreAuthorize("#clubAuthMember != null && #clubAuthMember.username eq \"user95@kmj.org\"")
 @GetMapping("/exOnly")
 public String exMemberOnly(@AuthenticationPrincipal ClubAuthMemberDTO clubAuthMember {
     log.info("exMemberOnly");
     log.info("clubAuthMemberDTO: " + clubAuthMember);

     return "/sample/admin";
 }
 
profile
웹 개발자 & DA

0개의 댓글