iT邦幫忙

2023 iThome 鐵人賽

DAY 29
0

昨日設計Security會根據 admin及member路由來選擇UserDetialService的邏輯,但是目前還有遺漏的部分,目前UserAuthFilter的驗證邏輯

以及Security的配置會根據URL規則,決定要驗證的項目,目前做的驗證只有「你有沒有登入」,換句話說,只要你攜帶的Token可以使用,滿足能登入的條件,就能在管理後台跟使用者前台,進行請求操作

安全性來說這樣是不夠的,這個問題可以套用Spring Security提供的角色驗證機制解決,在使用者登入後,綁定管理者或前台會員角色,今天來修正未檢查正確角色的問題

Spring Security Filter Chain

正式開始前先來了解過濾器鍊
https://ithelp.ithome.com.tw/upload/images/20231012/20161920fjnTIl1L7V.png

這張圖說明了 Spring Security預設的過濾鍊,實際上可以分為兩大核心步驟,身分驗證以及授權,先前的Securtiy配置,會在身分驗證前執行自訂的 UserAuthFilter,根據實際邏輯登入驗證成功就將當前請求使用者註明通過身分驗證

並且在配置的規則中並沒有要求角色檢查,因此不會觸發 FilterSecurityInteceptor 這個過濾器,針對角色或權限進行驗證

現在方向很明朗,今日修復驗證問題的流程

  1. 添加管理者與一般使用者API路由,進行角色驗證
  2. 在 UserDetialService 添加相應 Role

修改 Security 配置

將先前執行的 authenticated 改為 hasRole,表示需根據當前使用者,驗證ROLE符合規則才可放行

    authorize
        .requestMatchers("/api/admin/login").permitAll()
        .requestMatchers("/api/admin/**").hasRole("ADMIN")
        .requestMatchers("/api/member/login").permitAll()
        .requestMatchers("/api/member/register").permitAll()
        .requestMatchers("/api/member/**").hasRole("MEMBER")
        .anyRequest().permitAll();

調整 UserDetialService

作法很簡單,在User類別建構子的第三個參數,輸入相應ROLE即可

注意: User類別路徑是 org.springframework.security.core.userdetails.User

按照規定 第三個參數使用的項目,必須實作介面 GrantedAuthority,表示授權的物件
https://ithelp.ithome.com.tw/upload/images/20231012/20161920S9M0BqsBL6.png

調整 DBUserDetailsService.java

    @Override
    public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {

        Member member = memberMapper.getByAccount(account);

        if (member == null) {
            throw new UsernameNotFoundException("Member not found");
        }

        /**
         * 添加這段,表示回傳的User屬於 MEMBER角色
         * 要注意必須用 ROLE_當前綴,後面補上角色名稱
        */
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_MEMBER"));

        return new User(
                member.getAccount(),
                member.getPassword(),
                authorities
            );
    }

調整 AdminUserDetailsService.java

    @Override
    public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {

        Admin admin = adminMapper.getByAccount(account);

        if (admin == null) {
            throw new UsernameNotFoundException("Admin Not Found");
        }

        // 添加這段
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));

        return new User(
                admin.getAccount(),
                admin.getPassword(),
                authorities);
    }

透過API看當前請求的User資訊

AdminController 新增取得當前 User API

// /api/admin/me
    @GetMapping("/me")
    public ResponseEntity<Object> me() {

        // 從 Context取得當前登入 User(UserDetails)
        Authentication authUser = SecurityContextHolder.getContext().getAuthentication();

        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of(
                "authorities", authUser.getAuthorities(),
                "account", authUser.getName()));
    }

用Postman登入取得Token,請求 /api/admin/me 成功拿到預期內容
https://ithelp.ithome.com.tw/upload/images/20231012/20161920rvUWLrqhP8.png

重點筆記

  1. Spring Security 會根據配置規則,決定執行過濾器鍊
  2. security.core.userdetails.User 建構子的第三個參數,是用來填寫角色權限資料
  3. 自訂過濾器使用 SecurityContextHolder.getContext().setAuthentication 來設定當前請求的使用者資訊

參考資料

  1. 掘土-Spring Security

上一篇
第28日 來做管理後台登入
下一篇
第30日 第一個30天完賽感言
系列文
掌握Java神器,駕馭SpringBoot猛獸30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言