最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Spring Security實(shí)現(xiàn)RBAC權(quán)限控制的完整步驟

 更新時(shí)間:2026年04月22日 09:02:03   作者:無籽西瓜a  
文章主要講述了基于角色的訪問控制(RBAC)的基本概念、SpringSecurity的核心功能,以及實(shí)現(xiàn)RBAC的詳細(xì)步驟,包括定義用戶模型和角色、密碼加密、JWT工具類、讓SpringSecurity認(rèn)識(shí)用戶的用戶模型、JWT認(rèn)證過濾器以及安全配置規(guī)則等,需要的朋友可以參考下

什么是 RBAC

RBAC,全稱 Role-Based Access Control,基于角色的訪問控制。核心思路很簡(jiǎn)單:不直接給用戶分配權(quán)限,而是引入"角色"作為中間層。

用戶角色權(quán)限

舉個(gè)例子:一家公司里,"實(shí)習(xí)生"能查看文檔,"正式員工"能查看和編輯文檔,"部門經(jīng)理"能查看、編輯、刪除文檔。你入職的時(shí)候,HR 不會(huì)一條條給你勾權(quán)限,而是直接給你一個(gè)角色,權(quán)限就跟著來了。

這里面有幾個(gè)關(guān)鍵關(guān)系:

  • 一個(gè)用戶可以擁有多個(gè)角色
  • 一個(gè)角色可以分配給多個(gè)用戶
  • 一個(gè)角色可以包含多個(gè)權(quán)限
  • 一個(gè)權(quán)限可以屬于多個(gè)角色

還有一種模型叫角色繼承——上層角色自動(dòng)繼承下層角色的所有權(quán)限。比如"經(jīng)理"自動(dòng)擁有"員工"的全部權(quán)限,再額外擁有管理權(quán)限。這在組織架構(gòu)復(fù)雜的系統(tǒng)中非常實(shí)用。

Spring Security 是什么

Spring Security 本質(zhì)上是一個(gè)基于 Filter 的安全框架。請(qǐng)求進(jìn)入應(yīng)用時(shí),會(huì)經(jīng)過一系列過濾器鏈,每個(gè)過濾器各司其職:有的負(fù)責(zé)認(rèn)證,有的負(fù)責(zé)授權(quán),有的負(fù)責(zé) CSRF 防護(hù)。

它解決兩個(gè)核心問題:

  • 認(rèn)證(Authentication):你是誰?驗(yàn)證用戶名密碼是否正確。
  • 授權(quán)(Authorization):你能干什么?檢查你有沒有權(quán)限訪問某個(gè)接口。

用 RBAC 的思路來說,認(rèn)證就是確認(rèn)你的身份,授權(quán)就是根據(jù)你的角色判斷你能不能進(jìn)這扇門。

整體架構(gòu)設(shè)計(jì)

要基于 Spring Security 實(shí)現(xiàn) RBAC,需要這幾個(gè)核心模塊:

config/          → 安全配置、JWT 過濾器
controller/      → 接收請(qǐng)求的入口
service/         → 業(yè)務(wù)邏輯、用戶詳情加載
repository/      → 數(shù)據(jù)庫(kù)訪問
model/           → 用戶實(shí)體、角色定義
utils/           → 密碼加密、JWT 工具

下面按照請(qǐng)求的生命周期來講,從注冊(cè)到登錄到鑒權(quán),一步步走通。

第一步:定義用戶模型和角色

@Data
@Entity
@Table(name = "wzxg_users", uniqueConstraints = @UniqueConstraint(columnNames = "username"))
public class WzxgUser {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private WzxgRole role;

    @CreationTimestamp
    private LocalDateTime createdTime;

    @UpdateTimestamp
    private LocalDateTime updatedTime;

    public enum WzxgRole {
        USER, ADMIN
    }
}

@Enumerated(EnumType.STRING) 讓角色以字符串形式存到數(shù)據(jù)庫(kù)里,比如存的是 "ADMIN" 而不是 0,可讀性好,也不容易出錯(cuò)。

