Spring Security + RBAC (Role, Authority)

簡易的ですが、Spring Security + RBAC のサンプルを作りました。

kntmr/playground/spring-security-rbac - GitHub

f:id:knt_mr:20210203230209p:plain:w420

今回、User と Role は 1 対 1 にしています。あと、Thymeleaf 側では、Permission (hasAuthority) ではなく、あえて Role (hasRole) を使っているところがあります。RBAC ならすべて Permission に寄せるべきだろうとは思いますが、今回はサンプルなので...。

というわけで、UserDetailsService#loadUserByUsername では、権限と Prefix 付き (ROLE_*) の Role を UserDetails に渡しています。これで hasAuthorityhasRole を使い分ける。

@Transactional
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userRepository.findByName(username);
    return new org.springframework.security.core.userdetails.User(
            user.getName(),
            user.getPassword(),
            AuthorityUtils.createAuthorityList(
                    Stream.concat(
                            Stream.of(user.getRole().nameWithPrefix()), // Prefix を付けて返す
                            user.getRole().getPermissions().stream().map(Permission::getName)).toArray(String[]::new)));
}

WebSecurityConfigurerAdapter#configure はふつう。and() でメソッドチェーンするより分割して書いた方が見やすい気がする。なるほど。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
            .loginPage("/login")
            .usernameParameter("username")
            .passwordParameter("password")
            .defaultSuccessUrl("/", true)
            .permitAll();
    http.logout()
            .logoutUrl("/logout")
            .logoutSuccessUrl("/")
            .permitAll()
            .invalidateHttpSession(true);
    http.authorizeRequests()
            .mvcMatchers("/admin/**").hasAuthority("WRITE")
            .anyRequest().authenticated();
}

参考)