Spring Security笔记篇
Spring Security学习笔记
一、Spring Security简介
Spring Security是针对Spring项目的安全框架,同时它也是一个独立的安全框架,可以在JavaEE项目中使用。其核心功能主要包括认证(Authentication)和授权(Authorization)。
1.1 认证
认证是指验证用户身份的过程。比如在登录系统时,用户输入用户名和密码,系统需要确认这些信息是否正确,以判断该用户是否有权限访问系统。常见的认证方式有基于表单的认证、HTTP基本认证等。在Spring Security中,认证机制通过一系列的过滤器和认证提供者来实现。
1.2 授权
授权是指确定已认证用户对系统资源的访问权限。比如,系统中可能有普通用户和管理员用户,管理员用户拥有更多的操作权限,如创建、删除用户等,而普通用户可能只有查看某些信息的权限。Spring Security提供了丰富的授权策略,如基于角色的访问控制(RBAC)、基于权限的访问控制等。
二、核心组件
2.1 AuthenticationManager
负责处理认证请求。它是一个接口,其主要实现类是ProviderManager 。ProviderManager管理着多个AuthenticationProvider,每个AuthenticationProvider负责一种特定类型的认证,例如基于内存用户的认证、基于数据库用户的认证等。当一个认证请求到来时,ProviderManager会依次尝试每个AuthenticationProvider进行认证,直到认证成功或者所有的Provider都尝试完毕。
2.2 Authentication
这是一个接口,用于表示认证信息。当用户认证成功后,Spring Security会创建一个实现了该接口的对象,包含了用户的主体信息(如用户名)、凭证(如密码)以及用户的权限信息等。常见的实现类有UsernamePasswordAuthenticationToken等。
2.3 SecurityContextHolder
用于存储和获取当前用户的认证信息。它是一个线程安全的工具类,在整个请求处理过程中,Spring Security会将认证后的用户信息存储在SecurityContextHolder中,这样在应用的任何地方都可以方便地获取当前用户的相关信息,例如在控制器中获取当前登录用户的用户名。
2.4 AccessDecisionManager
负责授权决策。当一个请求到达需要授权的资源时,AccessDecisionManager会根据配置的授权规则和当前用户的权限信息来决定是否允许该请求访问资源。它有多个投票器(AccessDecisionVoter)来协助进行决策,每个投票器根据不同的规则进行投票,最终由AccessDecisionManager综合投票结果做出决策。
2.5 Filter链
Spring Security是通过一系列的过滤器来实现其功能的。这些过滤器按照特定的顺序组成一个过滤器链,对进入应用的每一个请求进行处理。例如,FilterChainProxy是Spring Security过滤器链的入口,它会根据请求的URL等信息决定使用哪个过滤器链来处理请求。常见的过滤器有用于认证的UsernamePasswordAuthenticationFilter,用于防止跨站请求伪造(CSRF)攻击的CsrfFilter等。
三、配置Spring Security
3.1 引入依赖
在Spring Boot项目中,引入Spring Security依赖非常简单,只需要在pom.xml文件中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
3.2 配置类
通常通过创建一个配置类来定制Spring Security的行为。配置类需要继承WebSecurityConfigurerAdapter并使用@EnableWebSecurity注解启用Web安全功能。以下是一个简单的配置示例:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// 配置哪些URL可以被匿名访问
.antMatchers("/", "/home").permitAll()
// 配置只有具有ADMIN角色的用户可以访问/admin/**下的URL
.antMatchers("/admin/**").hasRole("ADMIN")
// 配置具有USER或ADMIN角色的用户可以访问/user/**下的URL
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
// 其他所有URL都需要认证后才能访问
.anyRequest().authenticated()
.and()
.formLogin()
// 配置登录页面的URL
.loginPage("/login")
// 允许所有用户访问登录页面
.permitAll()
.and()
.logout()
// 允许所有用户访问注销功能
.permitAll();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
在上述配置中:
-
authorizeRequests()方法用于配置请求的访问权限。 -
formLogin()方法用于配置表单登录功能,包括指定登录页面的URL和允许所有用户访问登录页面。 -
logout()方法用于配置注销功能,允许所有用户访问注销操作。 -
passwordEncoder()方法配置了密码编码器,这里使用BCryptPasswordEncoder对密码进行加密存储和验证,提高密码的安全性。3.3 自定义UserDetailsService
在实际应用中,用户信息通常存储在数据库中。此时,需要自定义
UserDetailsService接口的实现类来从数据库中加载用户信息。示例代码如下:import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; // 假设存在一个UserRepository用于访问数据库中的用户信息 @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { UserEntity userEntity = userRepository.findByUsername(username); if (userEntity == null) { throw new UsernameNotFoundException("User not found with username: " + username); } return User.withUsername(userEntity.getUsername()) .password(userEntity.getPassword()) .authorities(userEntity.getAuthorities()) .build(); } }上述代码中,
CustomUserDetailsService实现了UserDetailsService接口的loadUserByUsername方法。在该方法中,通过UserRepository从数据库中查询用户信息,如果查询不到则抛出UsernameNotFoundException异常。如果查询到用户信息,则将其转换为Spring Security需要的UserDetails对象并返回,其中包含了用户名、密码以及用户的权限信息。四、认证流程
- 用户发送包含用户名和密码的认证请求,例如通过表单提交。
- Spring Security的过滤器链捕获到请求,其中
UsernamePasswordAuthenticationFilter负责处理用户名和密码的认证。 UsernamePasswordAuthenticationFilter将用户名和密码封装成UsernamePasswordAuthenticationToken对象,并将其传递给AuthenticationManager。AuthenticationManager委托给配置的AuthenticationProvider进行认证。如果使用自定义的UserDetailsService,对应的AuthenticationProvider会调用UserDetailsService的loadUserByUsername方法从数据库中加载用户信息。AuthenticationProvider将用户输入的密码与数据库中存储的密码进行比对(使用配置的密码编码器),如果密码匹配且用户存在,则认证成功,返回一个包含用户信息和权限的Authentication对象;否则认证失败,抛出AuthenticationException异常。- 如果认证成功,
UsernamePasswordAuthenticationFilter将Authentication对象存储到SecurityContextHolder中,后续的请求处理过程中就可以通过SecurityContextHolder获取当前用户的认证信息。
五、授权流程
- 用户发送请求访问受保护的资源,例如访问
/admin/someResource。 - Spring Security的过滤器链捕获到请求,
FilterSecurityInterceptor负责处理授权相关的逻辑。 FilterSecurityInterceptor从SecurityContextHolder中获取当前用户的Authentication对象,获取用户的权限信息。FilterSecurityInterceptor根据配置的访问规则(例如在配置类中通过authorizeRequests方法配置的规则),判断当前用户是否具有访问该资源的权限。- 访问决策过程由
AccessDecisionManager负责,它会结合用户的权限信息和配置的访问规则,通过多个AccessDecisionVoter进行投票。例如,基于角色的投票器会检查用户是否具有访问该资源所需的角色,如果有则投赞成票,否则投反对票。 AccessDecisionManager根据投票结果决定是否允许用户访问资源。如果允许,则请求继续处理;如果不允许,则抛出AccessDeniedException异常,用户会收到权限不足的错误提示。
通过对Spring Security的核心组件、配置方式、认证和授权流程的学习,可以构建出安全可靠的Spring应用程序,保护应用程序的资源不被非法访问。在实际项目中,还可以根据具体需求进一步扩展和定制Spring Security的功能,例如集成第三方认证服务、实现更复杂的授权策略等。


浙公网安备 33010602011771号