[Spring Security] DaoAuthenticationManager 와 UserDetailsService Test

spring_security

DaoAuthenticationManager 와 UserDetailsService Test #

실습 하기 #

본 코드는 GitHub 페이지에서 확인할 수 있습니다.


기본 세팅 #

web-user-admin #

apply from: "../web-common.gradle"

dependencies {
}

server-login-basic #

...
spring:
  ...
  thymeleaf:
    prefix: classpath:/templates/
    cache: false
    check-template-location: true
dependencies {
    ...
    compile project(":web-user-admin")
}

server-login-userdetails #

apply from: "../web-common.gradle"

dependencies {

    implementation("$boot:spring-boot-starter-data-jpa")

    runtime("com.h2database:h2")
    
    compile project(":comp-user-admin")
    compile project(":web-user-admin")
}

comp-user-admin #

user-domain 에는 UserDetails 를 implement 한 SpUser 객체를 만든다.

public class SpUser implements UserDetails {
    unimplement methods...
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@IdClass(SpAuthority.class)
@Table(name = "sp_user_authority")
public class SpAuthority implements GrantedAuthority {

    @Id
    @Column(name = "user_id")
    private Long userId;

    @Id
    private String authority;
}
dependencies {
    implementation("$boot:spring-boot-starter-data-jpa")
}

다시 SpUser 로 돌아와서

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "sp_user")
public class SpUser implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long userId;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name="user_id"))
    private Set<SpAuthority> authorities;

    private String email;

    private String password;

    private boolean enabled;

    @Override
    public String getUsername() {
        return email;
    }

    @Override
    public boolean isAccountNonExpired() {
        return enabled;
    }

    @Override
    public boolean isAccountNonLocked() {
        return enabled;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return enabled;
    }
}

DB 와 연동하도록 sevice 와 repository directory 를 만들고 객체를 생성한다.

public interface SpUserRepository extends JpaRepository<SpUser, Long> {
    Optional<SpUser> findSpUserByEmail(String email);
}
@Service
@Transactional
public class SpUserService implements UserDetailsService {

    private final SpUserRepository userRepository; // (1)
    public SpUserService(SpUserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userRepository.findSpUserByEmail(username)
            .orElseThrow(() -> new UsernameNotFoundException(username));
    }

    public Optional<SpUser> findUser(String email) {
        return userRepository.findSpUserByEmail(email);
    }

    public SpUser save(SpUser user) {
        return userRepository.save(user);
    }

    public void addAuthority(Long userId, String authority) {
        userRepository.findById(userId).ifPresent(user -> { // find by userId, if not null ->
            SpAuthority newRole = new SpAuthority(user.getUserId(), authority); // make new role
            if (user.getAuthorities() == null) { // if user has no authority
                HashSet<SpAuthority> authorities = new HashSet<>(); // make hash type authorities
                authorities.add(newRole); // add new role
                user.setAuthorities(authorities); // set authority
                save(user);
            } else if (!user.getAuthorities().contains(newRole)) { // else if user doesn't contain newRole
                HashSet<SpAuthority> authorities = new HashSet<>();
                authorities.addAll(user.getAuthorities());
                authorities.add(newRole);
                user.setAuthorities(authorities);
                save(user);
            }
        });
    }
    
    public void removeAuthority(Long userId, String authority) {
        userRepository.findById(userId).ifPresent(user -> { // find by userId, if not null ->
            if (user.getAuthorities() == null) { // if user has no authority -> return
                return;
            }
            SpAuthority targetRole = new SpAuthority(user.getUserId(), authority); // set target role
            if (user.getAuthorities().contains(targetRole)) { // if user has target role
                user.setAuthorities(                          // user get authority which isn't target
                    user.getAuthorities().stream().filter(auth -> !auth.equals(targetRole)).collect(Collectors.toSet())
                );
            }
        });
    }
}

server-login-userdetails #

@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final SpUserService userService; // (1)
    public SecurityConfig(SpUserService userService) {
        this.userService = userService;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService); // (2)
    }

    @Bean
    PasswordEncoder passwordEncoder() { // (3)
        return NoOpPasswordEncoder.getInstance();
    }
    ...
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .requestMatchers(
                        PathRequest.toStaticResources().atCommonLocations(),
                        PathRequest.toH2Console() // (4)
                )
        ;
    }
}

H2 DB를 사용할 것이기 때문에 application.yml 의 설정을 아래와 같이 변경한다.

...
spring:
  ...

  h2:
    console:
      enabled: true
      path: /h2-console

  datasource:
    url: jdbc:h2:mem:userdetails-test;
    driverClassName: org.h2.Driver
    username: sa
    password:

  jpa:
    database-platform: org.hibernate.dialect.H2Dialect

H2 Console #