數(shù)據(jù)訪問層很簡(jiǎn)單,一個(gè)根據(jù)用戶名查詢的方法就夠了:

public interface WzxgUserRepository extends JpaRepository<WzxgUser, Long> {
    Optional<WzxgUser> findByUsername(String username);
}

第二步:密碼加密

密碼絕對(duì)不能明文存儲(chǔ)。BCrypt 是目前主流的選擇,它內(nèi)置鹽值機(jī)制,同一個(gè)密碼每次加密結(jié)果都不同,能有效防御彩虹表攻擊。

public class WzxgPasswordUtil {
    private static final BCryptPasswordEncoder wzxgEncoder = new BCryptPasswordEncoder();

    public static String encode(String rawPassword) {
        return wzxgEncoder.encode(rawPassword);
    }

    public static boolean matches(String rawPassword, String encodedPassword) {
        return wzxgEncoder.matches(rawPassword, encodedPassword);
    }
}

注冊(cè)時(shí)調(diào) encode(),登錄時(shí)調(diào) matches()

第三步:JWT 工具類

系統(tǒng)采用無狀態(tài)認(rèn)證,服務(wù)端不存 session,而是通過 JWT 令牌來傳遞身份信息。

@Component
public class WzxgJwtUtils {
    private static final String WZXG_SECRET_KEY = "wzxg_jwt_secret_key_2024";
    private static final long WZXG_EXPIRATION = 86400000; // 24小時(shí)

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + WZXG_EXPIRATION))
                .signWith(SignatureAlgorithm.HS256, WZXG_SECRET_KEY)
                .compact();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(WZXG_SECRET_KEY).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public String extractUsername(String token) {
        Claims wzxgClaims = Jwts.parser()
                .setSigningKey(WZXG_SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();
        return wzxgClaims.getSubject();
    }
}

JWT 是一個(gè)自包含的令牌,里面可以塞用戶名、角色、組織信息等。這樣每次請(qǐng)求過來,解析 token 就能知道用戶是誰、有什么權(quán)限,不用查數(shù)據(jù)庫(kù)。

第四步:讓 Spring Security 認(rèn)識(shí)你的用戶

Spring Security 有自己的一套用戶模型 UserDetails,我們需要實(shí)現(xiàn) UserDetailsService 接口,把數(shù)據(jù)庫(kù)里的用戶轉(zhuǎn)換成它能理解的格式。

@Service
public class WzxgUserDetailsService implements UserDetailsService {

    @Autowired
    private WzxgUserRepository wzxgUserRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        WzxgUser wzxgUser = wzxgUserRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("用戶不存在"));

        return new org.springframework.security.core.userdetails.User(
                wzxgUser.getUsername(),
                wzxgUser.getPassword(),
                buildAuthorities(wzxgUser.getRole())
        );
    }

    private Collection<? extends GrantedAuthority> buildAuthorities(WzxgUser.WzxgRole role) {
        return Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.name()));
    }
}

注意 buildAuthorities 方法里的 "ROLE_" 前綴,這是 Spring Security 的約定。角色 ADMIN 會(huì)變成 ROLE_ADMIN,這樣后面配置 hasRole("ADMIN") 時(shí)框架才能正確匹配。

第五步:JWT 認(rèn)證過濾器

這是整個(gè)流程的關(guān)鍵——每個(gè)請(qǐng)求進(jìn)來時(shí),過濾器從請(qǐng)求頭里取出 JWT,驗(yàn)證有效性,然后把用戶信息塞進(jìn) Spring Security 的上下文。

@Component
public class WzxgJwtAuthFilter extends OncePerRequestFilter {

    @Autowired
    private UserDetailsService wzxgUserDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {
        try {
            String wzxgToken = getToken(request);//從請(qǐng)求頭獲取jwt token
            if (wzxgToken != null) {
                String username = getUsername(wzxgToken);
                UserDetails wzxgUserDetails = wzxgUserDetailsService.loadUserByUsername(username);//根據(jù)用戶名加載用戶詳細(xì)信息
                UsernamePasswordAuthenticationToken wzxgAuth =
                        new UsernamePasswordAuthenticationToken(
                                wzxgUserDetails, null, wzxgUserDetails.getAuthorities());
                wzxgAuth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(wzxgAuth);
            }
        } catch (Exception e) {
        }
        filterChain.doFilter(request, response);
    }

