springSecurity登录验证

1、配置是在WebSecurityConfigurerAdapter中的configure(HttpSecurity http)方法中提供一个默认的配置

1
2
3
4
5
6
7
8
9
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();

}

上面的默认配置:

  • 确保我们应用所有请求都需要用户被认证
  • 允许用户进行基于表单的认证
  • 允许用户使用http基本验证进行认证

与xml命名配置对比

1
2
3
4
5
<http>
<intercept-url pattern = "/**" access = "authenticated" />
<form-login />
<http-basic />
</http>

在java中的and()方法相当于xml的标签关闭

2、java配置和表单登录

配置类中需要更新前面配置

1
2
3
4
5
6
7
8
9
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login") //*1
.permitALl(); //*2

}

  1. 指定登录页的路径
  2. 允许表单登录的所有用户访问登录界面

页面
我们需要创建一个表单,action跳转登录接口。记住loginPage里面配置是我们跳转登录界面,需要的只是get请求。而页面配置的action跳转的接口,是post请求,只要跟登录接口保持一致即可
登录参数和密码如果你没有配置的话。 需要命名为username和password


3、验证请求

当我们需要进行身份验证并且在应用程序的每个url这样做,我们可以通过给http.authorizeRequests()添加多个及诶单来指定多个定制需求到url中

1
2
3
4
5
6
7
8
9
10
11
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() //1
.antMatchers("/resources/**", "/signup", "/about").permitAll() //2
.antMatchers("/admin/**").hasRole("ADMIN") //3
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") 4
.anyRequest().authenticated() //5
.and()
// ...
.formLogin();
}

  • http.authorizeRequests()天剑antMatchers多个节点
  • 指定多个url,可以让任何用户都可以访问的url,以resources开头的,还有”/signup”, “/about”
  • admin开头的url只能拥有roleAdmin角色的用户进行访问
  • 任何以db开头的需要同时具备admin 和dba角色才能访问
  • 尚未匹配的任何 URL 要求用户进行身份验证

4、处理登出

当使用WebSecurityConfigurerAdapter时 注销功能会被自动用上,默认是通过访问/logout,这个操作指定一下动作:

  • 使session失效
  • 清除所有已经配置的rememberMe认证
  • 清除SecurityContextHolder
  • 跳转到login?success
    当然你也可以定制注销功能
1
2
3
4
5
6
7
8
9
10
11
12
protected void configure(HttpSecurity http) throws Exception {
http
.logout() // 1
.logoutUrl("/my/logout")// 2
.logoutSuccessUrl("/my/index") // 3
.logoutSuccessHandler(logoutSuccessHandler)//4
.invalidateHttpSession(true) // 5
.addLogoutHandler(logoutHandler) // 6
.deleteCookies(cookieNamesToClear) //7
.and()
...
}
  1. 提供注销支持,使WebSecurityConfigurerAdapter是会自动被应用
  2. 设置触发注销操作的url,如果csrf保护被启动(默认启动)的话这个请求的方式被限定为post请求
  3. 注销之后跳转的url,默认是/login?logout
  4. 定制logoutSuccessHandler,如果指定了,那么logoutSuccessUrl()的设置会被忽略
  5. 指定是否在注销时让httpSession无效,默认true
  6. 添加一个logoutHandler,默认是SecurityContextLogoutHandler,会被添加为最后一个 logouthandler
  7. 允许指定注销成功时将移除cookie

LogoutHandler

流式API提供了一个调用相应的LogoutHandler实现的快捷方式,而不用直接提供LogoutHandler的实现
例如:deleteCookies()允许指定注销成功时要删除的一个或者多个cookie。不应该抛出异常

LogoutSuccessHandler

LogoutSuccessHandler 被LogoutFiler在成功注销后调用,用来进行重定向或者转发相应的目的地。可以抛出异常

5、验证

内存中的身份验证

配置多个用户的例子

1
2
3
4
5
6
7
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}

JDBC验证

下面的例子假设你已经在应用程序中定义好了DateSource , jdbc-jc示例提供了一个完整的基于JDBC的验证

1
2
3
4
5
6
7
8
9
10
11
12
@Autowired
private DataSource dataSource;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}

6、多个HttpSecurity

可以配置多个HttpSecurity实例,多WebSecurityConfigurationAdapter进行多次扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@EnableWebSecurity
public class MultiHttpSecurityConfig {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) { //1
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}

@Configuration
@Order(1) // 2
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**") // 3
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}

@Configuration // 4
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
}
  1. 配置正常的验证
  2. 创建一个WebSecurityConfigurerAdapter,包含一个@Order注解,用来指定哪个优先
  3. http.antMatcher指出,这个HttpSecurity只应用的到以/api开头的url上
  4. 创建另外一个WebSecurityConfigurerAdapter实例,用于不以/api开头的url,这个顺讯会在ApiWebSecurityConfigurationAdapter之后,因为没有指定@Order值

7、方法安全

EnableGlobalMethodSecurity

我们可以在任何使用@Configuration的实例上是哟好难过@EnableGlobalMethodSecurity注解来启用基于注解的安全性

1
2
3
4
@EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
// ...
}

添加一个注解到一个方法会限制对相应方法的访问
Spring Security的原生注解支持定义一套用户该方法的属性,这些将被传递到AccessDecisionManager来做实际决定

1
2
3
4
5
6
7
8
9
10
11
public interface BankService {

@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);

@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account[] findAccounts();

@Secured("ROLE_TELLER")
public Account post(Account account, double amount);
}

使用如下代码启用JSR-250注解的支持

1
2
3
4
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class MethodSecurityConfig {
// ...
}

这些都是基本标准,并允许应用简单的基于角色的约束,但是没有spring Security的本地注解的能力。要使用新的基本表达式的语法,可以使用

1
2
3
4
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {
// ...
}

和响应java代码

1
2
3
4
5
6
7
8
9
10
11
public interface BankService {

@PreAuthorize("isAnonymous()")
public Account readAccount(Long id);

@PreAuthorize("isAnonymous()")
public Account[] findAccounts();

@PreAuthorize("hasAuthority('ROLE_TELLER')")
public Account post(Account account, double amount);
}

GlobalMethodSecurityConfiguration

有时候需要制定一些比@EnableGlobalMethodSecurity 注解允许的更复杂的操作,我们可以扩展GlobalMethodSecurityConfiguration确保@EnableGlobalMethodSecurity注解出现在你的 子类中

1
2
3
4
5
6
7
8
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
// ... create and return custom MethodSecurityExpressionHandler ...
return expressionHander;
}
}

已配置对象的后续处理

Spring Security的Java配置没有公开每个配置对象的每一个属性,这简化了广大用户的配置。毕竟如果要配置每一个属性,用户可以使用标准的Bean配置。

虽然有一些很好的理由不直接暴露所有属性,用户可能任然需要更多高级配置,为了解决这个Spring Security引入了 ObjectPostProcessor 概念,用来修改或替换Java配置的对象实例。例如:如果你想在FilterSecurityInterceptor里配置filterSecurityPublishAuthorizationSuccess属性,你可以像下面一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
fsi.setPublishAuthorizationSuccess(true);
return fsi;
}
});
}