Spring Security是一个专门针对Sping应用系统的安全框架,充分利用了Spring框架的依赖注入和AOP功能,为Spring应用系统提供安全访问控制解决方案。
在Spring Security安全框架中,有两个重要概念,即授权(Authorization)和认证(Authentication)。授权即确定用户在当前应用系统下所拥有的功能权限;认证即确认用户访问当前系统的身份。
Spring Security的适配器
Spring Security为Web应用提供了一个适配器类WebSecurityConfigurerAdapter,该类实现了WebSecurityConfigurer<WebSecurity>接口,并提供了两个configure方法用于认证和授权操作。
开发者创建自己的Spring Security适配器类是非常简单的,只需要定义一个继承WebSecurityConfigurerAdapter的类,并在该类中使用@Configuration注解,就可以通过重写两个configure方法来配置所需要的安全配置。
@Configuration
public class MySecurityConfigurerAdapter extends WebSecurityConfigurerAdapter{
/**
* 用户认证
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
/**
* 请求授权
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
}
}
Spring Security的用户认证
在Spring Security的适配器类中,通过重写configure(AuthenticationManagerBuilder auth)方法完成用户认证。
1.内存中的用户认证
2.通用的用户认证
内存中的用户认证
使用AuthenticationManagerBuilder的inMemoryAuthentication()方法可以添加在内存中的用户,并给用户指定角色权限。示例代码如下:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("chenheng").password("123456").roles("ADMIN","DBA"); auth.inMemoryAuthentication().withUser("zhangsan").password("123456").roles("USER");
}
上述示例代码中添加了两个用户,一个是用户名为“chenheng”,密码为“123456”,用户权限为“ROLE_ADMIN”和“ROLE_DBA”;一个是用户名为“zhangsan”,密码为“123456”,用户权限为“ROLE_USER”。“ROLE_”是Spring Security保存用户权限的时候,默认加上。
通用的用户认证
我们在实际应用中,可以查询数据库获取用户和权限,这时我们需要自定义实现org.springframework.security.core.userdetails.UserDetailsService接口的类,并重写public UserDetails loadUserByUsername(String username)方法查询对应的用户和权限,然后注册Service类完成用户认证。
Spring Security的请求授权
在Spring Security的适配器类中,通过重写configure(HttpSecurity http)方法完成用户授权。在configure(HttpSecurity http)方法中,使用HttpSecurity的authorizeRequests()方法的子节点给指定用户授权访问URL模式。我们可以通过antMatchers方法使用Ant风格匹配URL路径。匹配请求路径后,可以针对当前用户对请求进行安全处理。
![](https://img2020.cnblogs.com/blog/1020928/202110/1020928-20211007114527449-1591373454.png)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//首页、登录、注册页面、登录注册功能、以及静态资源过滤掉,即可任意访问
.antMatchers("/toLogin", "/toRegister", "/", "/login", "/register", "/css/**", "/fonts/**", "/js/**").permitAll()
//这里默认追加ROLE_,/user/**是控制器的请求匹配路径
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasAnyRole("ADMIN", "DBA")
//其他所有请求登录后才能访问
.anyRequest().authenticated()
.and()
//将输入的用户名与密码和授权的进行比较
.formLogin()
.loginPage("/login").successHandler(myAuthenticationSuccessHandler)
.usernameParameter("username").passwordParameter("password")
//登录失败
.failureUrl("/login?error")
.and()
//注销行为可任意访问
.logout().permitAll()
.and()
//指定异常处理页面
.exceptionHandling().accessDeniedPage("/deniedAccess");
}
http.authorizeRequests():开始进行请求权限设置。
antMatchers("/toLogin", "/toRegister", "/", "/login", "/register", "/css/**", "/fonts/**", "/js/**").permitAll():“/toLogin”、“/toRegister”、“/”、“/login”、“/register”等请求以及静态资源过滤掉,即任意用户可访问。
antMatchers("/user/**").hasRole("USER"):请求匹配“/user/**”,拥有“ROLE_USER”角色的用户可访问。
antMatchers("/admin/**").hasAnyRole("ADMIN", "DBA"):请求匹配“/admin/**”,拥有“ROLE_ADMIN”或“ROLE_DBA”角色的用户可访问。
anyRequest().authenticated():其余所有请求都需要认证(用户登录后)才可访问。formLogin():开始设置登录操作。loginPage("/login").successHandler(myAuthenticationSuccessHandler):设置登录的访问地址以及登录成功处理操作。
usernameParameter("username").passwordParameter("password"):登录时接收参数username的值作为用户名,接收参数password的值作为密码。
failureUrl("/login?error"):指定登录失败后转向的页面和传递的参数。
logout().permitAll():注销操作,所有用户均可访问。
exceptionHandling().accessDeniedPage("/deniedAccess"):指定异常处理页面。
Spring Security的核心类
Spring Security的核心类包括Authentication、DaoAuthenticationProvider、GrantedAuthority、PasswordEncoder、SecurityContextHolder、UserDetails和UserDetailsService。
1.Authentication
Authentication用来封装用户认证信息的接口,在用户登录认证之前,Spring Security将相关信息封装为一个Authentication具体实现类的对象,在登录认证成功后将生成一个信息更全面、包含用户权限等信息的Authentication对象,然后将该对象保存在SecurityContextHolder所持有的SecurityContext中,方便后续程序进行调用,如当前用户名、访问权限等。
SecurityContextHolder
SecurityContextHolder顾名思义是用来持有SecurityContext的类。SecurityContext中包含当前认证用户的详细信息。Spring Security使用一个Authentication对象描述当前用户的相关信息。例如,最常见的是获得当前登录用户的用户名和权限。
//获得当前用户名称
SecurityContextHolder.getContext().getAuthentication().getName();
//获得当前用户权限
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
List<String> roles = new ArrayList<String>();
for (GrantedAuthority ga : authentication.getAuthorities()) {
roles.add(ga.getAuthority());
}
3.UserDetails
UserDetails是Spring Security的一个核心接口。该接口定义了一些可以获取用户名、密码、权限等与认证相关的信息的方法。通常需要在应用中获取当前用户的其他信息,如Email、电话等。这时只包含认证相关的UserDetails对象可能就不能满足我们的需要了。我们可以实现自己的UserDetails,在该实现类中定义一些获取用户其他信息的方法,这样我们就可以直接从当前SecurityContext的Authentication的principal中获取用户的其他信息。
Authentication.getPrincipal()的返回类型是Object,但通常返回的其实是一个UserDetails的实例,通过强制类型转换可以将Object转换为UserDetails类型。
4.UserDetailsService
UserDetails是通过UserDetailsService的loadUserByUsername(String username)方法加载的。UserDetailsService也是一个接口,也需要实现自己的UserDetailsService来加载自定义的UserDetails信息。
登录认证时,Spring Security将通过UserDetailsService的loadUserByUsername(String username)方法获取对应的UserDetails进行认证,认证通过后将该UserDetails赋给认证通过的Authentication的principal,然后再将该Authentication保存在SecurityContext中。在应用中,如果需要使用用户信息,可以通过SecurityContextHolder获取存放在SecurityContext中的Authentication的principal,即UserDetails实例。
5.GrantedAuthority
Authentication的getAuthorities()方法可以返回当前Authentication对象拥有的权限(一个GrantedAuthority类型的数组),即当前用户拥有的权限。GrantedAuthority是一个接口,通常是通过UserDetailsService进行加载,然后赋给UserDetails。
6.DaoAuthenticationProvider
在Spring Security安全框架中,默认使用DaoAuthenticationProvider实现AuthenticationProvider接口进行用户认证的处理。DaoAuthenticationProvider进行认证时,需要一个UserDetailsService来获取用户信息UserDetails。当然我们可以实现自己的AuthenticationProvider,进而改变认证方式。
7.PasswordEncoder
在Spring Security安全框架中,是通过PasswordEncoder接口完成对密码的加密。Spring Security对PasswordEncoder有多种实现,包括MD5加密、SHA-256加密等,开发者只需直接使用即可。在Spring Boot应用中,使用BCryptPasswordEncoder加密是较好的选择。BCryptPasswordEncoder使用BCrypt的强散列哈希加密实现,并可以由客户端指定加密强度,强度越高安全性越高。
Spring Security的验证机制
Spring Security的验证机制是由许多Filter实现的,Filter将在Spring MVC前拦截请求,主要包括注销Filter(LogoutFilter)、用户名密码验证Filter(UsernamePasswordAuthenticationFilter)等内容。Filter再交由其他组件完成细分的功能,最常用的UsernamePasswordAuthenticationFilter会持有一个AuthenticationManager引用,AuthenticationManager是一个验证管理器,专门负责验证。AuthenticationManager持有一个AuthenticationProvider集合,AuthenticationProvider是做验证工作的组件,验证成功或失败之后调用对应的Hanlder(处理)。
Spring Boot的支持
在Spring Boot应用中,我们只需引入spring-boot-starter-security依赖即可使用Spring Security安全框架,这是因为Spring Boot对Spring Security提供了自动配置功能。从org.springframework.boot.autoconfigure.security.SecurityProperties类中,我们可以看到使用以“spring.security”为前缀的属性配置了Spring Security的相关默认配置。因此,我们在实际应用开发中只需自定义一个类继承WebSecurityConfigurerAdapter,无须使用@EnableWebSecurity注解,即可自己扩展Spring Security的相关配置。