Spring Security详解(二)认证之核心配置详解
2.核心配置解读
2.1 导读
这是Spring Security入门指南中表单登陆的基本配置:
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/error")
.permitAll()
.and()
.logout()
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
首先解释一下类上面的配置:
- @EnableWebSecurity:禁用Boot的默认Security配置,配合@Configuration启用自定义配置(需要扩展WebSecurityConfigurerAdapter)
- @EnableGlobalMethodSecurity(prePostEnabled = true): 启用Security注解,例如最常用的@PreAuthorize。
接下来我们解释一下这样配置的作用:
- 除了“/”,”/home”(首页),”/login”(登录),”/logout”(注销),之外,其他路径都需要认证。
- 指定“/login”该路径为登录页面,当未认证的用户尝试访问任何受保护的资源时,都会跳转到“/login”。
- 指定“/error”为错误页面,当有异常抛出时,跳转到“/error”。
- 默认指定“/logout”为注销页面。
- 配置一个内存中的用户认证器,使用admin/admin作为用户名和密码,具有USER角色。
2.2 EnableWebSecurity
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
boolean debug() default false;
}
- @Import是用于Spring Boot提供的引入外部配置的注解
- SpringWebMvcImportSelector的作用是判断当前的环境是否包含springmvc,因为spring security可以在非spring环境下使用,为了避免DispatcherServlet的重复配置,所以使用了这个注解来区分。
- @EnableGlobalAuthentication注解的源码如下:
@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}
可以看出,这个注解引入了AuthenticationConfiguration配置。而这个类用来配置认证相关,主要任务就是生成全局的身份认证管理者。AuthenticationManager:
@Configuration
@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {
private AuthenticationManager authenticationManager;
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(
ObjectPostProcessor<Object> objectPostProcessor) {
return new AuthenticationManagerBuilder(objectPostProcessor);
}
public AuthenticationManager getAuthenticationManager() throws Exception {
...
}
}
- WebSecurityConfiguration用来配置web安全。在这个类中有一个非常重要的Bean被注册了。
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
/**
* Creates the Spring Security Filter Chain
* @return
* @throws Exception
*/
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
return webSecurity.build();
}
}
在未使用springboot之前,在XML配置中,想要启用spring security,需要在web.xml中进行如下配置:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
而众所周知,SpringSecurityFilterChain是Spring Security认证的入口。集成Spring Boot集成之后,xml配置被java注解配置取代,也就是在WebSecurityConfiguration中完成了声明springSecurityFilterChain的作用。并且最终交给DelegatingFilterProxy这个代理类,负责拦截请求。
也就是说:@EnableWebSecurity完成的工作便是加载了WebSecurityConfiguration,AuthenticationConfiguration这两个核心配置类,也就此将spring security的职责划分为了配置安全信息,配置认证信息两部分。
2.3 WebSecurityConfigurerAdapter
WebSecurityConfigurerAdapter提供了三个configure方法,是我们需要关心的:
可以看出,分别是对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();
}
}
上述是一个使用Java Configuration配置HttpSecurity的典型配置,其中http作为根开始配置,每一个and()对应了一个模块的配置(等同于xml配置中的结束标签),并且and()返回了HttpSecurity本身,于是可以连续进行配置。他们配置的含义也非常容易通过变量本身来推测。
- authorizeRequests()配置路径拦截,表明路径访问所对应的权限,角色,认证信息。
- formLogin()对应表单认证相关的配置
- logout()对应了注销相关的配置
- httpBasic()可以配置basic登录
- etc
他们分别代表了http请求相关的安全配置,这些配置项无一例外的返回了Configurer类,而所有的http相关配置可以通过查看HttpSecurity的主要方法得知:
WebSecurity
public final class WebSecurity {
//全局请求忽略规则配置(比如说静态文件,比如说注册页面)
public IgnoredRequestConfigurer ignoring() {
return ignoredRequestRegistry;
}
//全局http防火墙配置
public WebSecurity httpFirewall(HttpFirewall httpFirewall) {
this.httpFirewall = httpFirewall;
return this;
}
//是否debug配置
public WebSecurity debug(boolean debugEnabled) {
this.debugEnabled = debugEnabled;
return this;
}
//全局SecurityFilterChain配置
public WebSecurity addSecurityFilterChainBuilder(
SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
this.securityFilterChainBuilders.add(securityFilterChainBuilder);
return this;
}
//web权限评估器,默认DefaultWebInvocationPrivilegeEvaluator
public WebSecurity privilegeEvaluator(
WebInvocationPrivilegeEvaluator privilegeEvaluator) {
this.privilegeEvaluator = privilegeEvaluator;
return this;
}
//设置表达式处理器,默认DefaultWebSecurityExpressionHandler
public WebSecurity expressionHandler(
SecurityExpressionHandler<FilterInvocation> expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.expressionHandler = expressionHandler;
return this;
}
}
一般只有ignoring()配置需要重写:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/static/**");
}
}
AuthenticationManagerBuilder
主要的方法:
public class AuthenticationManagerBuilder {
//配置父AuthenticationManager
public AuthenticationManagerBuilder parentAuthenticationManager(
AuthenticationManager authenticationManager) {
...
}
//配置事件发布器
public AuthenticationManagerBuilder authenticationEventPublisher(
AuthenticationEventPublisher eventPublisher) {
...
}
//认证结束后是否删除密码凭证
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
...
}
//配置内存用户
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
throws Exception {
return apply(new InMemoryUserDetailsManagerConfigurer<>());
}
//配置数据库用户
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication()
throws Exception {
return apply(new JdbcUserDetailsManagerConfigurer<>());
}
//配置userDetailService
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
T userDetailsService) throws Exception {
...
}
//配置authenticationProvider
public AuthenticationManagerBuilder authenticationProvider(
AuthenticationProvider authenticationProvider) {
this.authenticationProviders.add(authenticationProvider);
return this;
}
}
具体根据实际情况进行配置,注意:UserDetailService会默认加载DaoAuthenticationProvider。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(jwtUserDetailsServiceImpl()).passwordEncoder(passwordEncoder());
}
这里还要做一个说明,AuthenticationManagerBuilder有两种配置方式,一种是如上,还有一种是文章开头的配置,如果你的应用只有唯一一个WebSecurityConfigurerAdapter,那么他们之间的差距可以被忽略,从方法名可以看出两者的区别:使用@Autowired注入的AuthenticationManagerBuilder是全局的身份认证器,作用域可以跨越多个WebSecurityConfigurerAdapter,以及影响到基于Method的安全控制;而 protected configure()的方式则类似于一个匿名内部类,它的作用域局限于一个WebSecurityConfigurerAdapter内部。
2.4 配置多个httpSecurity
我们可以配置多个HttpSecurity实例,就像我们可以有多个块. 关键在于对WebSecurityConfigurationAdapter进行多次扩展。例如下面是一个对/api/开头的URL进行的不同的设置。
@EnableWebSecurity
public class MultiHttpSecurityConfig {
@Bean //<1>
public UserDetailsService userDetailsService() throws Exception {
// ensure the passwords are encoded properly
UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users.username("user").password("password").roles("USER").build());
manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build());
return manager;
}
@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注解,用来指定个哪一个WebSecurityConfigurerAdapter更优先。
<3> http.antMatcher指出,这个HttpSecurity只应用到以/api/开头的URL上。
<4> 创建另外一个WebSecurityConfigurerAdapter实例。用于不以/api/开头的URL,这个配置的顺序在ApiWebSecurityConfigurationAdapter之后,因为他没有指定@Order值为1(没有指定@Order默认会被放到最后).
上一篇: 生蚝怎么做,你知道吗
下一篇: 4.2
推荐阅读
-
详解Spring Security如何配置JSON登录
-
详解最简单易懂的Spring Security 身份认证流程讲解
-
SpringBoot + Spring Security 基本使用及个性化登录配置详解
-
SpringBoot + Spring Security 基本使用及个性化登录配置详解
-
Spring之WEB模块配置详解
-
详解Spring Boot实战之Filter实现使用JWT进行接口认证
-
Spring实战之XML与JavaConfig的混合配置详解
-
spring基础系列之JavaConfig配置详解
-
Spring Security认证提供程序示例详解
-
JSP 开发之Spring Security详解