AdSense

網頁

2019/10/12

Spring Security WebFlux 實作ReactiveUserDetailsService從資料庫取得使用者資料

Spring Security WebFlux從資料庫取得使用者驗證的配置。

範例環境:

  • Windows 64 Bit
  • Java 11
  • Eclipse Version: 2019-03 (4.11.0)
  • Spring Boot版本2.1.6.RELEASE
  • Spring Data MongoDB
  • MongoDB
  • Maven

在Spring Web MVC的Spring Security從資料庫取得驗證用的使用者資訊時,通常會實作UserDetailsService介面並將其註冊為bean ,但由於Spring WebFlux是Reactive Programming,在Spring 5後Spring Security才有支援,並且要改為實作ReactiveUserDetailsService並註冊為bean。

下面是Spring Data MongoDB的實體類別Member

Member

package com.abc.demo.entity;

import java.util.Collection;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.CollectionUtils;

@Document(collection = "members")
public class Member {
    
    @Id
    private String id;
    @Field("name")
    private String name;
    @Field("password")
    private String password;
    @Field("email")
    private String email;
    @Field("roles")
    private Collection<String> roles;
    
    /** 轉自訂的member為Spring Security的UserDetails */
    public UserDetails toUserDetails() {
        return User.withUsername(name)
                .password(password)
                .roles(roles.toArray(String[]::new)) // Java 11 Collection to array
                .build();
    }

}

DemoUserDetailsService實作Spring Security WebFlux的ReactiveUserDetailsService並覆寫findByUsername(String username)方法來取得使用者資料,在此便可透過DAO層,也就是下面的MemberRepo去資料庫(本範例為MongoDB)根據傳入的使用者名稱來查詢使用者資料(UserDetails)並回傳Spring Security WebFlux進行後續的登入驗證。

DemoUserDetailsService

package com.abc.demo.config.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import reactor.core.publisher.Mono;
import com.abc.demo.entity.Member;
import com.abc.demo.repo.MemberRepo;

@Service // 讓Spring @componetScan 註冊為Bean
public class DemoUserDetailsService implements ReactiveUserDetailsService {

    @Autowired
    private MemberRepo memberRepo; // DAO layer to access database
    
    @Override
    public Mono<UserDetails> findByUsername(String username) {
        
        var example = Example.of(Member.builder().name(username).build()); // Query by Example
        return memberRepo.findOne(example)
                .map(e -> Mono.just(e.toUserDetails())) // wrap by Mono
                .orElse(Mono.error(new UsernameNotFoundException("User Not Found")));
    }

}


參考:

沒有留言:

AdSense