Spring Boot OAuth2 인증서버 적용기 - 5

황남욱·2021년 12월 17일
0
post-thumbnail

이번에는 회원정보를 DB에서 조회하여 로그인 할 수 있도록 해보겠습니다.

먼저 회원 Entity를 만들어야 합니다.

Member.java


@Builder
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "MEMBER")
public class Member implements UserDetails {

    @Id
    @Column(name="USER_ID")
    private String userId;

    @Column(name="USER_PWD")
    private String userPwd;

    @Column(name="USER_NM")
    private String userNm;

    private String gender;

    private Integer height;

    private Integer weight;

    @Column(name="PRIVATE_YN")
    private String privateYn;

    private Instant regdate;

    @ElementCollection(fetch = FetchType.EAGER)
    @Builder.Default
    private List<String> roles = new ArrayList<>();

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
    }

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public String getUsername() {
        return this.userId;
    }

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public String getPassword() {
        return this.userPwd;
    }

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public boolean isEnabled() {
        return true;
    }
}

제가 만들고있는 토이프로젝트의 Member Entity입니다.
Security에 필요한 정보를 담아야 하므로 UserDetails 를 상속받아 관련 필드를 Override합니다.

JPA를 사용할 예정이므로 Repository를 만들어 봅시다.

MemberRepository

@Repository
public interface MemberRepository extends JpaRepository<Member, String> {
}

JPARepository를 상속받아 Repository를 생성합니다.

이제 MemberService, MemberServiceImpl를 생성해봅시다.

MemberService

public interface MemberService extends UserDetailsService {
}

UserDetailsService를 상속받아 줍시다.

MemberServiceImpl


@Service
public class MemberServiceImpl implements MemberService {

    @Autowired
    private MemberRepository memberRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;
    private final AccountStatusUserDetailsChecker detailsChecker = new AccountStatusUserDetailsChecker();

    @PostConstruct
    public void init() {
        memberRepository.save(Member.builder()
                .userId("test")
                .userPwd(passwordEncoder.encode("test"))
                .userNm("테스트")
                .gender("M")
                .height(180)
                .weight(80)
                .privateYn("Y")
                .regdate(Instant.now())
                .roles(Collections.singletonList("ROLE_USER"))
                .build()
        );
    }

    @Override
    public UserDetails loadUserByUsername(String userId) {
        Member member = memberRepository.findById(userId).orElseThrow(() -> new UsernameNotFoundException("user is not exists"));
        detailsChecker.check(member);
        return member;
    }
}

@PostConstruct를 통해 테스트용 User를 셋팅해줍니다.
PasswordEncoder를 사용할 예정이므로 비밀번호는 enocde해서 넣어줍니다.

UserDetails에 있는 loadUserByUserName메소드를 오버라이딩 해줍니다.

이후 로그인 정보를 Validation 하기 위해 AuthenticationProvider를 추가합니다.

OAuth2Provider


@Slf4j
@Component
public class OAuthProvider implements AuthenticationProvider {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private MemberRepository memberRepository;

    @Override
    public Authentication authenticate(Authentication authentication) {
        String name = authentication.getName();
        String password = authentication.getCredentials().toString();

        Member member = memberRepository.findById(name)
                .orElseThrow(() -> new UsernameNotFoundException("user is not exists"));

        if(!passwordEncoder.matches(password, member.getPassword()))
            throw new BadCredentialsException("password is not valid");

        return new UsernamePasswordAuthenticationToken(name, password, member.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(
                UsernamePasswordAuthenticationToken.class
        );
    }
}

이제 Security 에서 Provider를 사용하도록 수정해봅시다.

SecurityConfig


@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private OAuthProvider oAuthProvider;

//    @Override
//    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth.inMemoryAuthentication()
//                .withUser("user")
//                .password("{bcrypt}$2a$10$Dp7dXcuT5cGW9clQRfJKIe22EVV7rNCjntXWBE6f0e8nPuu6GlRq6")
//                .roles("USER");
//    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable()
                .headers().frameOptions().disable()
                .and()
                .authorizeRequests()
                .antMatchers(
                        "/oauth/**",
                        "/oauth2/callback",
                        "/h2/**"
                ).permitAll()
                .and()
                .formLogin().and()
                .authenticationProvider(oAuthProvider)
                .httpBasic();
    }
}

authenticationProvider(oAuthProvider) 이 부분이 Provider를 사용하겠다고 하는 부분입니다.

모든 설정이 완료되었습니다 아까 생성한 User정보로 토큰을 받아보겠습니다.

http://localhost:1995/oauth/authorize?client_id=foo&redirect_uri=http://localhost:1995/oauth2/callback&response_type=code&scope=read

로 접속후 생성한 User정보로 로그인했을때 토큰이 발급되면 성공입니다.

profile
안녕하세요👋 주니어 백엔드 개발자입니다.

0개의 댓글