    private String getToken(HttpServletRequest request) {
        String wzxgBearer = request.getHeader("Authorization");
        if (wzxgBearer != null && wzxgBearer.startsWith("Bearer ")) {
            return wzxgBearer.substring(7);
        }
        return null;
    }

    private String getUsername(String token) {
        try {
            Claims claims = extractClaimsIgnoreExpiration(token);
            return claims != null ? claims.getSubject() : null;
        } catch (Exception e) {
            return null;
        }
    }
}

繼承 OncePerRequestFilter 保證每個(gè)請(qǐng)求只過濾一次。如果 token 無效或不存在,不會(huì)報(bào)錯(cuò),只是不設(shè)置認(rèn)證信息,后續(xù)的權(quán)限檢查自然會(huì)攔住未認(rèn)證的請(qǐng)求。

第六步:安全配置——定義規(guī)則

最后把所有東西串起來,告訴 Spring Security:哪些接口誰能訪問。

@Configuration
@EnableWebSecurity
public class WzxgSecurityConfig {

    @Autowired
    private WzxgJwtAuthFilter wzxgJwtAuthFilter;

    @Bean
    public SecurityFilterChain wzxgFilterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                // 登錄注冊(cè)不需要認(rèn)證
                .requestMatchers("/api/v1/auth/register", "/api/v1/auth/login").permitAll()
                // 管理接口只有 ADMIN 能訪問
                .requestMatchers("/api/v1/manage/**").hasRole("ADMIN")
                // 普通業(yè)務(wù)接口,USER 和 ADMIN 都能訪問
                .requestMatchers("/api/v1/workspace/**").hasAnyRole("USER", "ADMIN")
                // 其余接口都需要認(rèn)證
                .anyRequest().authenticated()
            )
            .sessionManagement(session ->
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            // 把 JWT 過濾器插到 UsernamePasswordAuthenticationFilter 前面
            .addFilterBefore(wzxgJwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}
  • csrf.disable():因?yàn)橛玫氖?JWT 無狀態(tài)認(rèn)證,不需要 CSRF 防護(hù)
  • SessionCreationPolicy.STATELESS:不創(chuàng)建 session,完全靠 token
  • addFilterBefore:確保 JWT 過濾器在 Spring Security 默認(rèn)的認(rèn)證過濾器之前執(zhí)行

完整請(qǐng)求流程

把上面的模塊串起來,一個(gè)請(qǐng)求的完整生命周期是這樣的:

1. 用戶注冊(cè) → 密碼用 BCrypt 加密 → 分配默認(rèn)角色 USER → 存入數(shù)據(jù)庫(kù)

2. 用戶登錄 → 驗(yàn)證用戶名密碼 → 生成 JWT Token → 返回給客戶端

3. 客戶端請(qǐng)求業(yè)務(wù)接口 → 請(qǐng)求頭帶上 Authorization: Bearer <token>

4. WzxgJwtAuthFilter 攔截請(qǐng)求 → 提取 token → 驗(yàn)證簽名和有效期
   → 解析出用戶名 → 加載用戶信息和角色 → 寫入 SecurityContext

5. Spring Security 根據(jù) WzxgSecurityConfig 中的規(guī)則
   → 檢查當(dāng)前用戶的角色是否匹配目標(biāo)接口的權(quán)限要求
   → 通過則放行,否則返回 403

