LoginSignup
18
23

More than 1 year has passed since last update.

Spring SecurityでRBACなサンプルを作ってみた

Last updated at Posted at 2018-07-17

@kawasimaさんが以前に書かれていた記事「業務システムにおけるロールベースアクセス制御」で公開されていたサンプルコードを、Spring Security + Spring MVC + Thymeleafで作成してみました(Spring Boot 2も利用してます)。

コード -> https://github.com/MasatoshiTada/rbac-example-springsecurity

この記事を読む前に、上記@kawasimaさんのブログを先に読んでおくことをおすすめします。

Spring SecurityのJava Config作成

src/main/java/com/example/security/SecurityConfig.java
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.formLogin(formLogin -> formLogin
                // configure login
                .loginPage("/login")
                .usernameParameter("account")
                .passwordParameter("password")
                .defaultSuccessUrl("/", true)
                .permitAll()
        ).logout(logout -> logout
                // configure logout
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login")
                .permitAll()
                .invalidateHttpSession(true)
        ).authorizeHttpRequests(authorize -> authorize
                // configure URL authorization
                .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
                .mvcMatchers("/signup").permitAll()
                .mvcMatchers(HttpMethod.GET, "/issues/").hasAuthority("readIssue")
                .mvcMatchers(HttpMethod.GET, "/issue/new").hasAuthority("writeIssue")
                .mvcMatchers(HttpMethod.POST, "/issues/").hasAuthority("writeIssue")
                .mvcMatchers("/users").hasAuthority("manageUser")
                .anyRequest().authenticated()
        );
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

ロールではなくパーミッションをhasAuthority()でチェックしているのがポイントです。
@kimullaaさんのブログ「SpringSecurity 権限に基づいた認可をする」を参考にしました。

ビジネスロジッククラス

src/main/java/com/example/services/IssueService.java
@Service
public class IssueService {

    // フィールド等省略

    @PreAuthorize("hasAuthority('readIssue')")
    @Transactional(readOnly = true)
    public List<Issue> findAll() {
        return issueRepository.findAll();
    }

    @PreAuthorize("hasAuthority('writeIssue')")
    @Transactional
    public void register(Issue issue, String account) {
        issueRepository.register(issue, account);
    }
}
src/main/java/com/example/services/UserService.java
@Service
public class UserService {

    // フィールド等省略

    @Transactional
    public void register(User user, String roleName) {
        String encodedPassword = passwordEncoder.encode(user.getPassword());
        user.setPassword(encodedPassword);
        userRepository.register(user, roleName);
    }

    @PreAuthorize("hasAuthority('manageUser')")
    @Transactional(readOnly = true)
    public List<User> findAll() {
        return userRepository.findAll();
    }
}

@PreAuthorize("hasAuthority('パーミッション名')")で権限チェックをしています。やはりここでも、ロール名ではなくパーミッション名で指定しています。

ビュー

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
    <!--/* 省略 */-->
</head>
<body>
<div class="ui fixed inverted menu">
    <div class="ui container">
        <a href="#" class="header item">
            RBAC Example
        </a>
        <a th:href="@{/}" href="../index.html" class="item"
           sec:authorize="isAuthenticated()">
            Home
        </a>
        <a th:href="@{/issues/}" href="../issue/list.html" class="item"
           sec:authorize="isAuthenticated() and hasAuthority('readIssue')">
            Issue
        </a>
        <a th:href="@{/users/}" href="../userAdmin/list.html" class="item"
           sec:authorize="isAuthenticated() and hasAuthority('manageUser')">
            Users
        </a>

    <!--/* 省略 */-->

sec:authorize属性とhasAuthority('パーミッション名')で権限チェックをしています。やはりここでも、ロール名ではなくパーミッション名で指定しています。

ロールをハードコーディングしない!

あるロールができることが増える(=あるロールが持つパーミッションが増える)ことって、けっこう頻繁に発生すると思います。例えば、guestロールでもIssueの書き込みができるようにしたい、とか。

そのような変更が発生した場合、ロールでチェックしているとソースコードへの修正が必要になります。

しかし、ソースコード内ではパーミッションでチェックしておけば、データベース側にレコードを追加するだけで済みます。

18
23
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
23