首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring Boot 整合 Spring Security 實現 RBAC 權限控制與 JWT 無狀態認證

     編輯:Java 相關知識

Spring Security 是 Spring 生態最強大的安全框架。本文從基礎表單登錄進階到 RBAC 權限模型與 JWT 無狀態認證,構建生產級安全體系。

核心依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

階段一:基礎表單登錄與授權

安全配置類

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)  // 啟用 @PreAuthorize 等註解
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/", "/home", "/login", "/register", "/css/**", "/js/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .defaultSuccessUrl("/dashboard", true)
                .failureUrl("/login?error")
                .permitAll()
            )
            .logout(logout -> logout
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login?logout")
                .permitAll()
            )
            .csrf(csrf -> csrf.disable());  // API 模式通常關閉 CSRF,改用 JWT
        
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder encoder) {
        // 內存用戶 (生產環境替換為資料庫實現)
        UserDetails user = User.withUsername("user")
            .password(encoder.encode("password"))
            .roles("USER")
            .build();
        UserDetails admin = User.withUsername("admin")
            .password(encoder.encode("admin123"))
            .roles("ADMIN")
            .build();
        return new InMemoryUserDetailsManager(user, admin);
    }

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

登錄頁面 (Thymeleaf)

<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
<body>
    <div th:if="${param.error}" class="alert alert-danger">用戶名或密碼錯誤</div>
    <div th:if="${param.logout}" class="alert alert-success">已成功登出</div>
    
    <form th:action="@{/login}" method="post">
        <input type="text" name="username" placeholder="用戶名" required/>
        <input type="password" name="password" placeholder="密碼" required/>
        <button type="submit">登錄</button>
    </form>
</body>
</html>

階段二:RBAC 權限模型 (資料庫驅動)

實體設計

@Entity
public class SysUser implements UserDetails {
    @Id @GeneratedValue
    private Long id;
    private String username;
    private String password;
    private Boolean enabled = true;
    
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "sys_user_role",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<SysRole> roles = new HashSet<>();
    
    // UserDetails 方法實現
    @Override public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles.stream()
            .flatMap(r -> r.getPermissions().stream())
            .map(p -> new SimpleGrantedAuthority(p.getCode()))
            .collect(Collectors.toSet());
    }
    // 其他方法略
}

@Entity
public class SysRole {
    @Id @GeneratedValue
    private Long id;
    private String name;      // ROLE_ADMIN
    private String code;      // ADMIN
    
    @ManyToMany
    @JoinTable(name = "sys_role_permission",
        joinColumns = @JoinColumn(name = "role_id"),
        inverseJoinColumns = @JoinColumn(name = "permission_id"))
    private Set<SysPermission> permissions = new HashSet<>();
}

@Entity
public class SysPermission {
    @Id @GeneratedValue
    private Long id;
    private String name;      // 用戶管理
    private String code;      // USER:READ, USER:WRITE
    private String type;      // MENU, BUTTON, API
}

自定義 UserDetailsService

new UsernameNotFoundException("用戶不存在: " + username)); } }

方法級權限控制

階段三:JWT 無狀態認證 (前後端分離標配)

JWT 工具類

(); claims.put("roles", userDetails.getAuthorities().stream() .map(GrantedAuthority::getAuthority).collect(Collectors.toList())); return Jwts.builder() .setClaims(claims) .setSubject(userDetails.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + expirationMs)) .signWith(getSigningKey(), SignatureAlgorithm.HS256) .compact(); } public boolean validateToken(String token) { try { Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token); return true; } catch (JwtException | IllegalArgumentException e) { return false; } } public String getUsername(String token) { return Jwts.parserBuilder().setSigningKey(getSigningKey()).build() .parseClaimsJws(token).getBody().getSubject(); } public List<String> getRoles(String token) { return (List<String>) Jwts.parserBuilder().setSigningKey(getSigningKey()).build() .parseClaimsJws(token).getBody().get("roles"); } }

JWT 認證過濾器

整合到 Security 鏈

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
    
    @Autowired
    private JwtAuthenticationFilter jwtFilter;
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
        
        return http.build();
    }
}

登錄接口簽發 Token

前端調用示例

// 登錄獲取 Token
const login = async (username, password) => {
    const res = await fetch('/api/auth/login', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({username, password})
    });
    const {token} = await res.json();
    localStorage.setItem('jwt_token', token);
    return token;
};

// 攜帶 Token 請求受保護資源
const fetchUsers = async () => {
    const token = localStorage.getItem('jwt_token');
    const res = await fetch('/api/users', {
        headers: {'Authorization': `Bearer ${token}`}
    });
    return res.json();
};

生產環境安全加固清單

  • ✅ HTTPS 強制傳輸加密
  • ✅ JWT Secret 使用 256 位以上隨機密鑰,配置中心管理,定期輪換
  • ✅ Token 過期時間合理設置 (Access Token 短 + Refresh Token 長)
  • ✅ 密碼加密使用 BCrypt/Argon2,嚴禁明文/MD5
  • ✅ 敏感接口限流 (Redis + Lua 腳本)
  • ✅ 審計日誌記錄關鍵操作 (登錄、權限變更、數據導出)
  • ✅ CORS 策略最小化放行域名
  • ✅ 防暴力破解:登錄失敗計數 + 鎖定 + 驗證碼

總結

Spring Security 從「繁瑣配置」進化為「註解驅動 + 函數式 DSL」,配合 JWT 實現無狀態認證,完美適配前後端分離架構。核心遵循:認證 確認身份 → 授權 判定權限 → 審計 記錄軌跡。掌握 SecurityFilterChain、UserDetailsService、@PreAuthorize、JWT 過濾器鏈四大核心組件,即可構建企業級安全防線。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved