概要
Spring Security を使って Web アプリケーションに認証・認可を導入する際、重要となってくる機能の一つがロール(役割)とアクセス制御のになります。
この記事では、ROLE_ADMIN や ROLE_USER の使い方を中心に、Spring Security における権限設計の基礎を解説します。
実装
導入部分は以前の記事にて解説しておりますので気になる方はそちらを参照いただければと思います。
【Spring Framework】Spring Securityを使用したログイン機能の実装
SecurityConfigの実装
まずは認証周りの設定を行うSecurityConfigを実装します。
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers( "/login").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN") // ROLE_ADMIN が必要
.requestMatchers("/user/**").hasAnyRole("USER") // ROLE_USER が必要
.anyRequest().access(authorizationManager())
)
.formLogin(formLogin -> formLogin
.loginPage("/login") // 共通ログインページでもOK
.defaultSuccessUrl("/")
.successHandler(loginHandler)
.permitAll()
)
.rememberMe(Customizer.withDefaults());
return http.build();
}
解説
-
/admin/**: -
.hasRole("ADMIN"):
ログインユーザーに
requestMatchers() を使って「どのURLか」を定義し、hasRoleで権限を指定します。
つまりこの部分では
/admin以下すべてのURL(例:/admin, /admin/dashboard, /admin/settings)に対してADMINが付与されていればアクセス可能という設定になります。
UserDetailsの実装
UserDetailsとはSpring Securityがユーザー情報(ログイン名、パスワード、ロールなど)を扱うためのインターフェースです。
このクラスでログイン時にロールを割り当てます。
package myapp.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import myapp.entity.User;
import myapp.repository.UserRepository;
@Service
public class LoginUserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUserLoginId(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
String role = "";
if (user.getRole().equals("1")) {
role = "ADMIN";
} else if (user.getRole().equals("2")) {
role = "USER";
}
UserDetails userDetails = org.springframework.security.core.userdetails.User.builder()
.username(user.getUserLoginId())
.password(user.getPassword())
.roles(role)
.build();
return userDetails;
}
}
解説
ユーザーはローカルのDBから取得しています。
userテーブルのカラムroleが「1」の時はADMIN、「2」の時はUSERと変数に代入し、
.roles(role)の部分でログインユーザーにADMINかUSERかのロールを設定しています。
user_id user_login_id password user_name role
-------- -------------- ------------ ---------- ------
1 BBB password アドミン 1
2 CCC password ユーザー 2
ADMINユーザーでログインし画面遷移する際の動作は以下のようになります。
アクセス権は以下のようになります。
| ロール | アクセス /admin/*
|
アクセス /user/*
|
|---|---|---|
| ADMIN | ✅ OK | ❌ 403 |
| USER | ❌ 403 | ✅ OK |
| ログインなし | ❌ 403 | ❌ 403 |
また、Controller単位でロールを割り当てることも可能なようです。
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true) // ← これが必要
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers( "/login").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN", "ADMIN2"))// ADMIN2を追加
.requestMatchers("/user/**").hasAnyRole("USER")
.anyRequest().access(authorizationManager())
)
.formLogin(formLogin -> formLogin
.loginPage("/login") // 共通ログインページでもOK
.defaultSuccessUrl("/")
.successHandler(loginHandler)
.permitAll()
)
.rememberMe(Customizer.withDefaults());
return http.build();
}
}
package myapp.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import myapp.service.SessionService;
@Controller
public class AdminHomeController {
@Autowired
private SessionService sessionService;
// ADMIN権限でアクセス可能
@GetMapping("/admin")
public String home(Model model) {
model.addAttribute("name", sessionService.getUserName());
return "adminHome";
}
// ADMIN2権限でアクセス可能
@GetMapping("/admin/home2")
@PreAuthorize("hasRole('ADMIN2')")
public String home2(Model model) {
model.addAttribute("name", sessionService.getUserName());
return "adminHome2";
}
}
ポイントはメソッドについている@PreAuthorize("hasRole('ADMIN2')")アノテーションです。
これによりこのメソッドの呼び出しにはADMIN2ロールが必要となります。
Spring Securityのロール管理は仕組みがわかると意外とシンプルで使いやすいと感じました。
アクセス制御を柔軟に行えるので、アプリのセキュリティ設計がとてもやりやすくなります。

