spring security的基本原理、配置使用方式
作者:猩火燎猿
一、Spring Security 基本原理
Spring Security 主要通过一系列过滤器链(Filter Chain)来实现安全控制。核心流程如下:
- 请求进入过滤器链(如
UsernamePasswordAuthenticationFilter、BasicAuthenticationFilter等)。 - 认证管理:通过
AuthenticationManager进行身份认证。 - 授权管理:通过
AccessDecisionManager判断当前用户是否有权限访问资源。 - 异常处理:如认证失败、权限不足等。
二、Spring Security 配置方式
1. 依赖引入
如果是 Spring Boot 项目,只需在 pom.xml 添加:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>2. 基本配置类(推荐方式:Java Config)
创建一个配置类,继承 WebSecurityConfigurerAdapter(Spring Security 5.7+ 推荐实现 SecurityFilterChain Bean)。
方式一:使用WebSecurityConfigurerAdapter(旧版)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() // 配置授权规则
.antMatchers("/public/**").permitAll() // 允许所有人访问
.antMatchers("/admin/**").hasRole("ADMIN") // 仅ADMIN角色可访问
.anyRequest().authenticated() // 其他请求需认证
.and()
.formLogin() // 使用表单登录
.loginPage("/login") // 自定义登录页
.permitAll()
.and()
.logout()
.permitAll();
}
}方式二:使用SecurityFilterChainBean(推荐)
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.antMatchers("/public/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
)
.logout(logout -> logout.permitAll());
return http.build();
}
}3. 用户认证配置
可以自定义用户信息来源,比如内存、数据库等。
内存用户配置
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("admin")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}自定义UserDetailsService(如从数据库读取用户)
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
return new User(user.getUsername(), user.getPassword(), getAuthorities(user));
}
private Collection<? extends GrantedAuthority> getAuthorities(UserEntity user) {
// 根据实际情况返回角色列表
return AuthorityUtils.createAuthorityList("ROLE_USER");
}
}4. 常见功能配置
1. 禁用 CSRF(开发调试时可用,生产建议开启)
http.csrf().disable();
2. 配置静态资源免认证
.authorizeRequests()
.antMatchers("/css/**", "/js/**", "/images/**").permitAll()3. 配置自定义登录成功/失败处理
.formLogin()
.successHandler(mySuccessHandler)
.failureHandler(myFailureHandler)4. 配置 Remember-Me 功能
.rememberMe()
.tokenValiditySeconds(86400)
.key("mySecretKey")三、常见使用场景举例
1. RESTful 接口安全(无状态)
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();2. 角色和权限控制
.authorizeRequests()
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.antMatchers("/admin/**").hasRole("ADMIN")3. 方法级安全
开启注解支持:
@EnableGlobalMethodSecurity(prePostEnabled = true)
在 Service 层方法上使用:
@PreAuthorize("hasRole('ADMIN')")
public void adminMethod() { ... }四、常见问题
密码存储建议使用加密算法(如 BCrypt):
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}- 登录接口自定义返回 JSON:需自定义
AuthenticationSuccessHandler和AuthenticationFailureHandler。 - 前后端分离项目建议关闭 CSRF,并使用 JWT 或 Token 认证。
五、官方文档与参考
六、进阶配置与应用
1. 自定义登录逻辑(前后端分离常用)
对于前后端分离项目,通常不使用 Spring Security 默认的表单登录,而是自定义登录接口,并返回 JSON 响应。
步骤:
1. 禁用默认登录页面和 CSRF
http
.csrf().disable()
.formLogin().disable();2. 自定义登录接口
在 Controller 层实现登录接口,手动认证用户:
@RestController
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
try {
Authentication authentication = authenticationManager.authenticate(authToken);
// 认证成功,生成 JWT 或返回用户信息
return ResponseEntity.ok(/* token or user info */);
} catch (AuthenticationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("登录失败");
}
}
}3. 配置 AuthenticationManager Bean
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}2. JWT(Json Web Token)整合
JWT 适用于前后端分离和 RESTful API 项目,实现无状态认证。
1. 依赖引入
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>2. JWT 工具类
public class JwtUtil {
private static final String SECRET_KEY = "yourSecretKey";
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 86400000))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static String getUsernameFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
}3. JWT 过滤器
在 Spring Security 过滤器链中添加 JWT 校验:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
String jwt = token.replace("Bearer ", "");
String username = JwtUtil.getUsernameFromToken(jwt);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
chain.doFilter(request, response);
}
}4. 注册过滤器
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
3. 异常处理(认证失败/权限不足)
1. 未认证异常处理
http.exceptionHandling()
.authenticationEntryPoint((request, response, authException) -> {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"error\":\"未认证\"}");
});2. 权限不足异常处理
.accessDeniedHandler((request, response, accessDeniedException) -> {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("{\"error\":\"权限不足\"}");
});4. 常见安全防护措施
1. 防止密码明文存储
始终使用加密算法存储密码,比如 BCrypt:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}2. 防止 XSS/CSRF 攻击
- 默认开启 CSRF,前后端分离项目建议关闭并采用 Token 防护。
- 对输入内容进行过滤和转义。
3. 防止 Session Fixation(会话固定攻击)
http.sessionManagement()
.sessionFixation().migrateSession();4. 限制登录尝试次数(防暴力破解)
可结合 Redis、数据库等记录登录失败次数,超过阈值后锁定账号。
5. 方法级安全进阶
@Secured("ROLE_ADMIN")@PreAuthorize("hasAuthority('sys:user:view')")@PostAuthorize("returnObject.username == authentication.name")
使用方式:
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
七、Spring Security 与 OAuth2、SSO 等整合
Spring Security 支持与 OAuth2、OpenID Connect、CAS 等单点登录系统整合,具体可参考 Spring Security OAuth2 文档。
- Spring Security OAuth2 Client:实现第三方登录(如微信、支付宝、GitHub)。
- Spring Authorization Server:自建 OAuth2 授权服务。
八、调试与排错建议
- 查看过滤器链
- 可在启动日志中查找
FilterChainProxy,或通过断点调试。
- 可在启动日志中查找
- 认证流程排查
- 重点关注
UserDetailsService、AuthenticationManager、PasswordEncoder配置是否正确。
- 重点关注
- 权限控制问题
检查角色前缀(ROLE_)、方法注解是否生效。
总结
Spring Security 配置灵活、功能强大,推荐使用 Java 配置方式(即 SecurityFilterChain Bean),并根据实际需求自定义用户认证、授权、异常处理等。对于前后端分离、RESTful API 项目,建议使用无状态认证(如 JWT),并合理配置安全策略。
到此这篇关于spring security 配置使用的文章就介绍到这了,更多相关spring security 配置使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
