1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Spring Framework】Spring Securityでユーザー権限管理を行う方法

Posted at

概要

Spring Security を使って Web アプリケーションに認証・認可を導入する際、重要となってくる機能の一つがロール(役割)とアクセス制御のになります。

この記事では、ROLE_ADMIN や ROLE_USER の使い方を中心に、Spring Security における権限設計の基礎を解説します。

実装

導入部分は以前の記事にて解説しておりますので気になる方はそちらを参照いただければと思います。

【Spring Framework】Spring Securityを使用したログイン機能の実装

SecurityConfigの実装

まずは認証周りの設定を行うSecurityConfigを実装します。

SecurityConfig.java
@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がユーザー情報(ログイン名、パスワード、ロールなど)を扱うためのインターフェースです。
このクラスでログイン時にロールを割り当てます。

UserDetails.java
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)の部分でログインユーザーにADMINUSERかのロールを設定しています。

DB構成
user_id    user_login_id    password      user_name    role
--------   --------------   ------------  ----------   ------
1          BBB              password      アドミン      1
2          CCC              password      ユーザー      2

ADMINユーザーでログインし画面遷移する際の動作は以下のようになります。

/adminにアクセス
アクセステスト1.png

/userにアクセス
アクセステスト2.png

アクセス権は以下のようになります。

ロール アクセス /admin/* アクセス /user/*
ADMIN ✅ OK ❌ 403
USER ❌ 403 ✅ OK
ログインなし ❌ 403 ❌ 403

また、Controller単位でロールを割り当てることも可能なようです。

SecurityConfig.java
@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();
    }
}
Controller.java
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のロール管理は仕組みがわかると意外とシンプルで使いやすいと感じました。
アクセス制御を柔軟に行えるので、アプリのセキュリティ設計がとてもやりやすくなります。

1
0
0

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?