Spring Security安全綜合大全指南

banq發表於2024-04-15

在 Web 開發領域,安全是不可或缺的支柱,保護應用程式免受惡意攻擊和未經授權的訪問。在眾多可用於強化 Web 應用程式的工具中,Spring Security 作為一個強大而靈活的框架脫穎而出,為 Java 應用程式提供全面的安全功能。在本初學者指南中,我們將踏上揭開 Spring Security 神秘面紗的旅程,揭開其關鍵概念,並指導您完成將其整合到專案中的過程。

瞭解 Spring Security
Spring Security 是一個強大的 Java 應用程式身份驗證和訪問控制框架。它構建在 Spring 框架之上,提供了大量功能來解決身份驗證、授權、會話管理以及針對跨站點請求偽造 (CSRF) 和跨站點指令碼 (XSS) 攻擊等常見安全威脅的保護。

設定您的環境
在深入研究 Spring Security 的複雜性之前,請確保您對 Java 和 Spring 框架有基本的瞭解。確保您已安裝以下先決條件:

1.Java開發工具包(JDK)
2. Apache Maven或Gradle進行依賴管理
3. 整合開發環境(IDE),例如 IntelliJ IDEA 或 Eclipse

將 Spring Security 整合到您的專案中
第1步:新增Spring Security依賴項
首先將 Spring Security 依賴項新增到專案的構建配置檔案(Maven 的 pom.xml 或 Gradle 的 build.gradle)。您可以透過新增以下依賴項來包含最新版本的 Spring Security:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

第 2 步:配置安全性
接下來,透過建立一個擴充套件“WebSecurityConfigurerAdapter”的類來配置 Spring Security。此類允許您根據應用程式的要求自定義安全設定。您可以在此類中定義身份驗證提供程式、授權規則和其他安全配置。

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;

public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers(<font>"/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage(
"/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }
}


第 3 步:保護端點
使用 antMatchers() 方法為應用程式的不同端點定義訪問規則。在上面的示例中,“/”和“/home”被配置為每個人都可以訪問,而所有其他請求都需要身份驗證。

第 4 步:自定義登入頁面
透過使用“loginPage()”方法指定 URL 來自定義登入頁面。您可以建立自定義登入頁面並在此處提供其 URL。

第 5 步:啟用登出
透過呼叫“logout()”方法啟用登出功能。這允許使用者安全地登出應用程式。


Spring Security 中的身份驗證
身份驗證是任何 Web 應用程式的一個重要方面,可確保使用者在授予資源訪問許可權之前確實是他們聲稱的身份。在 Java 開發領域,Spring Security 作為實現身份驗證和授權的強大框架而脫穎而出。

瞭解 Spring Security 中的身份驗證:
Spring Security 的核心提供了一個強大而靈活的身份驗證框架,可以與基於 Spring 的應用程式無縫整合。 Spring Security 中的身份驗證圍繞身份驗證提供程式的概念展開,身份驗證提供程式負責基於各種機制(例如使用者名稱/密碼、令牌或外部身份提供程式)對使用者進行身份驗證。

1. 身份驗證提供商:

  •    - Spring Security 支援多個開箱即用的身份驗證提供程式,包括:
  •      - DaoAuthenticationProvider:根據資料庫或使用者儲存對使用者進行身份驗證。
  •      - LDAPAuthenticationProvider:與輕量級目錄訪問協議 (LDAP) 伺服器整合以進行身份​​驗證。
  •      - OAuth2AuthenticationProvider:透過 OAuth 2.0 提供商促進身份驗證。
  •    - 自定義身份驗證提供程式可以透過擴充套件 AuthenticationProvider 介面來實現。

2. 認證管理器:
  •    - AuthenticationManager 介面是 Spring Security 中身份驗證過程的核心。它將身份驗證請求委託給一個或多個已配置的身份驗證提供程式。
  •    - Spring Security 提供了 AuthenticationManager 介面的多種實現,包括 ProviderManager,它將身份驗證委託給一系列身份驗證提供者。

3. 認證流程:
  •    - 當使用者嘗試訪問受保護的資源時,Spring Security 會攔截該請求並啟動身份驗證過程。
  •    - 呼叫 AuthenticationManager 以根據提供的憑據對使用者進行身份驗證。
  •    - 身份驗證成功後,Spring Security 會生成一個安全上下文,其中包含經過身份驗證的使用者的詳細資訊,包括許可權/角色。
  •    - 然後,安全上下文將儲存在 HTTP 會話中或執行緒本地上下文中,具體取決於配置的策略。

