简介:
一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方式的安全框架。简单的说就是访问权限控制。
应用的安全性包括:用户认证(Authentication)和用户授权(Authorization)
框架原理:
对web资源保护 --------->Filter
对方法进行保护 --------->AOP
主要过滤器:
- WebAsyncManagerIntegrationFilter
- SecurityContextPersistenceFilter
- HeaderWriterFilter
- CorsFilter
- LogoutFilter
- RequestCacheAwareFilter
- SecurityContextHolderAwareRequestFilter
- AnonymousAuthentivationFilter
- SessionManagementFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
- UsernamePasswordAuthenticationFilter
- BasicAuthticationFilter
框架核心组件:
- SecurityContextHolder :提供对SecurityContext的访问
- SecurityContext :持有Authentication对象和其他可能需要的信息
- AuthenticationManager其中可以包含多个AuthenticationProvider
- ProviderManager对象为AuthenticationManager接口的实现类
- AuthenticationProvider主要用来进行认证操作的类,调用其中的authenticate()方法去进行认证操作
- Authentication:SpringSecurity方式的认证主体
- GrantedAuthority:对认证主体的应用层面的授权,含当前用户的权限信息。通常使用角色表示
- UserDetail:构建Authentication对象必须的信息,可以自定义,可能需要访问数据库得到
- UserDetailService:通过username构建UserDetail对象,通过loadUserByUsername根据userName获取UserDetail对象
自定义安全配置的加载机制:
1.添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.配置yml文件
server:
port: 8081
spring:
datasource:
username: root
password: root
url: jdbc:mysql://127.0.0.1:3306/three_point
driver-class-name: com.mysql.jdbc.Driver
jpa:
hibernate:
#使用了JPA的自动建表,根据实体类自动生成数据表(需要先创建数据库)
ddl-auto: update
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
open-in-view: false
3.实体类
//用户类
@Entity
@Table
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
@ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)
private List<Role> roles;
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
//角色类
@Entity
@Table
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
}
4.Dao层,使用jpa操作
@Repository
public interface UserRepository extends JpaRepository<User,Integer> {
User findByUsername(String username);
}
UserDetailService实现类,用于Security查询角色进行认证,从数据库中获取用户权限信息
/**
* 这里使用的是User不继承UserDetails这个接口,而将添加认证用户的操作放在了上面这个类中,还有另外一种实现方式
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserRepository userRepository;
/**
* loadUserByUsername方法重写
* @param s
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = userRepository.findByUsername(s);
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role:
user.getRoles()) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return new org.springframework.security.core.userdetails.User(user.getUsername(),user.getPassword(),authorities);
}
}
5.Security配置类
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 获取到数据库中的用户信息
* @return
*/
@Bean
UserDetailsService detailsService(){
return new UserDetailsServiceImpl();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/**
* passwordEncoder(passwordEncoder())里面的passwordEncoder()为我们定义的bean
* 这样在登录的时候就会使用我们选择编码方式进行验证
*/
auth.userDetailsService(detailsService()).passwordEncoder(passwordEncoder());
}
/**
* 需要忽略掉的web资源
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/config/**","/css/**","/fonts/**","/img/**","/js/**");
}
/**
* 决定哪些请求会被拦截以及请求该怎么处理
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//项目的主路径可以放行
.antMatchers("/").permitAll()
//注册可以放行
.antMatchers("/registry").permitAll()
//其他请求必须要经过验证
.anyRequest().authenticated()
//注销允许访问
.and().logout().permitAll()
//允许表单登陆
.and().formLogin()
;
//关闭默认的csrf认证
http.csrf().disable();
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
6.Controller控制层
@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")
@PreAuthorize("#id<10 and principal.username.equals(#username) and #user.username.equals('abc')")
@PostAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")
@PostAuthorize("returnObject%2==0") //判断返回的id是否是双数
这两个注解支持and or 表达式 @PreAuthorize是在方法调用前进行权限检查 @PostAuthorize是在方法调用完成后进行权限检查
@PreFilter("filterObject%2==0")
@PostFilter("filterObject%4==0")
@PreFilter是对集合类型的参数进行过滤 @PostFilter是对集合类型的返回值进行过滤
@Controller
@EnableGlobalMethodSecurity(prePostEnabled = true)//启动@PreAuthorize()注解的作用
public class IndexController {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@RequestMapping("/")
@ResponseBody
public String home(){
return "hello spring boot";
}
@RequestMapping("/hello")
@ResponseBody
public String hello(){
return "hello world";
}
@RequestMapping("/registry")
@ResponseBody
public String registry(User user){
userRepository.save(new User(user.getUsername(),passwordEncoder.encode(user.getPassword())));
return "save successful!";
}
@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")
@RequestMapping("roleAuth")
@ResponseBody
public String roleAuth(){
return "hello Auth";
}
@PreAuthorize("#id<10 and principal.username.equals(#username)" +
"and #user.username.equals('abc')")
@RequestMapping("/test")
public String test(Integer id,String username,User user){
return "hello test";
}
/**
* @PostAuthroized实在方法调用后检查权限
* @param id
* @return
*/
@PostAuthorize("returnObject%2==0") //判断返回的id是否是双数
@RequestMapping("/test1")
public Integer test1(Integer id){
// ....
return id;
}
/**
* @PreFilter("filterObject%2==0")
* 对集合类型的参数进行过滤
* @PostFilter("filterObject%4==0")
* 对集合类型的返回值进行过滤
* @param idList
* @return
*/
@PreFilter("filterObject%2==0")
@PostFilter("filterObject%4==0")
@RequestMapping("/test2")
public List<Integer> test2(List<Integer> idList){
// ....省略部分代码
return idList;
}
}
总结:
优点:提供了一套安全的框架,并且这个框架是可以使用的。提供了很多用户认证的功能,实现相关接口即可,节约大量开发工作。基于spring,易于集成到spring项目中,且封装了许多方法
缺点:配置文件比较多,角色被”编码“到配置文件和源文件中,RBAC不明显。对于系统中用户,角色,权限之间的关系,没有可操作的界面。大数据量的情况下,springsecurity几乎不可用