SpringSecurity学习笔记
保护web应用的安全----参考的详细教程,点击这里,感谢大佬!
Spring Security(一)–Architecture Overview----参考的详细教程,点击这里,感谢大佬!
Spring Security(二)–Guides----参考的详细教程,点击这里,感谢大佬!
Spring Security(三)–核心配置解读----参考的详细教程,点击这里,感谢大佬!
Spring Security(四)–核心过滤器源码分析----参考的详细教程,点击这里,感谢大佬!
Spring Security(五)–动手实现一个IP_Login----参考的详细教程,点击这里,感谢大佬!
注:鄙人不才,此篇文章约等于搬运,写此文章的目的一是为了加深学习印象,二是希望能通过发布文章的方式来督促自己,建议大家直接看上述链接中的原教程,讲解更加通俗易懂。
对于任何一门技术都无耻的保持着得过且过的心态,多年后发现,基础根基不牢,只会在错误的道路上渐行渐远。。。
幡然醒悟,一定痛改前非,自我救赎就从这篇SpringSecurity开始!
感谢互联网时代,让每一门技术在网上都有教程可循!
感谢技术大佬们的无私分享!
一、SpringSecurity是什么
众所周知,SpringSecurity是Spring的一个安全框架,核心功能为认证(你是谁)、授权(你能干点啥)、攻击防护(防止伪造身份),具体如何使用呢,请慢慢看~
二、安装SpringSecurity
maven项目下,直接在pom.xml中引入依赖即可,代码入下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.1.1.RELEASE</version>
</dependency>
三、核心组件
1、SecurityContextHolder
SecurityContextHolder用于存储安全上下文(security content)的信息。当前操作的用户是谁,拥有哪些权限,是否已经被认证了,都被保存在这里。SecurityContextHolder默认使用 ThreadLocal (JDK提供的,保证多线程安全的方法) 策略来存储认证信息。
因为身份信息存储于SecurityContextHolder中,并且与线程是绑定的,所以我们可以在程序的任何地方,通过静态方法,来获取用户的身份信息。代码举例如下
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
//getAuthentication()返回了认证信息,getPrincipal()返回了身份信息
if (principal instanceof UserDetails) {
//UserDetails是SpringSecurity封装的一个身份信息的接口
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
2、Authentication
接口源码:
//Authentication是SpringSecurity中最高级别的身份/认证的抽象
public interface Authentication extends Principal, Serializable {
//权限信息列表
Collection<? extends GrantedAuthority> getAuthorities();
//密码信息,用户输入的密码字符串,在认证后通常会被移除
Object getCredentials();
//细节信息,它记录了访问者的ip地址和sessionId的值。
Object getDetails();
//最重要的身份信息,大部分情况下返回的是UserDetails接口的实现类,也是框架中的常用接口之一
Object getPrincipal();
boolean isAuthenticated();
void setAuthenticated(boolean var1) throws IllegalArgumentException;
}
SpringSecurity身份验证过程:
- 认证拦截器获取到用户的登录账号和密码,封装成Authentication,通常是UsernamePasswordAuthenticationToken
- AuthenticationManager身份认证器对Authentication进行认证,认证通过后,返回一个带着信息(权限信息,身份信息,细节信息,但密码通常会被移除)的Authentication实例。
- SecurityContextHolder安全上下文容器将第2步中填充了信息的Authentication,通过SecurityContextHolder.getContext().setAuthentication(…);方法,设置到其中。
3、AuthenticationManager
在一个项目中,登录方式可以有多种:账号+密码登录、手机号+验证码登录、邮箱+密码登录等等,所以AuthenticationManager是不会进行直接验证的,其接口的常用实现类ProviderManager内部会维护一个List< AuthenticationProvider>列表。账号+密码登录、手机号+验证码登录、邮箱+密码登录对应着三个AuthenticationProvider,最终都要通过AuthenticationManager,也就是身份认证管理器中进行验证逐一验证。
4、DaoAuthenticationProvider
Dao,顾名思义,就是数据库访问层
获取用户提交的用户名密码,比对其与数据库中已经存储的用户名密码,如果正确,返回数据库中的用户信息
5、UserDetails和UserDetailsService
UserDetails代表了用户的详细信息,代码如下
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
UserDetails与Authentication有很多相似之处,但如何区分两者是很重要的!
Authentication中的getCredentials与UserDetails中的getPassword的区别在于,前者是用户提交的,后者是数据库中取出来的,认证器实际上就是将这两者进行比对。
Authentication中的getAuthorities实际上是由UserDetails的getAuthorities传递形成的,Authentication中的getUserDetails则是由AuthenticationManager认证后被填充的。
UserDetailsService负责从指定位置(通常是数据库)中取得用户信息,仅此而已,UserDetailsService常见的实现类有JdbcDaoImpl,InMemoryUserDetailsManager,前者从数据库加载用户,后者从内存中加载用户,也可以自己实现UserDetailsService,通常这更加灵活。

四、核心配置解读
1、功能介绍
这是SpringSecurity入门指南中的配置项目
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("admin").roles("USER");
}
}
当配置了上述的javaConfig之后,我们的项目具有了以下功能:
- 除了“/”,”/home”(首页),”/login”(登录),”/logout”(注销),之外,其他路径都需要认证。
- 指定“/login”该路径为登录页面,当未认证的用户尝试访问任何受保护的资源时,都会跳转到“/login”。
- 默认指定“/logout”为注销页面。
- 配置一个内存中的用户认证器,使用admin/admin作为用户名和密码,具有USER角色。
- 防止CSRF攻击。
- Session Fixation protection(防止别人篡改sessionId)。
- Security Header(添加一系列和Header相关的控制)
(1)HTTP Strict Transport Security for secure requests
(2)集成X-Content-Type-Options
缓存控制
(3)集成X-XSS-Protection.aspx)
(4)X-Frame-Options integration to help prevent Clickjacking(iframe被默认禁止使用) - 为Servlet API集成了如下的几个方法
(1)HttpServletRequest#getRemoteUser())
(2)HttpServletRequest.html#getUserPrincipal())
(3)HttpServletRequest.html#isUserInRole(java.lang.String))
(4)HttpServletRequest.html#login(java.lang.String, java.lang.String))
(5)HttpServletRequest.html#logout())
2、@EnableWebSecurity
组合注解:
@Import({ WebSecurityConfiguration.class, // <2>
SpringWebMvcImportSelector.class }) // <1>
@EnableGlobalAuthentication // <3>
@Configuration
public @interface EnableWebSecurity {
boolean debug() default false;
}
在自定义类中,我们同时使用了@EnableWebSecurity和WebSecurityConfigurerAdapter,那么显然,@EnableWebSecurity注解的作用更大。
@import是SpringBoot提供的用于引入外部配置的注解,可以理解为,是@EnableWebSecurity激活了@import注解中包含的配置类。
<1> SpringWebMvcImportSelector的作用是判断当前的环境是否包含springmvc,因为spring security可以在非spring环境下使用,为了避免DispatcherServlet的重复配置,所以使用了这个注解来区分。
<2> WebSecurityConfiguration顾名思义,是用来配置web安全的,下面会详细介绍。
<3> @EnableGlobalAuthentication注解的源码如下:
@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}
重点仍是@import中的配置类,AuthenticationConfiguration用来配置认证相关的核心类。
综上所述,@EnableWebSecurity的核心功能就是加载了WebSecurityConfiguration和AuthenticationConfiguration两个配置类,也就是将SpringSecurity的职责分成了配置安全信息、配置认证信息两大部分。
WebSecurityConfiguration
在这个配置类中,有一个非常重要的Bean被注册了,
@Configuration
public class WebSecurityConfiguration {
//DEFAULT_FILTER_NAME = "springSecurityFilterChain"
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
...
}
}
springSecurityFilterChain是SpringSecurity的核心过滤器,是整个认证的入口。WebSecurityConfiguration中完成了声明springSecurityFilterChain的作用,并且最终交给DelegatingFilterProxy这个代理类,负责拦截请求(注意DelegatingFilterProxy这个类不是spring security包中的,而是存在于web包中,spring使用了代理模式来实现安全过滤的解耦)。
AuthenticationConfiguration
AuthenticationConfiguration的主要作用,便是为了生成全局的身份认证管理器 AuthenticationManage。
3、WebSecurityConfigurerAdapter
Adapter:适配器模式–使得原本不能一起使用的两个类,通过适配器模式,在不覆盖无关源代码的前提下,可以一起使用。就像讲中文的人和讲英文的人之间进行对话,需要一个翻译一样。
WebSecurityConfigurerAdapter中,我们只关心需要重写的三个方法即可,如下
/
由方法中的参数可得,我们接下来需要进行的是对AuthenticationManagerBuilder、WebSecurity、HttpSecurity的个性化配置。
HttpSecurity常用配置
@Configuration
@EnableWebSecurity
public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated()
.and()
.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.failureForwardUrl("/login?error")
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/index")
.permitAll()
.and()
.httpBasic()
.disable();
}
}
上述代码是一个典型的HttpSecurity配置,在 四、核心配置解读->1、功能介绍中,已经详细解读,这里不再赘述。
.authorizeRequests()、 .formLogin()、 .logout()、.httpBasic()都代表的Http的相关安全配置,无一例外全部返回configure。而全部的Http配置,见下图:
我们只掌握SpringSecutiry中的自动配置就可以了,详细本人就不多做了解了。
WebSecurityBuilder
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}
}
以大佬的经验,这个配置中并不会出现太多的配置信息。不多做了解。
AuthenticationManagerBuilder
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("admin").roles("USER");
}
}
想要在WebSecurityConfig 中进行认证,我们可以用configure(AuthenticationManagerBuilder auth)来暴露一个AuthenticationManager建造器:AuthenticationManagerBuilder
我们在上述文章中,配置方法并不是这样的,而是:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("admin").roles("USER");
}
}
如果你的应用只有一个WebSecurityConfigurerAdapter ,那么他们之间的差别可以忽略不计,如果有多个,则使用@Autowired注解注入的AuthenticationManagerBuilder则是全局身份认证器,可以跨多个WebSecurityConfigurerAdapter,以及影响到基于Method的安全控制;而 protected configure()的方式则类似于一个匿名内部类,它的作用域局限于一个WebSecurityConfigurerAdapter内部。
笔记(一),完