在 Spring Security 中配置身份驗證:
在 Spring Security 中配置身份驗證涉及定義身份驗證提供程式、配置身份驗證管理器以及使用方法級或基於 URL 的安全性保護端點。
1. 定義身份驗證提供者:

  •    - 透過重寫configure(AuthenticationManagerBuilder auth)方法在Spring Security配置類中配置身份驗證提供程式。
  •    - 例子:

   

 @Autowired
     public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
         auth.authenticationProvider(myAuthenticationProvider);
     }

     
2. 配置身份驗證管理器:
  •    - 透過提供額外的配置(例如密碼編碼器、使用者詳細資訊服務和身份驗證成功/失敗處理程式)來自定義身份驗證管理器。
  •    - 例子:

     @Override
     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
         auth
             .userDetailsService(myUserDetailsService)
             .passwordEncoder(passwordEncoder());
     }

     
3. 安全端點:

  •    - 使用方法級安全註釋(@Secured、@PreAuthorize、@PostAuthorize)或安全配置類中基於 URL 的安全配置來保護端點。
  •    - 例子:

 @EnableWebSecurity
     public class SecurityConfig extends WebSecurityConfigurerAdapter {
     
         @Override
         protected void configure(HttpSecurity http) throws Exception {
             http
                 .authorizeRequests()
                     .antMatchers(<font>"/admin/**").hasRole("ADMIN")
                     .antMatchers(
"/user/**").authenticated()
                     .anyRequest().permitAll()
                 .and()
                 .formLogin()
                     .loginPage(
"/login")
                     .permitAll();
         }
     }

最佳實踐和注意事項:

  1. 使用強密碼加密:採用bcrypt等強密碼加密演算法來安全儲存使用者密碼。
  2. 實施多重身份驗證 (MFA):透過實施一次性密碼 (OTP) 或生物識別身份驗證等 MFA 機制來增強安全性。
  3.  定期更新依賴項:及時瞭解 Spring Security 的最新版本,以利用新功能和安全增強功能。
  4. 監控身份驗證日誌:監控身份驗證日誌中是否存在可疑活動,並實施適當的日誌記錄和監控機制。

Spring Security:自定義使用者身份驗證
Spring Security 是一個強大且高度可定製的 Java 應用程式身份驗證和訪問控制框架。它為基於Java EE的企業軟體應用程式提供全面的安全服務。 Spring Security 的關鍵功能之一是它能夠輕鬆自定義使用者身份驗證以滿足應用程式的需求。

自定義使用者身份驗證
在 Spring Security 中自定義使用者身份驗證涉及實現自定義 UserDetailsS​​ervice 和配置 AuthenticationProvider。

  • UserDetailsS​​ervice 介面用於從資料來源(例如資料庫或 LDAP 目錄)檢索使用者詳細資訊。
  • AuthenticationProvider 介面用於根據 UserDetailsS​​ervice 檢索到的資訊對使用者進行身份驗證。

實現自定義 UserDetailsS​​ervice
要實現自定義 UserDetailsS​​ervice,您需要建立一個實現 UserDetailsS​​ervice 介面的類並重寫 loadUserByUsername 方法。此方法應從資料來源載入使用者詳細資訊並返回表示經過身份驗證的使用者的 UserDetails 物件。以下是自定義 UserDetailsS​​ervice 實現的示例:

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
            .orElseThrow(() -> new UsernameNotFoundException(<font>"User not found with username: " + username));

        return UserPrincipal.create(user);
    }
}


在此示例中,UserRepository 是一個 Spring Data JPA 儲存庫,它提供對儲存在資料庫中的使用者資料的訪問。 UserPrincipal 類是一個自定義類,它實現 UserDetails 介面並代表經過身份驗證的使用者。

配置 AuthenticationProvider
要配置 AuthenticationProvider,您需要建立一個實現 AuthenticationProvider 介面並重寫authenticate 方法的類。此方法應根據 UserDetailsS​​ervice 檢索到的資訊對使用者進行身份驗證。以下是 AuthenticationProvider 實現的示例:

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        UserDetails userDetails = userDetailsService.loadUserByUsername(username);

        if (!passwordEncoder().matches(password, userDetails.getPassword())) {
            throw new BadCredentialsException(<font>"Invalid username or password");
        }

        return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

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

在此示例中,CustomAuthenticationProvider 類使用 UserDetailsS​​ervice 載入使用者詳細資訊並對使用者進行身份驗證。它還使用 BCryptPasswordEncoder 來編碼和驗證密碼。