以上就是基于Spring Security實(shí)現(xiàn)RBAC權(quán)限控制的完整步驟的詳細(xì)內(nèi)容,更多關(guān)于Spring Security RBAC權(quán)限控制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java枚舉類使用場(chǎng)景及實(shí)例解析

    Java枚舉類使用場(chǎng)景及實(shí)例解析

    這篇文章主要介紹了Java枚舉類使用場(chǎng)景及實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • java利用mybatis攔截器統(tǒng)計(jì)sql執(zhí)行時(shí)間示例

    java利用mybatis攔截器統(tǒng)計(jì)sql執(zhí)行時(shí)間示例

    這篇文章主要介紹了java利用mybatis攔截器統(tǒng)計(jì)sql執(zhí)行時(shí)間示例,該攔截器攔截mybatis的query和update操作,能統(tǒng)計(jì)sql執(zhí)行時(shí)間
    2014-03-03
  • Java日期時(shí)間格式化操作DateUtils 的整理

    Java日期時(shí)間格式化操作DateUtils 的整理

    這篇文章主要介紹了Java日期時(shí)間格式化操作DateUtils 的整理的相關(guān)資料,這里總結(jié)了java日期格式化的操作,需要的朋友可以參考下
    2017-07-07
  • Java?Swing實(shí)現(xiàn)自定義彈窗組件CusDialog(附源碼)

    Java?Swing實(shí)現(xiàn)自定義彈窗組件CusDialog(附源碼)

    Swing 原生對(duì)話框 JDialog 和 JFrame 一樣,標(biāo)題欄是系統(tǒng)自帶的,樣式陳舊且無法自定義,下面我們就來看看如何通過Java Swing實(shí)現(xiàn)自定義彈窗組件CusDialog吧
    2026-05-05
  • 淺談IDEA2018打包可執(zhí)行jar包的流程

    淺談IDEA2018打包可執(zhí)行jar包的流程

    這篇文章主要介紹了淺談IDEA2018打包可執(zhí)行jar包的流程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Java實(shí)現(xiàn)棧和最小棧的方法

    Java實(shí)現(xiàn)棧和最小棧的方法

    棧(Stack)是一種常用的數(shù)據(jù)結(jié)構(gòu),其核心特點(diǎn)是先進(jìn)后出,這篇文章主要介紹了Java實(shí)現(xiàn)棧和最小棧的相關(guān)操作,需要的朋友可以參考下
    2026-02-02
  • JavaCV?本地視頻推流實(shí)現(xiàn)依賴示例

    JavaCV?本地視頻推流實(shí)現(xiàn)依賴示例

    這篇文章主要為大家介紹了JavaCV?本地視頻推流實(shí)現(xiàn)的依賴示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Java中的拷貝數(shù)組CopyOnWriteArrayList詳解

    Java中的拷貝數(shù)組CopyOnWriteArrayList詳解

    這篇文章主要介紹了Java中的拷貝數(shù)組CopyOnWriteArrayList詳解,ArrayList和LinkedList都不是線程安全的,如果需要線程安全的List,可以使用synchronizedList來生成一個(gè)同步list,但是這個(gè)同步list的方法都是通過synchronized修飾來保證同步的,需要的朋友可以參考下
    2023-12-12
  • SpringBoot RedisTemplate分布式鎖的項(xiàng)目實(shí)戰(zhàn)

    SpringBoot RedisTemplate分布式鎖的項(xiàng)目實(shí)戰(zhàn)

    本文主要介紹了SpringBoot RedisTemplate分布式鎖的項(xiàng)目實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • 淺談Java中常用數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)類 Collection和Map

    淺談Java中常用數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)類 Collection和Map

    下面小編就為大家?guī)硪黄獪\談Java中常用數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)類 Collection和Map。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-09-09

最新評(píng)論

二连浩特市| 江西省| 醴陵市| 区。| 寿阳县| 仪征市| 湘乡市| 无为县| 仙居县| 南丰县| 平顺县| 久治县| 永兴县| 禹城市| 永新县| 江西省| 涞源县| 六枝特区| 措美县| 田阳县| 余干县| 武威市| 信阳市| 军事| 资中县| 天长市| 大理市| 浏阳市| 介休市| 罗平县| 厦门市| 额敏县| 肇州县| 鹤峰县| 长武县| 当阳市| 开阳县| 塘沽区| 天台县| 开平市| 疏附县|