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

Spring Security 2 核心配置精講 (上):命名空間簡化、數據庫用戶認證與資源授權設計

     編輯:Java 相關知識

Spring Security 2 引入基於 Schema 的命名空間配置,將 Acegi 時代繁瑣的 Bean 定義與過濾器鏈手工排序簡化為聲明式標籤。本文系統梳理最小化配置、核心元素解析、過濾器鏈內部機制、數據庫用戶認證實現、以及資源授權的領域模型設計。

一、最小化配置三步走

1. web.xml 註冊委派過濾器

<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

注意:springSecurityFilterChain 為命名空間自動創建的 Bean ID,不可在業務配置中重複使用。

2. 核心安全配置 (security-context.xml)

<http auto-config="true">
  <intercept-url pattern="/**" access="ROLE_USER"/>
</http>

<authentication-provider>
  <user-service>
    <user name="admin" password="admin" authorities="ROLE_ADMIN,ROLE_USER"/>
    <user name="user" password="user" authorities="ROLE_USER"/>
  </user-service>
</authentication-provider>

二、auto-config 解包與核心元素對照

auto-config="true" 等價於:

<http>
  <intercept-url pattern="/**" access="ROLE_USER"/>
  <form-login />           <!-- 表單登錄 -->
  <anonymous />           <!-- 匿名訪問 -->
  <http-basic />          <!-- HTTP Basic 認證 -->
  <logout />              <!-- 登出處理 -->
  <remember-me />        <!-- 記住我 -->
</http>

常用屬性微調

<form-login 
    login-page="/login.jsp" 
    authentication-failure-url="/login.jsp?error=true" 
    default-target-url="/dashboard"/>

<logout logout-success-url="/login.jsp"/>

<remember-me key="myAppKey" token-validity-seconds="1209600"/> <!-- 14 天 -->

三、命名空間 vs Acegi Bean 配置對比

維度Acegi 1.x (Bean)Spring Security 2 (Namespace)
代碼量數十個 Bean 定義幾行標籤
過濾器鏈排序手工維護 FilterChainProxy 順序框架內置強制順序
錯誤易發性高 (順序錯誤難診斷)低 (結構約束)
擴展性完全可控受限於內置鏈,但支援 custom-filter

四、內置過濾器鏈順序 (不可替換核心過濾器)

1. SecurityContextPersistenceFilter     (HttpSession 上下文持久化)
2. LogoutFilter                         (登出處理)
3. AuthenticationProcessingFilter       (表單登錄 /login_process)
4. SecurityContextHolderAwareRequestFilter (Servlet API 包裝)
5. ExceptionTranslationFilter           (異常轉換、入口點重定向)
6. FilterSecurityInterceptor            (URL 權限攔截 - 核心決策點)

不可替換:第 1、5、6 個過濾器。
可擴展:custom-filter position="AUTHENTICATION_PROCESSING_FILTER" 或 before/after 插入自定義過濾器。

五、數據庫認證:JDBC UserService 配置

<authentication-provider>
  <jdbc-user-service data-source-ref="dataSource"
    users-by-username-query="SELECT username, password, enabled FROM users WHERE username=?"
    authorities-by-username-query="SELECT u.username, r.name FROM users u 
        JOIN user_role ur ON u.id=ur.user_id 
        JOIN roles r ON r.id=ur.role_id 
        WHERE u.username=?"/>
  <password-encoder hash="md5"/>
</authentication-provider>

六、擴展 UserDetails + UserDetailsService 實現領域模型認證

1. 領域實體 (User + Role 多對多)

@Entity
public class User implements UserDetails {
    @Id @GeneratedValue private Long id;
    private String name, password;
    private boolean disabled;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name="user_role", joinColumns=@JoinColumn(name="user_id"),
               inverseJoinColumns=@JoinColumn(name="role_id"))
    private Set roles;

    // UserDetails 實現
    public GrantedAuthority[] getAuthorities() {
        return roles.stream()
            .map(r -> new GrantedAuthorityImpl(r.getName()))
            .toArray(GrantedAuthority[]::new);
    }
    public String getPassword() { return password; }
    public String getUsername() { return name; }
    public boolean isEnabled() { return !disabled; }
    // isAccountNonExpired/Locked/CredentialsNonExpired 均返回 true
}

2. UserDetailsService 實現

@Repository("securityManager")
public class SecurityManagerImpl extends HibernateDaoSupport implements UserDetailsService {
    @Autowired public void init(SessionFactory sf) { setSessionFactory(sf); }

    public UserDetails loadUserByUsername(String username) {
        List users = getHibernateTemplate().find(
            "FROM User u WHERE u.name=? AND u.disabled=false", username);
        if (users.isEmpty()) throw new UsernameNotFoundException("User not found: " + username);
        return users.get(0); // User 已實現 UserDetails
    }
}

3. 配置引用

<authentication-provider user-service-ref="securityManager">
  <password-encoder hash="md5"/>
</authentication-provider>

七、資源授權領域模型設計 (Role + Resource 多對多)

@Entity
public class Role {
    @Id @GeneratedValue private Long id;
    private String name, description;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name="role_resource", joinColumns=@JoinColumn(name="role_id"),
               inverseJoinColumns=@JoinColumn(name="resource_id"))
    private Set resources;
}

@Entity
public class Resource {
    @Id @GeneratedValue private Long id;
    private String type;   // "URL", "METHOD", "MENU"
    private String value;  // "/admin/**", "userService.delete", "menu.userMgmt"

    @ManyToMany(mappedBy="resources")
    private Set roles;
}

八、資源授權實現難點與突破口

問題:Spring Security 內置 FilterSecurityInterceptor 不可替換,無法直接注入動態資源決策邏輯。
突破:實現自定義 FilterInvocationDefinitionSource,通過 custom-filter after="LAST" 追加第二個 FilterSecurityInterceptor 實例,形成雙重攔截機制 (詳見下篇)。

九、配置最佳實踐清單

  1. 生產環境關閉 auto-config,顯式聲明所需元素,避免匿名訪問、HTTP Basic 等非預期功能開啟
  2. 密碼編碼器升級為 BCryptPasswordEncoder (Spring Security 3.1+),棄用 MD5/SHA
  3. CSRF 保護:<csrf/> (3.2+ 默認開啟),REST API 可選擇性關閉
  4. 會話管理:<session-management session-fixation-protection="migrateSession"/>
  5. 異步請求支援:配置 SecurityContextHolderStrategy.MODE_INHERITABLETHREADLOCAL

掌握命名空間核心語義、內置鏈結構與擴展點,是驾驭 Spring Security 進行精細化安全控制的前提。

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