自定義 AuthenticationManagerBuilder
要使用自定義的 UserDetailsS​​ervice 和 AuthenticationProvider,您需要在 Spring Security 配置類中配置 AuthenticationManagerBuilder。以下是如何執行此操作的示例:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Autowired
    private CustomAuthenticationProvider customAuthenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserDetailsService)
            .passwordEncoder(passwordEncoder());
        auth.authenticationProvider(customAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers(<font>"/admin/**").hasRole("ADMIN")
                .antMatchers(
"/user/**").hasRole("USER")
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage(
"/login")
                .permitAll()
            .and()
            .logout()
                .permitAll();
    }

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


在此示例中,SecurityConfig 類擴充套件 WebSecurityConfigurerAdapter 並重寫 configure 方法來配置 AuthenticationManagerBuilder 和 HttpSecurity。

Spring Security:JWT 身份驗證
在現代 Web 應用程式中,保護端點對於保護敏感資料並確保只有授權使用者才能訪問某些資源至關重要。由於其無狀態特性和易用性,JSON Web 令牌 (JWT) 已成為在 Web 應用程式中實現身份驗證的流行選擇。

什麼是JWT
JWT 是一種開放標準 (RFC 7519),它定義了一種緊湊且獨立的方式,用於在各方之間以 JSON 物件的形式安全地傳輸資訊。 JWT 可以使用秘密(使用 HMAC 演算法)或公鑰/私鑰對(使用 RSA 或 ECDSA 演算法)進行簽名,這使您可以驗證 JWT 的傳送者是否是其所說的人,並且訊息沒有被篡改。被篡改。

JWT 通常由三部分組成:標頭、有效負載和簽名。標頭指定令牌的型別和所使用的簽名演算法,有效負載包含宣告(例如,使用者資訊、過期時間),簽名用於驗證令牌的真實性。

使用 Spring Security 實現 JWT 身份驗證
要使用 Spring Security 實現 JWT 身份驗證,我們需要執行以下步驟:

1. 新增依賴項:將必要的依賴項新增到您的 pom.xml 或 build.gradle 檔案中:

 <!-- Spring Security -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-security</artifactId>
   </dependency>
   <!-- JWT -->
   <dependency>
       <groupId>io.jsonwebtoken</groupId>
       <artifactId>jjwt</artifactId>
       <version>0.9.1</version>
   </dependency>


2. 建立 JWT 實用程式類:建立一個實用程式類來生成和驗證 JWT。此類通常具有用於建立 JWT、解析 JWT 和驗證 JWT 的方法。

 public class JwtUtil {
       private String secretKey = <font>"your_secret_key_here";

       public String generateToken(String username) {
           return Jwts.builder()
                   .setSubject(username)
                   .setIssuedAt(new Date())
                   .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
// 10 hours<i>
                   .signWith(SignatureAlgorithm.HS256, secretKey)
                   .compact();
       }

       public Claims extractClaims(String token) {
           return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
       }

       public String extractUsername(String token) {
           return extractClaims(token).getSubject();
       }

       public boolean validateToken(String token) {
           try {
               Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
               return true;
           } catch (JwtException | IllegalArgumentException e) {
               return false;
           }
       }
   }


3. 配置 Spring Security:配置 Spring Security 使用 JWT 進行身份驗證。建立一個擴充套件 WebSecurityConfigurerAdapter 的類並重寫 configure 方法來配置 JWT 身份驗證。

   

  @Configuration
   @EnableWebSecurity
   public class SecurityConfig extends WebSecurityConfigurerAdapter {

       @Autowired
       private JwtRequestFilter jwtRequestFilter;

       @Override
       protected void configure(HttpSecurity http) throws Exception {
           http.csrf().disable()
                   .authorizeRequests().antMatchers(<font>"/authenticate").permitAll()
                   .anyRequest().authenticated()
                   .and().sessionManagement()
                   .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
           http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
       }
   }

   

4. 建立 JWT 請求過濾器:建立過濾器來攔截傳入請求並驗證授權標頭中的 JWT。

 @Component
   public class JwtRequestFilter extends OncePerRequestFilter {

       @Autowired
       private JwtUtil jwtUtil;

       @Override
       protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
               throws ServletException, IOException {

           final String authorizationHeader = request.getHeader(<font>"Authorization");

           String username = null;
           String jwt = null;

           if (authorizationHeader != null && authorizationHeader.startsWith(
"Bearer ")) {
               jwt = authorizationHeader.substring(7);
               username = jwtUtil.extractUsername(jwt);
           }

           if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

               if (jwtUtil.validateToken(jwt)) {

                   UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                           new UsernamePasswordAuthenticationToken(username, null, Collections.emptyList());
                   usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                   SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
               }
           }
           chain.doFilter(request, response);
       }
   }

   

