简介
一般来说,Web应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分,这两点也是SpringSecurity重要核心功能
- 用户认证。验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码,系统通过校验用户名和密码来完成认证过程。通俗点就是系统认为用户是否能登录
- 用户授权。验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。通俗点讲就是系统判断用户是否有权限去做某些事情
和Shiro对比
- SpringSecurity
- 和Spring无缝整合
- 全面的权限控制
- 专门为Web开发设计
- 旧版本不能脱离Web环境使用
- 新版本对整个框架进行了分层抽取,分成了核心模块和Web模块。单独引入核心模块就可以脱离Web环境
- 重量级
- Shiro
- Apache旗下的轻量级权限控制框架
- 轻量级,主张把复杂的事情变简单。针对性能有更高要求的互联网应用有更好表现
- 通用性
- 好处:不限于Web环境,可以脱离Web环境使用
- 缺陷:在Web环境下一些特定的需求需要手动编写代码定制
一般来说,常见的安全管理技术栈组合是 SSM+Shiro
或 SpringBoot/SpringCloud+SpringSecurity
简单示例
新建一个Springboot项目,引入SpringSecurity依赖
<!--spring security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
新建一个Controller类,测试一下
@GetMapping(value = "/hello")
public Result test(){
return Result.success("hello");
}
浏览器请求,发现和以前不一样了
需要账号密码登录,默认的账号是user,密码是项目启动时给出的
可以看到这里的密码是b99d0223-4009-4009-9a2c-1c04470cc1f1,登录后就可以成功获取请求内容
但是一般开发时,用户名和密码都是自己从数据库查的,这里就用到下面的两个重要接口
两个重要的接口
UserDetailService
接口:查询数据库用户名和密码
- 创建类继承
UsernamePasswordAuthorizationFilter
,重写三个方法 - 创建类实现
UserDetailService
,编写查询数据过程,返回User
对象买这个User
对象是安全框架提供的对象
PasswordEncode
接口:数据加密接口,用于返回User
对象里面密码加密
Web权限方案
设置账号、密码
这里有三种方法实现,在application.yml文件中配置、通过配置类配置、自定义编写实现类,以下依次实现
在application.yml中配置
spring:
security:
user:
name: myuser
password: 123456
通过配置类
@Configuration
public class SecuritySimpleConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
//密码加密
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String password = encoder.encode("123456");
auth.inMemoryAuthentication().withUser("bob").password(password).roles("");
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
配置类继承WebSecurityConfigurerAdapter
类,重写configure
方法,在方法中设置用户名和密码并对密码加密
自定义实现类
- 创建配置类,设置使用哪个
userDetailService
实现类 编写实现类,返回
User
对象,User
对象有用户名、密码、操作权限@Configuration
public class SecurityComplexConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
@Service("userDetailsService")
public class WebUserServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
List<GrantedAuthority> role = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
return new User("mary", new BCryptPasswordEncoder().encode("123456"), role);
}
}
这里没连数据库,仅做演示
使用
注解使用
- @Secured
用户具有某个角色,可以访问方法
使用时,需要现在启动类或配置类上开启注解
@EnableGlobalMethodSecurity(securedEnabled = true)
在controller的方法上使用注解,设置角色
@Secured({"ROLE_one","ROLE_two"})
可以设置多个角色,并且在角色名称前加前缀“ROLE_”,即如果角色是“one”,则这里值是“ROLE_one”
- @PreAuthorize
使用时,需要现在启动类或配置类上开启注解
@EnableGlobalMethodSecurity(prePostEnabled = true)
在controller的方法上使用注解,四个用法选一个
@PreAuthorize("hasRole('ROLE_admin')")
@PreAuthorize("hasAnyRole('ROLE_admin,Role_one')")
@PreAuthorize("hasAuthority('menu:system')")
@PreAuthorize("hasAnyAuthority('menu:system,menu:one')")
- @PostAuthorize
这个注解用的不多,作用是在方法执行后再进行权限验证,适合验证带有返回值的权限
先开始注解
@EnableGlobalMethodSecurity(prePostEnabled = true)
在controller的方法上使用注解
@PostAuthorize("hasAuthority('menu:system')")
- @PreFilter
进入控制器之前对数据进行过滤