Spring Boot Outh 2認証サーバアプリケーション-5


今回は、DBで会員情報を検索して登録してみます.
まずメンバーエンティティを作成する必要があります.

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;
    }
}
私が作っているTOYプランのメンバーEntity
セキュリティには必要な情報が含まれている必要がありますので、UserDetailsを継承し、関連フィールドを超えてください.
JPAを使用してRepositoryを作成します.

MemberRepository

@Repository
public interface MemberRepository extends JpaRepository<Member, String> {
}
JPA Repositoryを継承して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でテストユーザーを設定します.
PasswordEncoderを使用するので、パスワードはenocde形式で挿入されます.
UserDetailsのloadUserByUserNameメソッドを上書きします.
認証プロバイダを追加して、後続のログイン情報を検証します.

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
        );
    }
}
では、Providerを使用するためにSecurityを変更します.

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を使用している部分です
すべての設定が完了し、先ほど作成したユーザー情報でトークンを受信します.
http://localhost:1995/oauth/authorize?client_id=foo&redirect_uri=http://localhost:1995/oauth2/callback&response_type=code&scope=read
接続後に作成したユーザー情報を使用してログインすると、トークンが発行されていれば成功します.