本系列笔记仅供学习参考,希望能给大家带来提升和帮助
有不完善的地方欢迎评论指导,本人一定及时改正
本系列笔记基于Spring Boot 2.7.5 + Java 8 + Spring Security 5.7.4版本,先从入门案例开始学会使用之后,在深入源码高效学习
开发软甲系统时,Web安全非常重要,Java安全框架,使用最多的是Spring Security、Apache Shiro
一个功能完善的安全框架,一般都需要支持以下特性,安全框架的底层是一批过滤器,原理大似相同,学会一个,其他的上手也很容易
SpringSecurity GitHub地址
SpringSecurity 官方文档地址
SpringSecurity是Spring家族中的一个安全管理框架,相比另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。
一般来说中大型的项目基本使用的都是SpringSecurity来做安全框架,因为SpringSecurity属于Spring家族系列的,能够与Spring项目完美整合,小型项目有Shiro的比较多,因为Shiro是轻量级的安全认证框架,操作简单易上手。
一般Web应用的需要进行认证和授权
认证:SpringSecurity为身份验证提供了全面的支持,身份验证是验证进行访问的主体身份,常用方法是要求用户输入用户名(username)和密码(password),一旦执行身份验证,就知道身份并可以执行授权
授权:授权是指验证某个用户是否具有权限执行某个操作,在系统中不同用户所具有的权限是不同的,比如对淘宝商品信息来说,普通用户只能浏览,只有商家才能进行修改,一般系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限
认证和授权是SpringSecurity作为安全框架的核心功能
首先要搭建SpringBoot工作,使用Spring Initializr快速创建一个SpringBoot项目,可以直接选择引入SpringSecurity依赖:
在SpringBoot项目中打开pom文件,修改SpringBoot的版本
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.7.5version>
<relativePath/>
parent>
导入开发所需要的依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
spring-boot-starter-security是SpringBoot官方提供的启动器,提供了自动配置和依赖包管理
主要包含以下几个模块:
@RestController
public class SecurityController {
@GetMapping("/security")
public String welcomeToSecurityPage(){
return "欢迎学习Security,努力加油!";
}
}
启动程序,SpringBootSecurityApplication自动配置类,系统会自动生成一个默认的随机登录密码(因为当前没有配置用户信息,配置之后就不会在生成默认登录密码);
访问测试接口,此时被重定向到Security默认的登录页面:
用户名输入user,密码输入控制台生成的随机登录密码,登录成功后重定向到访问接口:
也可以在SpringBoot的配置文件application.yml中配置一个默认的登录用户和密码(了解就行)
spring:
security:
user:
name: jack
password: 123456
这样就完成了入门案例,上手操作非常简单,因为认证和授权的过程都被SpringSecurity底层给我们做了。
下面我们需要深入了解SpringSecurity相关知识,熟悉掌握SpringSecurity
SpringSecurity的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。这里我们可以看看入门案例中的过滤器
图中只展示了核心过滤器,其他非核心的过滤器并没有在图中展示
UsernamePasswordAuthenticationFilter:负责处理我们在登录页面填写了用户名和密码后的登录请求,入门案例的认证工作主要有它负责
ExceptionTranslationFilter:异常过滤器,处理过滤器链中抛出的任何AccessDeniedException和AuthenticationException。
FilterSecurityInterceptor:负责权限校验的过滤器,最底层的过滤器,真正的调用后台服务
这个只是入门案例的流程图,不求记住大致做个了结就行,知道运行过程,后面我们自定义开发自己的配置
在Spring Security中,认证、授权等功能都是基于过滤器来完成的,下列为Spring Security中常见的过滤器,混个眼熟
过滤器 | 过滤器作用 | 是否默认加载 |
---|---|---|
ChannelProcessingFilter | 过滤请求协议 ,如HTTP和HTTPS | NO |
WebAsyncManagerIntegrationFilter | 将WebAsyncManager与SpringSecurity上下文进行集成 | YES |
SecurityContextPersistenceFilter | 在处理请求之前,将安全信息加载到SecurityContextHolder中以便后续使用。请求结束后再擦除SecurityContextHolder中的信息 | YES |
HeaderWriterFilter | 头信息加入到响应中 | YES |
CorsFilter | 处理跨域请求问题 | NO |
CsrfFilter | 处理CSRF攻击 | YES |
LogoutFilter | 处理注销登录 | YES |
OAuth2AuthorizationRequestRedirectFilter | 处理OAuth2认证重定向 | NO |
Saml2WebSsoAuthenticationRequestFilter | 处理SAML认证 | NO |
X509AuthenticationFilter | 处理X509认证 | NO |
AbstractPreAuthenticatedProcessingFilter | 处理预认证问题 | NO |
CasAuthenticationFilter | 处理CAS单点登录 | NO |
OAuth2LoginAuthenticationFilter | 处理OAuth2认证 | NO |
Saml2WebSsoAuthenticationFilter | 处理SAML认证 | NO |
UsernamePasswordAuthenticationFilter | 处理表单登录 | YES |
OpenIDAuthenticationFilter | 处理OpenID认证 | NO |
DefaultLoginPageGeneratingFilter | 配置默认登录页面 | YES |
DefaultLogoutPageGeneratingFilter | 配置默认注销页面 | YES |
ConcurrentSessionFilter | 处理session有效期 | NO |
DigestAuthenticationFilter | 处理HTTP摘要认证 | NO |
BearerTokenAuthenticationFilter | 处理OAuth2认证时的Access Token | NO |
BasicAuthenticationFilter | 处理HttpBasic登录 | YES |
RequestCacheAwareFilter | 处理请求缓存 | YES |
SecurityContextHolderAwareRequestFilter | 包装原始请求 | YES |
JaasApiIntegrationFilter | 处理JAAS认证 | NO |
RememberMeAuthenticationFilter | 处理RememberMe登录 | NO |
AnonymousAuthenticationFilter | 配置匿名认证 | YES |
OAuth2AuthorizationCodeGrantFilter | 处理OAuth2认证中的授权码 | NO |
SessionManagementFilter | 处理Session并发问题 | YES |
ExceptionTranslationFilter | 处理异常/授权中的情况 | YES |
FilterSecurityInterceptor | 处理授权 | YES |
SwitchUserFilter | 处理账号切换 | NO |
以上过滤器是否默认加载是指引入Spring Security依赖之后,开发者不做任何配置时,会自动加载的过滤器
当什么也没有配置的时候,账号和密码是由SpringSecurity定义生成,而在实际开发中账号和密码都是从数据库中查询出来,所以我们要通过自定义逻辑控制认证逻辑
总结:查询数据库用户名和密码的过程
//UseDetailService接口
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
//自定义实现类实现UserDetailService接口
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//查询用户信息
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUserName,username);
User user = userMapper.selectOne(queryWrapper);
//如果没有查询到用户就抛出异常
if (Objects.isNull(user)){
throw new UsernameNotFoundException("查无该用户,请重试");
}
//TODO 查询对应的权限信息
return new LoginUser(user);
}
}
数据加密接口,用于返回User对象里面的密码加密
public interface PasswordEncoder {
//表示把参数按照特定的解析规则进行解析
String encode(CharSequence rawPassword);
//表示验证从存储中获取的编码密码与编码后提交的原始密码是否匹配,如果
//密码匹配,则返回true;如果不匹配;则返回false,第一个参数表示需要
//被解析的密码,第二个参数表示存储的密码
boolean matches(CharSequence rawPassword, String encodedPassword);
//表示如果解析的密码能够再次进行解析且达到更安全的结果则返回true
//否则返回false,默认返回false
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
接口实现类:
BCryptPasswordEncoder是SpringSecurity官方推荐的密码解析器,平时多使用这个解析器
BCryptPasswordEncoder是基于Hash算法实现的单向加密,可以通过strength控制加密强度,默认10
方法实例演示
@Test
public void test01() {
// 创建密码解析器
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// 对密码进行加密
String password= bCryptPasswordEncoder.encode("password");
// 打印加密之后的数据
System.out.println("加密之后数据:\t" + password);
//判断原字符加密后和加密之前是否匹配
boolean result = bCryptPasswordEncoder.matches("password", password);
// 打印比较结果
System.out.println("比较结果:\t" + result);
}
开发者所见到的Spring Security提供的功能,都是通过这些过滤器实现的,这些过滤器按照既定的优先级排列,最终形成一个过滤器链,开发者也可以自定义过滤器,并通过==@Order==注解去调整自定义过滤器在过滤器链中的位置
需要注意的是,默认过滤器并不是直接放在Web项目的原生过滤器中,而是通过一个FilterChainProxy来统一管理,Spring Security中的过滤器链通过FilterChainProxy嵌入到Web项目的原生过滤器链中