Spring 安全框架概述
Spring Security 是 Spring 生态系统中用于提供身份验证和授权功能的强大框架,它可以无缝集成到 Spring Boot 和 Spring 应用中,帮助开发者保护应用程序的资源,防止未授权的访问。以下是 Spring Security 的一些核心特性:
- 身份验证(Authentication):验证用户的身份,常见的方式有表单登录、HTTP 基本认证、OAuth 等。
- 授权(Authorization):确定已认证用户是否有权限访问特定的资源,支持基于角色、权限的访问控制。
- 防止常见攻击:如跨站请求伪造(CSRF)、会话固定攻击等。
- 集成多种认证方式:可以与 LDAP、数据库、OAuth 提供商等集成。
实现基于角色的访问控制(RBAC)步骤
1. 添加依赖
如果你使用的是 Spring Boot 项目,在 pom.xml
中添加 Spring Security 的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2. 配置 Spring Security
创建一个配置类,继承 WebSecurityConfigurerAdapter
类,并重写相关方法来配置安全规则。以下是一个示例:
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll() // 允许所有用户访问 /public 路径下的资源
.antMatchers("/admin/**").hasRole("ADMIN") // 只有具有 ADMIN 角色的用户可以访问 /admin 路径下的资源
.anyRequest().authenticated() // 其他请求需要用户认证
.and()
.formLogin() // 使用表单登录
.and()
.httpBasic(); // 支持 HTTP 基本认证
}
@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
UserDetails admin =
User.withDefaultPasswordEncoder()
.username("admin")
.password("password")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
}
在上述代码中:
configure(HttpSecurity http)
方法用于配置 HTTP 请求的访问规则,antMatchers
方法用于指定路径模式,permitAll()
表示允许所有用户访问,hasRole()
表示只有具有指定角色的用户可以访问,authenticated()
表示需要用户认证。userDetailsService()
方法用于创建用户信息,这里使用了内存中的用户存储,实际应用中可以从数据库或其他数据源获取用户信息。
3. 创建控制器
创建一个简单的控制器来测试访问控制:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/public/hello")
public String publicHello() {
return "Hello, public!";
}
@GetMapping("/admin/hello")
public String adminHello() {
return "Hello, admin!";
}
@GetMapping("/user/hello")
public String userHello() {
return "Hello, user!";
}
}
4. 运行和测试
启动 Spring Boot 应用,尝试访问不同的路径:
- 访问
/public/hello
:无需登录即可访问。 - 访问
/admin/hello
:只有使用具有ADMIN
角色的用户登录后才能访问。 - 访问
/user/hello
:使用任何已认证的用户登录后都可以访问。
基于数据库的用户和角色管理
上述示例使用了内存中的用户存储,实际应用中通常会使用数据库来存储用户和角色信息。以下是一个简单的基于数据库的实现步骤:
- 创建数据库表,如
users
表和roles
表,以及关联表user_roles
。 - 创建实体类和 Repository 接口来操作数据库。
- 实现
UserDetailsService
接口,从数据库中获取用户信息。
import org.springframework.beans.factory.annotation.Autowired;
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;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
return org.springframework.security.core.userdetails.User
.withUsername(user.getUsername())
.password(user.getPassword())
.roles(user.getRoles().stream().map(Role::getName).toArray(String[]::new))
.build();
}
}
完整代码示例
以下是包含上述配置和控制器的完整代码示例:
SecurityConfig.java
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
UserDetails admin =
User.withDefaultPasswordEncoder()
.username("admin")
.password("password")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
}
HelloController.java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/public/hello")
public String publicHello() {
return "Hello, public!";
}
@GetMapping("/admin/hello")
public String adminHello() {
return "Hello, admin!";
}
@GetMapping("/user/hello")
public String userHello() {
return "Hello, user!";
}
}
通过以上步骤,你可以在 Spring 应用中实现基于角色的访问控制。