이번에는 회원정보를 DB에서 조회하여 로그인 할 수 있도록 해보겠습니다.
먼저 회원 Entity를 만들어야 합니다.
@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를 만들어 봅시다.
@Repository
public interface MemberRepository extends JpaRepository<Member, String> {
}
JPARepository를 상속받아 Repository를 생성합니다.
이제 MemberService, MemberServiceImpl를 생성해봅시다.
public interface MemberService extends UserDetailsService {
}
UserDetailsService를 상속받아 줍시다.
@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를 추가합니다.
@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를 사용하도록 수정해봅시다.
@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정보로 토큰을 받아보겠습니다.
로 접속후 생성한 User정보로 로그인했을때 토큰이 발급되면 성공입니다.