5. 建立控制器:建立控制器來處理身份驗證請求。

@RestController
   public class AuthController {

       @Autowired
       private JwtUtil jwtUtil;

       @Autowired
       private AuthenticationManager authenticationManager;

       @RequestMapping(value = <font>"/authenticate", method = RequestMethod.POST)
       public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthRequest authRequest) throws Exception {
           try {
               authenticationManager.authenticate(
                       new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
               );
           } catch (BadCredentialsException e) {
               throw new Exception(
"Incorrect username or password", e);
           }

           final UserDetails userDetails = userDetailsService
                   .loadUserByUsername(authRequest.getUsername());

           final String jwt = jwtUtil.generateToken(userDetails.getUsername());

           return ResponseEntity.ok(new AuthResponse(jwt));
       }
   }

   

6. 測試應用程式:透過使用有效憑據向 /authenticate 傳送 POST 請求來測試應用程式。您應該在響應中收到 JWT,然後您可以透過將其作為 Bearer <token> 包含在請求的 Authorization 標頭中來使用它來訪問受保護的資源。


Spring Security中的授權
授權是應用程式安全的一個重要方面,確保使用者擁有訪問某些資源或執行特定操作的正確許可權。在 Spring 生態系統中,Spring Security 為在 Java 應用程式中實現授權提供了強大的支援。

什麼是授權?
授權是確定是否允許使用者訪問資源或在應用程式內執行特定操作的過程。它通常基於使用者的身份以及與該身份關聯的許可權。授權經常與身份驗證混淆,身份驗證是驗證使用者身份的過程。

在 Spring Security 中,授權是使用訪問控制規則來實現的,這些規則定義了哪些使用者或角色可以訪問哪些資源或操作。這些規則通常使用註釋、XML 配置或 Java 程式碼進行配置。

基於角色的訪問控制 (RBAC)
基於角色的訪問控制 (RBAC) 是一種流行的授權方法,其中將訪問許可權分配給角色,並向使用者分配一個或多個角色。在 Spring Security 中,角色由 GrantedAuthority 介面表示,該介面定義了使用者可能擁有的角色或許可權。 

例如,您可以定義 ROLE_USER 和 ROLE_ADMIN 等角色,然後將這些角色分配給使用者。然後,您可以使用這些角色來限制對應用程式某些部分的訪問。

在 Spring Security 中配置授權
要在Spring Security中配置授權,需要定義訪問控制規則。這可以使用 Java 配置、XML 配置或註釋來完成。

Java配置

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers(<font>"/admin/**").hasRole("ADMIN")
            .antMatchers(
"/user/**").hasAnyRole("USER", "ADMIN")
            .anyRequest().authenticated()
            .and()
            .formLogin().permitAll()
            .and()
            .logout().permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser(
"user").password("{noop}password").roles("USER")
            .and()
            .withUser(
"admin").password("{noop}password").roles("ADMIN");
    }
}

在此示例中,我們使用 antMatchers 定義了訪問控制規則,以指定哪些 URL 需要哪些角色。我們還使用 inMemoryAuthentication 配置身份驗證,但在實際應用程式中,您將使用資料庫或其他身份驗證提供程式。

XML配置

<beans:beans xmlns=<font>"http://www.springframework.org/schema/security"
    xmlns:beans=
"http://www.springframework.org/schema/beans"
    xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation=
"http://www.springframework.org/schema/beans<i>
        http:
//www.springframework.org/schema/beans/spring-beans.xsd<i>
        http:
//www.springframework.org/schema/security<i>
        http:
//www.springframework.org/schema/security/spring-security.xsd"><i>

    <http auto-config=
"true">
        <intercept-url pattern=
"/admin/**" access="hasRole('ROLE_ADMIN')" />
        <intercept-url pattern=
"/user/**" access="hasAnyRole('ROLE_USER', 'ROLE_ADMIN')" />
        <form-login />
        <logout />
    </http>

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

</beans:beans>

在此 XML 配置中,我們定義了與 Java 配置示例中類似的訪問控制規則和身份驗證配置。

授權是應用程式安全的一個重要方面,Spring Security 提供了在 Java 應用程式中實施授權的強大工具。透過了解授權的概念以及如何配置訪問控制規則,您可以確保應用程式的安全性,並確保使用者擁有訪問所需資源的適當許可權。

相關文章