Spring security 以及 shiro 等权限框架,一般都有两部分功能:认证和鉴权,也就是用户登陆和权限控制。本文记录spring security 在 用户身份认证时的配置使用方法,以及自定义认证组件的方法。
Spring security 的配置首先继承
,下面时一个简单的配置示例:WebSecurityConfigurerAdapter
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login")
.defaultSuccessUrl("/index.html").permitAll()
.and()
.logout().permitAll();
super.configure(http);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("yawn1").password("111").roles("USER")
.and()
.withUser("yawn2").password("123456").roles("USER");
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/static/**");
super.configure(web);
}
}
如果需要自定义组件和认证,可如下配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private String[] authenticated = {"/**"};
private String[] permits = {"/css/**", "/js/**", "/images/**"};
private boolean iframeEnabled = true;
@Override
protected void configure(HttpSecurity http) throws Exception {
// 权限的配置
http.authorizeRequests()
.antMatchers(permits).permitAll()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/web/**").hasAuthority("user")
.antMatchers("/admin/**").hasRole("admin")
.antMatchers(authenticated).authenticated();
// 禁用csrf
http.csrf().disable();
// iframe的配置
if (iframeEnabled) {
http.headers()
.frameOptions()
.sameOrigin()
.cacheControl();
}
// 登陆注销的配置
http.formLogin()
.loginPage("/login")
.permitAll()
.failureHandler(authenticationFailureHandler())
.successHandler(authenticationSuccessHandler())
.defaultSuccessUrl("app.html")
.and()
.logout()
.logoutUrl("logout")
.logoutSuccessUrl("login");
// 添加自定义过滤器
http.addFilterBefore(new KaptchaFilter(), CsrfFilter.class);
// 重写认证逻辑
http.authenticationProvider(authenticationProvider());
// 认证异常的处理逻辑
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
AuthenticationFailureHandler authenticationFailureHandler() {
return new LoginFailHandler();
}
@Bean
AuthenticationSuccessHandler authenticationSuccessHandler() {
return new LoginSuccessHandler();
}
@Bean
AuthenticationProvider authenticationProvider() {
return new SqlAuthenticationProvider();
}
@Bean
AuthenticationEntryPoint authenticationEntryPoint() {
return new AjaxAuthenticationEntryPoint();
}
}
其中实现AuthenticationProvider
可以自定义认证的逻辑。 AuthenticationProvider
中有两个方法需要实现,如下:
public class SqlAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// ...
renturn authentication;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication == UsernamePasswordAuthenticationToken.class;
}
}
在ProviderManager的authenticate方法中,轮询成员变量List<AuthenticationProvider> providers。该providers中如果有一个 AuthenticationProvider的supports函数返回true,那么就会调用该AuthenticationProvider的authenticate函数认证,如果认证成功则整个认证过程结束。如果不成功,则继续使用下一个合适的AuthenticationProvider进行认证,只要有一个认证成功则为认证成功。
完整的认证逻辑为:
- 在UsernamePasswordAuthenticationFilter的attemptAuthentication()方法中,调用AuthenticationManager进行认证
- AuthenticationManager接收Authentication对象作为参数,并通过authenticate方法对其进行验证(实际由其实现类ProviderManager完成)
- 在ProviderManager的authenticate方法中,轮训成员变量List<AuthenticationProvider> providers。该providers中如果有一个AuthenticationProvider的supports函数返回true,那么就会调用该AuthenticationProvider的authenticate函数认证,如果认证成功则整个认证过程结束。如果不成功,则继续使用下一个合适的AuthenticationProvider进行认证,只要有一个认证成功则为认证成功。
- UsernamePasswordAuthenticationToken实现了Authentication,主要是将用户输入的用户名密码进行封装,并提供给AuthenticationManager进行验证,验证成功后,返回一个认证成功的UsernamePasswordAuthenticationToken对象